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<'facet>(wip: Wip<'facet>, value: &'facet str) -> Result<Wip<'facet>, 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::<&str>() {
17                wip.put(value)
18            } else if shape.is_type::<bool>() {
19                log::trace!("Boolean field detected, setting to true");
20                wip.put(value.to_lowercase() == "true")
21            } else {
22                wip.parse(value)
23            }
24        }
25        _def => {
26            return Err(ReflectError::OperationFailed {
27                shape,
28                operation: "parsing field",
29            });
30        }
31    }?
32    .pop()
33}
34
35/// Parses command-line arguments
36pub fn from_slice<'input, 'facet, T>(s: &[&'input str]) -> T
37where
38    T: Facet<'facet>,
39    'input: 'facet,
40{
41    log::trace!("Entering from_slice function");
42    let mut s = s;
43    let mut wip = Wip::alloc::<T>().expect("failed to allocate");
44    log::trace!("Allocated Poke for type T");
45
46    while let Some(token) = s.first() {
47        log::trace!("Processing token: {}", token);
48        s = &s[1..];
49
50        if let Some(key) = token.strip_prefix("--") {
51            log::trace!("Found named argument: {}", key);
52            let field_index = match wip.field_index(key) {
53                Some(index) => index,
54                None => panic!("Unknown argument: {}", key),
55            };
56            let field = wip.field(field_index).unwrap();
57
58            if field.shape().is_type::<bool>() {
59                wip = parse_field(field, "true").unwrap();
60            } else {
61                let value = s.first().expect("expected value after argument");
62                log::trace!("Field value: {}", value);
63                s = &s[1..];
64                wip = parse_field(field, value).unwrap();
65            }
66        } else {
67            log::trace!("Encountered positional argument: {}", token);
68            let Def::Struct(sd) = wip.shape().def else {
69                panic!("Expected struct definition");
70            };
71
72            for (field_index, f) in sd.fields.iter().enumerate() {
73                if f.attributes
74                    .iter()
75                    .any(|a| matches!(a, FieldAttribute::Arbitrary(a) if a.contains("positional")))
76                {
77                    let field = wip.field(field_index).unwrap();
78                    wip = parse_field(field, token).unwrap();
79                    break;
80                }
81            }
82        }
83    }
84    wip.build().unwrap().materialize().unwrap()
85}