1use core::fmt::Debug;
4use core::str::FromStr;
5
6use heapless::Vec;
7
8use crate::lexer::{Flag, Token};
9
10mod values;
11
12pub use values::{AtMost, Values};
13
14#[cfg(test)]
15mod tests;
16
17#[derive(Debug, PartialEq, Eq, thiserror::Error)]
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20#[non_exhaustive]
21pub enum Error {
22 #[error("undefined argument")]
24 UndefinedArgument,
25
26 #[error("invalid argument")]
29 InvalidArgument,
30
31 #[error("no value expected")]
33 NoValueArgument,
34
35 #[error("missing argument")]
38 MissingArgument,
39
40 #[error("out of parser memory space")]
42 OutOfMemory,
43}
44
45pub type Result<T, E = Error> = core::result::Result<T, E>;
47
48#[derive(Clone, Debug, PartialEq)]
50pub enum Arg<'a> {
51 Named(&'a str, Values<'a>),
53
54 Positional(&'a str),
56}
57
58#[derive(Debug)]
60pub struct ArgLookupTable<'a> {
61 table: &'a [(Flag<'a>, &'a str, AtMost)],
62}
63
64impl<'a> ArgLookupTable<'a> {
65 pub const fn new(table: &'a [(Flag<'a>, &'a str, AtMost)]) -> Self {
67 ArgLookupTable { table }
68 }
69
70 pub fn metadata_of(&self, flag: &Flag<'_>) -> Option<(&'a str, AtMost)> {
72 let (_, id, expected) = self.table.iter().find(|&x| x.0 == *flag)?;
73 Some((*id, *expected))
74 }
75}
76
77#[derive(Default, Debug)]
80#[cfg_attr(feature = "defmt", derive(defmt::Format))]
81pub struct ParsedArgs<'a, const CAPACITY: usize = 1> {
82 args: Vec<Arg<'a>, CAPACITY>,
83}
84
85impl<'a, const CAPACITY: usize> ParsedArgs<'a, CAPACITY> {
86 pub fn parse_from(argv: &'a [&'a str], ids: &ArgLookupTable<'static>) -> Self {
88 Self::try_parse_from(argv, ids).expect("cannot parse arguments")
89 }
90
91 pub fn try_parse_from(
93 argv: &'a [&'a str],
94 table: &ArgLookupTable<'static>,
95 ) -> Result<Self, Error> {
96 Self::check_capacity(argv)?;
98 Self::check_undefined_argument(argv, table)?;
99
100 let mut parsed = ParsedArgs::default();
101
102 let lookup = |flag: &Flag<'a>| {
103 unsafe { table.metadata_of(flag).unwrap_unchecked() }
105 };
106
107 let named = |args: &mut Vec<_, _>, name, expected, (start, end)| {
108 let (rest, arg) = Self::parse_arg_values(&argv[start..end], name, expected);
109
110 unsafe { args.push(arg).unwrap_unchecked() };
113
114 for value in rest.iter() {
115 unsafe { args.push(Arg::Positional(value)).unwrap_unchecked() };
118 }
119 };
120
121 let positional = |args: &mut Vec<_, _>, value| {
122 unsafe { args.push(Arg::Positional(value)).unwrap_unchecked() }
125 };
126
127 let parse_then_push =
128 |state, (index, arg): (usize, &&'a str)| match (state, Token::tokenize(arg)) {
129 (Some((flag, start)), Token::Flag(next)) => {
132 let (name, expected) = lookup(&flag);
133 named(&mut parsed.args, name, expected, (start, index));
134 Some((next, index + 1))
135 }
136
137 (Some(_), Token::Value(_)) => state,
139
140 (None, Token::Flag(flag)) => Some((flag, index + 1)),
143
144 (None, Token::Value(value)) => {
146 positional(&mut parsed.args, value);
147 None
148 }
149 };
150
151 let last_flag = argv.iter().enumerate().fold(None, parse_then_push);
152
153 if let Some((flag, start)) = last_flag {
154 let (name, expected) = lookup(&flag);
155 named(&mut parsed.args, name, expected, (start, argv.len()));
156 }
157
158 Ok(parsed)
159 }
160
161 #[inline(always)]
163 pub fn contains(&self, id: &str) -> bool {
164 self.args
165 .iter()
166 .any(|arg| matches!(arg, Arg::Named(name, _) if id == *name))
167 }
168
169 pub fn get_one<T>(&self, id: &str) -> Option<Option<T>>
171 where
172 T: FromStr,
173 {
174 self.try_get_one::<T>(id).expect("invalid argument")
175 }
176
177 pub fn get_many<B, T>(&self, id: &str) -> Option<B>
179 where
180 B: FromIterator<T>,
181 T: FromStr,
182 {
183 self.try_get_many::<B, T>(id).expect("invalid argument")
184 }
185
186 pub fn try_get_one<T>(&self, id: &str) -> Result<Option<Option<T>>, Error>
188 where
189 T: FromStr,
190 {
191 if let Some(Arg::Named(_, values)) = self
192 .args
193 .iter()
194 .find(|&x| matches!(x, Arg::Named(name, _) if *name == id))
195 {
196 let mut iter = values.iter();
197
198 let value = if let Some(value) = iter.next() {
199 value
200 } else {
201 return Ok(Some(None));
203 };
204
205 if iter.next().is_some() {
206 return Err(Error::InvalidArgument);
208 }
209
210 return value
211 .parse::<T>()
212 .map(Some)
214 .map(Some)
215 .map_err(|_| Error::InvalidArgument);
217 }
218
219 Ok(None)
221 }
222
223 pub fn try_get_many<B, T>(&self, id: &str) -> Result<Option<B>, Error>
226 where
227 B: FromIterator<T>,
228 T: FromStr,
229 {
230 if let Some(Arg::Named(_, values)) = self
231 .args
232 .iter()
233 .find(|&x| matches!(x, Arg::Named(name, _) if *name == id))
234 {
235 return Ok(Some(
236 values
237 .iter()
238 .map(|x| x.parse::<T>())
239 .collect::<Result<B, _>>()
240 .map_err(|_| Error::InvalidArgument)?,
241 ));
242 }
243
244 Ok(None)
245 }
246
247 fn check_capacity(argv: &[&str]) -> Result<()> {
248 if CAPACITY < argv.len() {
249 return Err(Error::OutOfMemory);
250 }
251 Ok(())
252 }
253
254 fn check_undefined_argument(argv: &[&str], table: &ArgLookupTable<'_>) -> Result<()> {
255 let undefined = argv
256 .iter()
257 .map(|&x| Token::tokenize(x))
258 .any(|x| matches!(x, Token::Flag(flag) if table.metadata_of(&flag).is_none()));
259
260 if undefined {
261 return Err(Error::UndefinedArgument);
262 }
263
264 Ok(())
265 }
266
267 fn parse_arg_values<'b>(
268 argv: &'b [&'b str],
269 name: &'b str,
270 expected: AtMost,
271 ) -> (Values<'b>, Arg<'b>) {
272 match expected {
273 AtMost::Zero => (Values::new(argv), Arg::Named(name, Values::empty())),
274 AtMost::One => {
275 let rest = if argv.len() <= 1 { &[] } else { &argv[1..] };
276 let arg = if argv.is_empty() { &[] } else { &argv[..1] };
277 (Values::new(rest), Arg::Named(name, Values::new(arg)))
278 }
279 AtMost::Many => (Values::empty(), Arg::Named(name, Values::new(argv))),
280 }
281 }
282}