1use core::str::FromStr;
4
5use heapless::Vec;
6
7use crate::lexer;
8
9#[derive(Debug, PartialEq, Eq, thiserror::Error)]
11#[cfg_attr(feature = "defmt", derive(defmt::Format))]
12#[non_exhaustive]
13pub enum Error {
14 #[error("invalid argument")]
17 InvalidArgument,
18
19 #[error("missing argument")]
22 MissingArgument,
23}
24
25#[derive(Debug, Default)]
28#[cfg_attr(feature = "defmt", derive(defmt::Format))]
29pub struct ParsedArgs<'a, const ARG_COUNT_MAX: usize = 8> {
30 args: Vec<lexer::Token<'a>, ARG_COUNT_MAX>,
31}
32
33impl<'a> ParsedArgs<'a> {
34 pub fn parse<I>(tokens: I) -> Self
36 where
37 I: Iterator<Item = lexer::Token<'a>>,
38 {
39 let mut out = Self::default();
40
41 for token in tokens {
42 out.args.push(token).ok();
43 }
44
45 out
46 }
47
48 pub fn get<'k, T>(&self, key: &'k str) -> Result<Option<T>, Error>
50 where
51 'a: 'k,
52 T: FromStr,
53 {
54 let mut wait_for_value = false;
55
56 for token in &self.args {
57 if !wait_for_value && match_key_with_token(key, token) {
58 wait_for_value = true;
59 continue;
60 }
61
62 if wait_for_value {
63 if let lexer::Token::Value(value) = token {
64 let typed = value.parse::<T>().map_err(|_| Error::InvalidArgument)?;
65 return Ok(Some(typed));
66 }
67
68 return Err(Error::MissingArgument);
69 }
70 }
71
72 if wait_for_value {
74 return Err(Error::MissingArgument);
75 }
76
77 Ok(None)
78 }
79
80 pub fn is_enabled<'k>(&self, key: &'k str) -> bool
82 where
83 'a: 'k,
84 {
85 for token in &self.args {
86 if match_key_with_token(key, token) {
87 return true;
88 }
89 }
90
91 false
92 }
93}
94
95fn match_key_with_token<'a, 'k>(key: &'k str, token: &lexer::Token<'a>) -> bool
96where
97 'a: 'k,
98{
99 match token {
100 lexer::Token::ShortFlag(name) => key.len() == 1 && *name == key.chars().next().unwrap(),
101 lexer::Token::LongFlag(name) => key.len() > 1 && *name == key,
102 _ => false,
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use googletest::prelude::*;
109
110 use crate::Lexer;
111
112 use super::*;
113
114 #[test]
115 fn it_should_parse_valid_value() {
116 let lexer = Lexer::new(&["-v", "42"]);
117 let args = ParsedArgs::parse(lexer);
118 assert_that!(args.get::<u32>("v"), matches_pattern!(&Ok(Some(42))));
119 }
120
121 #[test]
122 fn it_should_parse_invalid_arg() {
123 let lexer = Lexer::new(&["-v", "-42"]);
124 let args = ParsedArgs::parse(lexer);
125 assert_that!(args.get::<u32>("v"), eq(&Err(Error::InvalidArgument)));
126 }
127
128 #[test]
129 fn it_should_parse_missing_arg() {
130 let lexer = Lexer::new(&["-v"]);
131 let args = ParsedArgs::parse(lexer);
132 assert_that!(args.get::<u32>("v"), eq(&Err(Error::MissingArgument)));
133 }
134
135 #[test]
136 fn it_should_parse_enabled_bool_arg() {
137 let lexer = Lexer::new(&["-v"]);
138 let args = ParsedArgs::parse(lexer);
139 assert_that!(args.is_enabled("v"), eq(true));
140 assert_that!(args.get::<bool>("v"), eq(&Err(Error::MissingArgument)));
141 }
142
143 #[test]
144 fn it_should_parse_enabled_bool_arg_with_value() {
145 let lexer = Lexer::new(&["-v", "true"]);
146 let args = ParsedArgs::parse(lexer);
147 assert_that!(args.is_enabled("v"), eq(true));
148 assert_that!(args.get::<bool>("v"), eq(&Ok(Some(true))));
149 }
150}