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
7extern crate alloc;
8use alloc::borrow::Cow;
9
10pub(crate) mod defaults;
12pub mod error;
14pub(crate) mod parse;
16
17use defaults::apply_field_defaults;
18use error::{ArgsError, ArgsErrorKind};
19use facet_core::{Def, Facet, Type, UserType};
20use facet_reflect::{ReflectError, Wip};
21use parse::{parse_named_arg, parse_positional_arg, parse_short_arg};
23
24pub(crate) fn parse_field<'facet>(
26 wip: Wip<'facet>,
27 value: &'facet str,
28) -> Result<Wip<'facet>, ArgsError> {
29 let shape = wip.shape();
30
31 if shape.is_type::<String>() {
32 log::trace!("shape is String");
33 wip.put(value.to_string())
34 } else if shape.is_type::<&str>() {
35 log::trace!("shape is &str");
36 wip.put(value)
37 } else if shape.is_type::<bool>() {
38 log::trace!("shape is bool, setting to true");
39 wip.put(value.to_lowercase() == "true")
40 } else {
41 match shape.def {
42 Def::Scalar(_) => {
43 log::trace!("shape is nothing known, falling back to parse: {}", shape);
44 wip.parse(value)
45 }
46 _def => {
47 return Err(ArgsError::new(ArgsErrorKind::GenericReflect(
48 ReflectError::OperationFailed {
49 shape,
50 operation: "parsing field",
51 },
52 )));
53 }
54 }
55 }
56 .map_err(|e| ArgsError::new(ArgsErrorKind::GenericReflect(e)))?
57 .pop()
58 .map_err(|e| ArgsError {
59 kind: ArgsErrorKind::GenericReflect(e),
60 })
61}
62
63fn kebab_to_snake(input: &str) -> Cow<str> {
64 if !input.contains('-') {
67 return Cow::Borrowed(input);
68 }
69 Cow::Owned(input.replace('-', "_"))
70}
71
72pub fn from_slice<'input, 'facet, T>(s: &[&'input str]) -> Result<T, ArgsError>
74where
75 T: Facet<'facet>,
76 'input: 'facet,
77{
78 log::trace!("Entering from_slice function");
79 let mut s = s;
80 let mut wip =
81 Wip::alloc::<T>().map_err(|e| ArgsError::new(ArgsErrorKind::GenericReflect(e)))?;
82 log::trace!("Allocated Poke for type T");
83 let Type::User(UserType::Struct(st)) = wip.shape().ty else {
84 return Err(ArgsError::new(ArgsErrorKind::GenericArgsError(
85 "Expected struct type".to_string(),
86 )));
87 };
88
89 while let Some(token) = s.first() {
90 log::trace!("Processing token: {}", token);
91 s = &s[1..];
92
93 if let Some(key) = token.strip_prefix("--") {
94 let key = kebab_to_snake(key);
95 log::trace!("Found named argument: {}", key);
96 wip = parse_named_arg(wip, &key, &mut s)?;
97 } else if let Some(key) = token.strip_prefix("-") {
98 log::trace!("Found short named argument: {}", key);
99 wip = parse_short_arg(wip, key, &mut s, &st)?;
100 } else {
101 log::trace!("Encountered positional argument: {}", token);
102 wip = parse_positional_arg(wip, token, &st)?;
103 }
104 }
105
106 wip = apply_field_defaults(wip)?;
108
109 for (field_index, f) in st.fields.iter().enumerate() {
113 if f.shape().is_type::<bool>() && !wip.is_field_set(field_index).expect("in bounds") {
114 let field = wip.field(field_index).expect("field_index is in bounds");
115 wip = parse_field(field, "false")?;
116 }
117 }
118
119 log::trace!("Checking field attributes");
121 for (i, field) in st.fields.iter().enumerate() {
122 log::trace!(
123 "Field {}: {} - Attributes: {:?}",
124 i,
125 field.name,
126 field.attributes
127 );
128 }
129
130 let heap_vale = wip
131 .build()
132 .map_err(|e| ArgsError::new(ArgsErrorKind::GenericReflect(e)))?;
133 let result = heap_vale
134 .materialize()
135 .map_err(|e| ArgsError::new(ArgsErrorKind::GenericReflect(e)))?;
136 Ok(result)
137}