facet_args/
lib.rs

1#![warn(missing_docs)]
2#![warn(clippy::std_instead_of_core)]
3#![warn(clippy::std_instead_of_alloc)]
4#![forbid(unsafe_code)]
5#![doc = include_str!("../README.md")]
6
7use facet_core::{Def, Facet, FieldAttribute};
8use facet_reflect::{ReflectError, Wip};
9
10fn parse_field<'mem>(wip: Wip<'mem>, value: &str) -> Result<Wip<'mem>, ReflectError> {
11    let shape = wip.shape();
12    match shape.def {
13        Def::Scalar(_) => {
14            if shape.is_type::<String>() {
15                wip.put(value.to_string())
16            } else if shape.is_type::<bool>() {
17                log::trace!("Boolean field detected, setting to true");
18                wip.put(value.to_lowercase() == "true")
19            } else {
20                wip.parse(value)
21            }
22        }
23        _def => {
24            return Err(ReflectError::OperationFailed {
25                shape,
26                operation: "parsing field",
27            });
28        }
29    }?
30    .pop()
31}
32
33/// Parses command-line arguments
34pub fn from_slice<T: Facet>(s: &[&str]) -> T {
35    log::trace!("Entering from_slice function");
36    let mut s = s;
37    let mut wip = Wip::alloc::<T>();
38    log::trace!("Allocated Poke for type T");
39
40    while let Some(token) = s.first() {
41        log::trace!("Processing token: {}", token);
42        s = &s[1..];
43
44        if let Some(key) = token.strip_prefix("--") {
45            log::trace!("Found named argument: {}", key);
46            let field_index = match wip.field_index(key) {
47                Some(index) => index,
48                None => panic!("Unknown argument: {}", key),
49            };
50            let field = wip.field(field_index).unwrap();
51
52            if field.shape().is_type::<bool>() {
53                wip = parse_field(field, "true").unwrap();
54            } else {
55                let value = s.first().expect("expected value after argument");
56                log::trace!("Field value: {}", value);
57                s = &s[1..];
58                wip = parse_field(field, value).unwrap();
59            }
60        } else {
61            log::trace!("Encountered positional argument: {}", token);
62            let Def::Struct(sd) = wip.shape().def else {
63                panic!("Expected struct definition");
64            };
65
66            for (field_index, f) in sd.fields.iter().enumerate() {
67                if f.attributes
68                    .iter()
69                    .any(|a| matches!(a, FieldAttribute::Arbitrary(a) if a.contains("positional")))
70                {
71                    let field = wip.field(field_index).unwrap();
72                    wip = parse_field(field, token).unwrap();
73                    break;
74                }
75            }
76        }
77    }
78    wip.build().unwrap().materialize().unwrap()
79}