facet_args/
format.rs

1use crate::arg::{ArgType, extract_subspan};
2use crate::fields::*;
3use crate::parse::parse_scalar;
4use crate::results::*;
5use alloc::borrow::Cow;
6use core::fmt;
7use facet_core::Facet;
8use facet_deserialize::{
9    DeserError, DeserErrorKind, Expectation, Format, NextData, NextResult, Outcome, Raw, Scalar,
10    Span, Spanned,
11};
12
13/// Command-line argument format for Facet deserialization
14pub struct Cli;
15
16impl fmt::Display for Cli {
17    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18        write!(f, "Cli")
19    }
20}
21
22/// Parse command line arguments into a Facet-compatible type
23pub fn from_slice<'input, 'facet, 'shape, T: Facet<'facet>>(
24    args: &'input [&'input str],
25) -> Result<T, DeserError<'input, 'shape>>
26where
27    'input: 'facet + 'shape,
28{
29    facet_deserialize::deserialize(args, Cli)
30}
31
32impl Format for Cli {
33    type Input<'input> = [&'input str];
34    type SpanType = Raw;
35
36    fn source(&self) -> &'static str {
37        "args"
38    }
39
40    fn next<'input, 'facet, 'shape>(
41        &mut self,
42        nd: NextData<'input, 'facet, 'shape, Self::SpanType, Self::Input<'input>>,
43        expectation: Expectation,
44    ) -> NextResult<
45        'input,
46        'facet,
47        'shape,
48        Spanned<Outcome<'input>, Self::SpanType>,
49        Spanned<DeserErrorKind<'shape>, Self::SpanType>,
50        Self::SpanType,
51        Self::Input<'input>,
52    >
53    where
54        'shape: 'input,
55    {
56        let arg_idx = nd.start();
57        let shape = nd.wip.shape();
58        let args = nd.input();
59        let subspans = nd.substack().get();
60        let has_subspans = !subspans.is_empty();
61
62        let stay_put = Span::new(arg_idx, 0);
63        let step_forth = Span::new(arg_idx, 1);
64
65        let span = match expectation {
66            Expectation::Value => stay_put,
67            Expectation::ObjectKeyOrObjectClose
68            | Expectation::ObjectVal
69            | Expectation::ListItemOrListClose => step_forth,
70        };
71
72        let result = match expectation {
73            // Top-level value
74            Expectation::Value => {
75                // Check if it's a struct type
76                wrap_outcome_result(validate_struct_type(shape), Outcome::ObjectStarted, span)
77            }
78
79            // Object key (or finished)
80            Expectation::ObjectKeyOrObjectClose => {
81                /* Check if we have more arguments */
82                if arg_idx < args.len() {
83                    let arg = args[arg_idx];
84
85                    // Check if we need to resegment an arg with '='
86                    if arg.starts_with("-") && arg.contains('=') && !has_subspans {
87                        // This is an argument with '=' that needs resegmentation
88                        if let Some(key_value_subspans) = create_key_value_subspans(arg) {
89                            return (nd, wrap_resegmented_result(key_value_subspans, stay_put));
90                        }
91                    }
92
93                    // Regular argument or subspan processing
94                    let effective_arg = if has_subspans {
95                        extract_subspan(&subspans[0], arg)
96                    } else {
97                        arg
98                    };
99
100                    // Parse the argument type
101                    match ArgType::parse(effective_arg) {
102                        ArgType::LongFlag(key) => {
103                            // Validate field exists
104                            wrap_string_result(
105                                validate_field(&key, shape, &nd.wip).map(|_| key),
106                                if has_subspans { stay_put } else { span },
107                            )
108                        }
109                        ArgType::ShortFlag(key) => {
110                            // Convert short argument to field name via shape
111                            wrap_field_result(
112                                find_field_by_short_flag(key, shape),
113                                if has_subspans { stay_put } else { span },
114                            )
115                        }
116                        ArgType::Positional => {
117                            // Handle positional argument
118                            wrap_field_result(find_positional_field(shape, &nd.wip), stay_put)
119                        }
120                        ArgType::None => {
121                            // Handle empty argument (shouldn't happen normally)
122                            let err = create_unknown_field_error("empty argument", shape);
123                            Err(Spanned { node: err, span })
124                        }
125                    }
126                } else {
127                    // EOF: inject implicit-false-if-absent bool flags, if there are any
128                    handle_unset_bool_field_error(find_unset_bool_field(shape, &nd.wip), span)
129                }
130            }
131
132            // Value for the current key
133            Expectation::ObjectVal => {
134                // Determine what to do based on the type and available arguments
135                if shape.is_type::<bool>() {
136                    // Handle boolean values (true if we have an arg, false if EOF)
137                    let has_arg = arg_idx < args.len();
138                    wrap_result(handle_bool_value(has_arg), Outcome::Scalar, stay_put)
139                } else {
140                    // For non-boolean types, check if we have subspans
141                    let result = if has_subspans && arg_idx < args.len() {
142                        let arg = args[arg_idx];
143                        let subspan = &subspans[1];
144                        let arg_type: ArgType = (subspan, arg).into();
145
146                        // If this isn't a flag type (neither ShortFlag nor LongFlag), use it as a value
147                        match arg_type {
148                            ArgType::ShortFlag(_) | ArgType::LongFlag(_) => {
149                                // It's a flag, not a value - continue to validation
150                                None
151                            }
152                            _ => {
153                                // Extract the actual substring to use
154                                let part = extract_subspan(subspan, arg);
155                                Some(Ok(parse_scalar(part, span)))
156                            }
157                        }
158                    } else {
159                        None
160                    };
161
162                    // Use the result from above if available, otherwise fall back to regular validation
163                    result.unwrap_or_else(|| {
164                        // No usable subspans, fall back to regular validation
165                        match validate_value_available(arg_idx, args) {
166                            Ok(arg) => Ok(parse_scalar(arg, span)),
167                            Err(err) => Err(Spanned {
168                                node: err,
169                                span: Span::new(arg_idx.saturating_sub(1), 0),
170                            }),
171                        }
172                    })
173                }
174            }
175
176            // List items
177            Expectation::ListItemOrListClose => {
178                // End the list if we're out of arguments, or if it's a new flag
179                if is_list_ended(arg_idx, args) {
180                    // End the list
181                    Ok(Spanned {
182                        node: Outcome::ListEnded,
183                        span,
184                    })
185                } else {
186                    // Process the next item
187                    Ok(Spanned {
188                        node: Outcome::Scalar(Scalar::String(Cow::Borrowed(args[arg_idx]))),
189                        span: step_forth,
190                    })
191                }
192            }
193        };
194
195        (nd, result)
196    }
197
198    fn skip<'input, 'facet, 'shape>(
199        &mut self,
200        nd: NextData<'input, 'facet, 'shape, Self::SpanType, Self::Input<'input>>,
201    ) -> NextResult<
202        'input,
203        'facet,
204        'shape,
205        Span<Self::SpanType>,
206        Spanned<DeserErrorKind<'shape>, Self::SpanType>,
207        Self::SpanType,
208        Self::Input<'input>,
209    >
210    where
211        'shape: 'input,
212    {
213        let arg_idx = nd.start();
214        let args = nd.input();
215        let span = Span::new(arg_idx, 1);
216
217        let result = if arg_idx < args.len() {
218            // Simply skip one position
219            Ok(span)
220        } else {
221            // No argument to skip
222            Err(Spanned {
223                node: DeserErrorKind::UnexpectedEof {
224                    wanted: "argument to skip",
225                },
226                span,
227            })
228        };
229
230        (nd, result)
231    }
232}