Skip to content

Commit 8927e39

Browse files
Add very basic support for positional Vecs
1 parent c0fac0a commit 8927e39

12 files changed

+128
-70
lines changed

.github/FUNDING.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github: [fasterthanlime]
2+
patreon: fasterthanlime

Cargo.lock

Lines changed: 12 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ keywords = ["cli", "args", "parser", "facet"]
1111
categories = ["command-line-interface"]
1212

1313
[dependencies]
14-
facet-reflect = { version = "0.28.0" }
15-
facet-core = { version = "0.28.0" }
14+
facet-reflect = { version = "0.28.3" }
15+
facet-core = { version = "0.28.3" }
1616
log = "0.4.27"
1717
owo-colors = "4.2.2"
1818
ariadne = { version = "0.5.1", optional = true }
@@ -22,7 +22,7 @@ cargo-husky = { version = "1.5.0", default-features = false, features = [
2222
"user-hooks",
2323
] }
2424
eyre = "0.6.12"
25-
facet = { version = "0.28.0" }
25+
facet = { version = "0.28.3" }
2626
facet-pretty = { version = "0.28.0" }
2727
facet-testhelpers = { version = "0.28" }
2828
insta = "1.43.1"

README.md

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,11 @@
1-
<h1>
2-
<picture>
3-
<source type="image/webp" media="(prefers-color-scheme: dark)" srcset="https://github.com/facet-rs/facet/raw/main/static/logo-v2/facet-b-dark.webp">
4-
<source type="image/png" media="(prefers-color-scheme: dark)" srcset="https://github.com/facet-rs/facet/raw/main/static/logo-v2/facet-b-dark.png">
5-
<source type="image/webp" srcset="https://github.com/facet-rs/facet/raw/main/static/logo-v2/facet-b-light.webp">
6-
<img src="https://github.com/facet-rs/facet/raw/main/static/logo-v2/facet-b-light.png" height="35" alt="Facet logo - a reflection library for Rust">
7-
</picture>
8-
</h1>
1+
# facet-args
92

10-
[![Coverage Status](https://coveralls.io/repos/github/facet-rs/facet/badge.svg?branch=main)](https://coveralls.io/github/facet-rs/facet?branch=main)
3+
[![Coverage Status](https://coveralls.io/repos/github/facet-rs/facet-args/badge.svg?branch=main)](https://coveralls.io/github/facet-rs/facet?branch=main)
114
[![crates.io](https://img.shields.io/crates/v/facet-args.svg)](https://crates.io/crates/facet-args)
125
[![documentation](https://docs.rs/facet-args/badge.svg)](https://docs.rs/facet-args)
136
[![MIT/Apache-2.0 licensed](https://img.shields.io/crates/l/facet-args.svg)](./LICENSE)
147
[![Discord](https://img.shields.io/discord/1379550208551026748?logo=discord&label=discord)](https://discord.gg/JhD7CwCJ8F)
158

16-
_Logo by [Misiasart](https://misiasart.com/)_
17-
18-
Thanks to all individual and corporate sponsors, without whom this work could not exist:
19-
20-
<p> <a href="https://ko-fi.com/fasterthanlime">
21-
<picture>
22-
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/kofi-dark.svg">
23-
<img src="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/kofi-light.svg" height="40" alt="Ko-fi">
24-
</picture>
25-
</a> <a href="https://github.com/sponsors/fasterthanlime">
26-
<picture>
27-
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/github-dark.svg">
28-
<img src="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/github-light.svg" height="40" alt="GitHub Sponsors">
29-
</picture>
30-
</a> <a href="https://patreon.com/fasterthanlime">
31-
<picture>
32-
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/patreon-dark.svg">
33-
<img src="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/patreon-light.svg" height="40" alt="Patreon">
34-
</picture>
35-
</a> <a href="https://zed.dev">
36-
<picture>
37-
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/zed-dark.svg">
38-
<img src="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/zed-light.svg" height="40" alt="Zed">
39-
</picture>
40-
</a> <a href="https://depot.dev?utm_source=facet">
41-
<picture>
42-
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/depot-dark.svg">
43-
<img src="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/depot-light.svg" height="40" alt="Depot">
44-
</picture>
45-
</a> </p>
46-
479
Provides CLI argument parsing (WIP).
4810

4911
```rust
@@ -69,6 +31,47 @@ Ok(())
6931
# }
7032
```
7133

34+
## Sponsors
35+
36+
Thanks to all individual sponsors:
37+
38+
<p> <a href="https://github.com/sponsors/fasterthanlime">
39+
<picture>
40+
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/github-dark.svg">
41+
<img src="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/github-light.svg" height="40" alt="GitHub Sponsors">
42+
</picture>
43+
</a> <a href="https://patreon.com/fasterthanlime">
44+
<picture>
45+
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/patreon-dark.svg">
46+
<img src="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/patreon-light.svg" height="40" alt="Patreon">
47+
</picture>
48+
</a> </p>
49+
50+
...along with corporate sponsors:
51+
52+
<p> <a href="https://aws.amazon.com">
53+
<picture>
54+
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/aws-dark.svg">
55+
<img src="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/aws-light.svg" height="40" alt="AWS">
56+
</picture>
57+
</a> <a href="https://zed.dev">
58+
<picture>
59+
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/zed-dark.svg">
60+
<img src="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/zed-light.svg" height="40" alt="Zed">
61+
</picture>
62+
</a> <a href="https://depot.dev?utm_source=facet">
63+
<picture>
64+
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/depot-dark.svg">
65+
<img src="https://github.com/facet-rs/facet/raw/main/static/sponsors-v3/depot-light.svg" height="40" alt="Depot">
66+
</picture>
67+
</a> </p>
68+
69+
...without whom this work could not exist.
70+
71+
## Special thanks
72+
73+
The facet logo was drawn by [Misiasart](https://misiasart.com/).
74+
7275
## License
7376

7477
Licensed under either of:

src/deserialize.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,12 +1348,15 @@ where
13481348
{
13491349
match scalar {
13501350
Scalar::String(cow) => {
1351+
trace!("parsed string, probing innermost type");
13511352
match wip.innermost_shape().ty {
13521353
Type::User(UserType::Enum(_)) => {
13531354
if wip.selected_variant().is_some() {
1355+
trace!("has enum with variant selected, setting");
13541356
// If we already have a variant selected, just put the string
13551357
wip.set(cow).map_err(|e| self.reflect_err(e))?;
13561358
} else {
1359+
trace!("has enum without selected variant");
13571360
// Try to select the variant
13581361
match wip.find_variant(&cow) {
13591362
Some((variant_index, _)) => {
@@ -1372,6 +1375,8 @@ where
13721375
Type::Pointer(PointerType::Reference(_))
13731376
if wip.innermost_shape().is_type::<&str>() =>
13741377
{
1378+
trace!("handling &str");
1379+
13751380
// This is for handling the &str type
13761381
// The Cow may be Borrowed (we may have an owned string but need a &str)
13771382
match cow {
@@ -1382,7 +1387,10 @@ where
13821387
_ => {
13831388
// Check if this is a scalar type that can be parsed from a string
13841389
let shape = wip.innermost_shape();
1390+
13851391
if matches!(shape.def, Def::Scalar) {
1392+
trace!("is scalar, trying parse");
1393+
13861394
// Try parse_from_str for scalar types that might parse from strings
13871395
// (like IpAddr, UUID, Path, etc.)
13881396
match wip.parse_from_str(cow.as_ref()) {
@@ -1413,7 +1421,18 @@ where
14131421
}
14141422
}
14151423
}
1424+
} else if matches!(shape.def, Def::List(_)) {
1425+
trace!("is list, trying push");
1426+
1427+
wip.begin_list()
1428+
.map_err(|e| self.reflect_err(e))?
1429+
.push(cow.to_string())
1430+
.map_err(|e| self.reflect_err(e))?;
14161431
} else {
1432+
trace!(
1433+
"def was not a scalar, try to set as string (will probably fail)"
1434+
);
1435+
14171436
// Not a scalar, just set as String
14181437
wip.set(cow.to_string()).map_err(|e| self.reflect_err(e))?;
14191438
}

src/fields.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use alloc::borrow::Cow;
22
use alloc::string::ToString;
3-
use facet_core::{FieldAttribute, Shape, Type, UserType};
3+
use facet_core::{Def, FieldAttribute, Shape, Type, UserType};
4+
use log::trace;
45

56
use crate::deserialize::{
67
DeserErrorKind, Outcome, Raw, Scalar, Span, Spanned, Subspan, SubspanMeta,
@@ -28,16 +29,21 @@ pub(crate) fn find_positional_field<'facet>(
2829
shape: &'static Shape,
2930
wip: &Partial<'facet>,
3031
) -> Result<&'static str, DeserErrorKind> {
32+
trace!("attempting to find positional field for shape {shape:?}");
33+
3134
if let Type::User(UserType::Struct(st)) = &shape.ty {
3235
for (idx, field) in st.fields.iter().enumerate() {
3336
for attr in field.attributes.iter() {
3437
let FieldAttribute::Arbitrary(a) = attr;
3538
if a.contains("positional") {
36-
// Check if this field is already set
3739
let is_set = wip.is_field_set(idx).unwrap_or(false);
38-
if !is_set {
39-
return Ok(field.name);
40+
if matches!(field.shape.def, Def::List(_)) {
41+
// we can always append to a list
42+
} else if is_set {
43+
trace!("got field at {idx} but it's set");
44+
continue;
4045
}
46+
return Ok(field.name);
4147
}
4248
}
4349
}

tests/sequence.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,35 @@
11
use facet::Facet;
22
use facet_testhelpers::test;
33

4+
#[test]
5+
fn test_simplest_value_singleton_list_named() {
6+
#[derive(Facet, Debug, PartialEq)]
7+
struct Args {
8+
#[facet(named, short = "s")]
9+
strings: Vec<String>,
10+
}
11+
12+
// Test with multiple values (no delimiters)
13+
let args_single: Args =
14+
facet_args::from_slice(&["-s", "joe", "-s", "le", "-s", "rigolo"]).unwrap();
15+
16+
assert_eq!(args_single.strings, vec!["joe", "le", "rigolo"]);
17+
}
18+
19+
#[test]
20+
fn test_simplest_value_singleton_list_positional() {
21+
#[derive(Facet, Debug, PartialEq)]
22+
struct Args {
23+
#[facet(positional)]
24+
strings: Vec<String>,
25+
}
26+
27+
// Test with multiple values (no delimiters)
28+
let args_single: Args = facet_args::from_slice(&["joe", "le", "rigolo"]).unwrap();
29+
30+
assert_eq!(args_single.strings, vec!["joe", "le", "rigolo"]);
31+
}
32+
433
#[test]
534
#[ignore]
635
fn test_value_singleton_list() {
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
---
2-
source: facet-args/tests/err.rs
2+
source: tests/err.rs
33
expression: err
44
---
55
Error:
66
╭─[ <unknown>:1:10 ]
77
│
88
1 │ --letter abc
99
 │ ─┬─
10-
[38;5;240m │[0m [31m╰[0m[31m─[0m[31m─[0m[31m─[0m Operation failed on shape [34mchar[39m: Failed to parse string value
10+
[38;5;240m │[0m [31m╰[0m[31m─[0m[31m─[0m[31m─[0m Operation failed on shape char: Failed to parse string value
1111
───╯
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
---
2-
source: facet-args/tests/err.rs
2+
source: tests/err.rs
33
expression: err
44
---
55
Error:
66
╭─[ <unknown>:1:10 ]
77
│
88
1 │ --config {server={port=8080,host=localhost},database={url=postgresql://,pool_size=10}}
99
 │ ──────────────────────────────────────┬──────────────────────────────────────
10-
 │ ╰──────────────────────────────────────── Wrong shape: expected AppConfig, but got String
10+
 │ ╰──────────────────────────────────────── Wrong shape: expected AppConfig, but got String
1111
───╯
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
---
2-
source: facet-args/tests/err.rs
2+
source: tests/err.rs
33
expression: err
44
---
55
Error:
66
╭─[ <unknown>:1:11 ]
77
│
88
1 │ --address not-an-ip
99
 │ ────┬────
10-
[38;5;240m │[0m [31m╰[0m[31m─[0m[31m─[0m[31m─[0m[31m─[0m[31m─[0m[31m─[0m Operation failed on shape [34mIpAddr[39m: Failed to parse string value
10+
[38;5;240m │[0m [31m╰[0m[31m─[0m[31m─[0m[31m─[0m[31m─[0m[31m─[0m[31m─[0m Operation failed on shape IpAddr: Failed to parse string value
1111
───╯

0 commit comments

Comments
 (0)