Skip to content

Commit a2c0a13

Browse files
authored
Merge pull request #4 from sunsided/feature/codecov
Add code coverage in GitHub Actions
2 parents f86621e + e06e3f7 commit a2c0a13

File tree

3 files changed

+98
-10
lines changed

3 files changed

+98
-10
lines changed

.github/workflows/rust.yml

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,69 @@
1+
---
12
name: Rust
23

34
on:
45
push:
56
branches: [ "main" ]
7+
paths:
8+
- 'Cargo.toml'
9+
- 'Cargo.lock'
10+
- 'src/**'
11+
- 'crates/**'
12+
- 'bins/**'
13+
- '.github/workflows/rust.yml'
614
pull_request:
715
branches: [ "main" ]
16+
paths:
17+
- 'Cargo.toml'
18+
- 'Cargo.lock'
19+
- 'src/**'
20+
- 'crates/**'
21+
- 'bins/**'
22+
- '.github/workflows/rust.yml'
823

924
env:
1025
CARGO_TERM_COLOR: always
1126

1227
jobs:
1328
build:
29+
name: Build and Test
30+
runs-on: ${{ matrix.os }}
31+
strategy:
32+
matrix:
33+
os: [ ubuntu-latest, macos-latest, windows-latest ]
34+
steps:
35+
- uses: actions/checkout@v4
36+
- name: Check format
37+
run: cargo fmt --check
38+
- name: Build
39+
run: cargo build --verbose
40+
- uses: taiki-e/install-action@nextest
41+
- name: Run tests
42+
run: cargo nextest run --verbose --all-features
43+
- name: Run doctests
44+
run: cargo test --doc --verbose --all-features
1445

46+
codecov:
47+
name: Code Coverage
1548
runs-on: ubuntu-latest
16-
49+
env:
50+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
1751
steps:
18-
- uses: actions/checkout@v3
19-
- name: Build
20-
run: cargo build --verbose
21-
- name: Run tests
22-
run: cargo test --tests --verbose
23-
- name: Run doctests
24-
run: cargo test --doc --verbose
52+
- uses: actions/checkout@v4
53+
- name: Build
54+
run: cargo build --verbose
55+
- uses: dtolnay/rust-toolchain@stable
56+
with:
57+
components: llvm-tools-preview
58+
- name: Install cargo-llvm-cov
59+
uses: taiki-e/install-action@cargo-llvm-cov
60+
- name: Install nextest
61+
uses: taiki-e/install-action@nextest
62+
- name: Generate code coverage
63+
run: cargo llvm-cov nextest --all-features --workspace --lcov --output-path lcov.info
64+
- name: Upload coverage to Codecov
65+
uses: codecov/codecov-action@v4.0.1
66+
with:
67+
token: ${{ secrets.CODECOV_TOKEN }}
68+
files: lcov.info
69+
fail_ci_if_error: true

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# A query string builder for percent encoding key-value pairs
22

3+
[![Crates.io](https://img.shields.io/crates/v/query-string-builder)](https://crates.io/crates/query-string-builder)
4+
[![Crates.io](https://img.shields.io/crates/l/query-string-builder)](https://crates.io/crates/query-string-builder)
5+
[![codecov](https://codecov.io/gh/sunsided/query-string-builder/graph/badge.svg?token=HUCXM04DOG)](https://codecov.io/gh/sunsided/query-string-builder)
6+
37
This is a tiny helper crate for simplifying the construction of URL query strings.
48
The initial `?` question mark is automatically prepended.
59

src/lib.rs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121
2222
#![deny(unsafe_code)]
2323

24-
use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
2524
use std::fmt::{Debug, Display, Formatter};
2625

26+
use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
27+
2728
/// https://url.spec.whatwg.org/#fragment-percent-encode-set
2829
const FRAGMENT: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'<').add(b'>').add(b'`');
2930

@@ -218,7 +219,7 @@ impl QueryString {
218219
impl Display for QueryString {
219220
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
220221
if self.pairs.is_empty() {
221-
return Ok(());
222+
Ok(())
222223
} else {
223224
write!(f, "?")?;
224225
for (i, pair) in self.pairs.iter().enumerate() {
@@ -247,6 +248,14 @@ struct Kvp {
247248
mod tests {
248249
use super::*;
249250

251+
#[test]
252+
fn test_empty() {
253+
let qs = QueryString::new();
254+
assert_eq!(qs.to_string(), "");
255+
assert_eq!(qs.len(), 0);
256+
assert!(qs.is_empty());
257+
}
258+
250259
#[test]
251260
fn test_simple() {
252261
let qs = QueryString::new()
@@ -256,6 +265,8 @@ mod tests {
256265
qs.to_string(),
257266
"?q=apple???&category=fruits%20and%20vegetables"
258267
);
268+
assert_eq!(qs.len(), 2);
269+
assert!(!qs.is_empty());
259270
}
260271

261272
#[test]
@@ -287,5 +298,33 @@ mod tests {
287298
qs.to_string(),
288299
"?q=celery&category=fruits%20and%20vegetables"
289300
);
301+
assert_eq!(qs.len(), 2); // not three!
302+
}
303+
304+
#[test]
305+
fn test_push_optional() {
306+
let mut qs = QueryString::new();
307+
qs.push("a", "apple");
308+
qs.push_opt("b", None::<String>);
309+
qs.push_opt("c", Some("🍎 apple"));
310+
311+
assert_eq!(
312+
format!("https://example.com/{qs}"),
313+
"https://example.com/?a=apple&c=%F0%9F%8D%8E%20apple"
314+
);
315+
}
316+
317+
#[test]
318+
fn test_append() {
319+
let qs = QueryString::new().with_value("q", "apple");
320+
let more = QueryString::new().with_value("q", "pear");
321+
322+
let mut qs = qs.append_into(more);
323+
qs.append(QueryString::new().with_value("answer", "42"));
324+
325+
assert_eq!(
326+
format!("https://example.com/{qs}"),
327+
"https://example.com/?q=apple&q=pear&answer=42"
328+
);
290329
}
291330
}

0 commit comments

Comments
 (0)