1use core::fmt::Debug;
4use core::str::FromStr;
5
6use heapless::Vec;
7
8use crate::lexer::{Flag, IntoTokens, Token, Values};
9
10#[derive(Debug, PartialEq, Eq, thiserror::Error)]
12#[cfg_attr(feature = "defmt", derive(defmt::Format))]
13#[non_exhaustive]
14pub enum Error {
15 #[error("undefined argument")]
17 UndefinedArgument,
18
19 #[error("invalid argument")]
22 InvalidArgument,
23
24 #[error("missing argument")]
27 MissingArgument,
28
29 #[error("out of parser memory space")]
31 OutOfMemory,
32}
33
34#[derive(Debug, Default)]
37#[cfg_attr(feature = "defmt", derive(defmt::Format))]
38pub struct ParsedArgs<'a, const ARG_COUNT_MAX: usize = 8> {
39 args: Vec<(&'a str, Values<'a>), ARG_COUNT_MAX>,
40}
41
42impl<'a, const SIZE: usize> ParsedArgs<'a, SIZE> {
43 pub fn parse(argv: impl IntoTokens<'a>, ids: &[(Flag<'a>, &'a str)]) -> Self {
45 Self::try_parse(argv, ids).expect("cannot parse arguments")
46 }
47
48 pub fn try_parse(
50 argv: impl IntoTokens<'a>,
51 ids: &[(Flag<'a>, &'a str)],
52 ) -> Result<Self, Error> {
53 let mut tokens = argv.into_tokens();
54
55 let mut out = Self::default();
56
57 while let Some(token) = tokens.next() {
58 if let Token::Flag(f) = &token {
59 let id = if let Some((_, id)) = ids.iter().find(|x| f == &x.0) {
60 id
61 } else {
62 return Err(Error::UndefinedArgument);
63 };
64
65 if out.args.push((id, tokens.values())).is_err() {
66 return Err(Error::OutOfMemory);
67 }
68 }
69 }
70
71 Ok(out)
72 }
73
74 #[inline(always)]
76 pub fn contains(&self, id: &str) -> bool {
77 self.args.iter().any(|x| id == x.0)
78 }
79
80 pub fn get_one<T>(&self, id: &str) -> Option<Option<T>>
82 where
83 T: FromStr,
84 {
85 self.try_get_one::<T>(id).expect("invalid argument")
86 }
87
88 pub fn get_many<B, T>(&self, id: &str) -> Option<B>
90 where
91 B: FromIterator<T>,
92 T: FromStr,
93 {
94 self.try_get_many::<B, T>(id).expect("invalid argument")
95 }
96
97 pub fn try_get_one<T>(&self, id: &str) -> Result<Option<Option<T>>, Error>
99 where
100 T: FromStr,
101 {
102 if let Some((_, values)) = self.args.iter().find(|x| id == x.0) {
103 let mut iter = values.clone();
104
105 let value = if let Some(value) = iter.next() {
106 value
107 } else {
108 return Ok(Some(None));
110 };
111
112 if iter.next().is_some() {
113 return Err(Error::InvalidArgument);
115 }
116
117 return value
118 .parse::<T>()
119 .map(Some)
121 .map(Some)
122 .map_err(|_| Error::InvalidArgument);
124 }
125
126 Ok(None)
128 }
129
130 pub fn try_get_many<B, T>(&self, id: &str) -> Result<Option<B>, Error>
133 where
134 B: FromIterator<T>,
135 T: FromStr,
136 {
137 if let Some((_, values)) = self.args.iter().find(|x| x.0 == id) {
138 let iter = values.clone();
139
140 let result: Result<B, _> = iter.map(|x| x.parse::<T>()).collect();
142
143 return result.map(Some).map_err(|_| Error::InvalidArgument);
145 }
146
147 Ok(None)
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use googletest::prelude::*;
154
155 use crate::lexer::Tokens;
156
157 use super::*;
158
159 #[test]
160 fn it_should_parse_missing_arg() {
161 let ids = &[(Flag::Short('v'), "verbose")];
162 let tokens = Tokens::new(&["-v"]);
163
164 let args: ParsedArgs<'_, 1> = ParsedArgs::parse(tokens, ids);
165
166 assert_that!(args.try_get_one::<u32>("verbose"), eq(&Ok(Some(None))));
167 }
168
169 #[test]
170 fn it_should_parse_invalid_arg() {
171 let ids = &[(Flag::Short('v'), "verbose")];
172 let tokens = Tokens::new(&["-v", "-42"]);
173
174 let args: ParsedArgs<'_, 1> = ParsedArgs::parse(tokens, ids);
175
176 assert_that!(
177 args.try_get_one::<u32>("verbose"),
178 eq(&Err(Error::InvalidArgument))
179 );
180 }
181
182 #[test]
183 fn it_should_parse_valid_value() {
184 let ids = &[(Flag::Short('v'), "verbose")];
185 let tokens = Tokens::new(&["-v", "42"]);
186
187 let args: ParsedArgs<'_, 1> = ParsedArgs::parse(tokens, ids);
188
189 assert_that!(
190 args.try_get_one::<u32>("verbose"),
191 matches_pattern!(&Ok(Some(Some(42))))
192 );
193 }
194}