1use crate::{ArgParseError, InteractiveError};
3
4pub trait ArgParse: Sized {
6 fn arg_parse(s: &str) -> Result<Self, ArgParseError<'_>>;
8}
9
10fn parse_arg<'a, T: ArgParse>(
11 method_name: &'a str,
12 haystack: &mut &'a str,
13 expected: usize,
14 found: usize,
15) -> crate::Result<'a, T> {
16 let arg_str = get_next_arg(method_name, haystack, expected, found)?;
17
18 ArgParse::arg_parse(arg_str).map_err(|e| InteractiveError::ArgParseError {
19 method_name,
20 error: e,
21 })
22}
23
24fn get_next_arg<'a>(
25 method_name: &'a str,
26 haystack: &mut &'a str,
27 expected: usize,
28 found: usize,
29) -> crate::Result<'a, &'a str> {
30 match find_next_separator_index(haystack) {
31 Some(arg_end_idx) => {
32 let (arg_str, rest_str) = haystack.split_at(arg_end_idx);
33 let arg_str = arg_str.trim();
34 if arg_str.is_empty() {
35 return Err(InteractiveError::SyntaxError {});
37 }
38 *haystack = &rest_str[1..]; Ok(arg_str)
40 }
41 None => {
42 let arg_str = haystack.trim();
43 if arg_str.is_empty() {
44 return Err(InteractiveError::WrongNumberOfArguments {
46 method_name,
47 expected,
48 found,
49 });
50 }
51 *haystack = "";
52 Ok(arg_str)
53 }
54 }
55}
56
57fn clear_args<'a>(
58 method_name: &'a str,
59 haystack: &mut &'a str,
60 expected: usize,
61 mut found: usize,
62) -> crate::Result<'a, ()> {
63 if !haystack.is_empty() {
64 loop {
65 get_next_arg(method_name, haystack, expected, found)?;
66 found += 1;
67 }
68 }
69
70 Ok(())
71}
72
73fn find_next_separator_index(s: &str) -> Option<usize> {
74 let mut chars = s.char_indices();
75 let mut inside_single_quotes = false;
76 let mut inside_double_quotes = false;
77
78 while let Some((idx, c)) = chars.next() {
79 match c {
80 '\\' => {
81 chars.next();
82 }
83 ',' => {
84 if !inside_double_quotes && !inside_single_quotes {
85 return Some(idx);
86 }
87 }
88 '\'' => {
89 if !inside_double_quotes {
90 inside_single_quotes = !inside_single_quotes;
91 }
92 }
93 '"' => {
94 if !inside_single_quotes {
95 inside_double_quotes = !inside_double_quotes;
96 }
97 }
98 _ => {}
99 }
100 }
101 None
102}
103
104#[allow(missing_docs)]
105pub fn parse_0_args<'a>(method_name: &'a str, mut args: &'a str) -> crate::Result<'a, ()> {
106 clear_args(method_name, &mut args, 0, 0)
107}
108
109macro_rules! parse_x_args {
110 ($funcname:ident::<$($TN:ident),*>, ($($i:literal),*), x=$x:literal) => {
111 #[allow(non_snake_case, missing_docs)]
112 pub fn $funcname<'a, $($TN: ArgParse,)*>(
113 method_name: &'a str,
114 mut args: &'a str,
115 ) -> crate::Result<'a, ($($TN,)*)> {
116 $(let $TN = parse_arg(method_name, &mut args, $x, $i)?;)*
117 clear_args(method_name, &mut args, $x, $x)?;
118 Ok(($($TN,)*))
119 }
120 };
121}
122
123parse_x_args!(parse_1_arg::<T0>, (0), x = 1);
124parse_x_args!(parse_2_args::<T0, T1>, (0, 1), x = 2);
125parse_x_args!(parse_3_args::<T0, T1, T2>, (0, 1, 2), x = 3);
126parse_x_args!(parse_4_args::<T0, T1, T2, T3>, (0, 1, 2, 3), x = 4);
127parse_x_args!(parse_5_args::<T0, T1, T2, T3, T4>, (0, 1, 2, 3, 4), x = 5);
128parse_x_args!(
129 parse_6_args::<T0, T1, T2, T3, T4, T5>,
130 (0, 1, 2, 3, 4, 5),
131 x = 6
132);
133
134macro_rules! parse_int {
135 ($($t:ty),*) => (
136 $(impl ArgParse for $t {
137 fn arg_parse(s: &str) -> Result<Self, ArgParseError<'_>> {
138 s.parse().map_err(ArgParseError::ParseIntError)
139 }
140 })*
141 )
142}
143
144parse_int!(i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize);
145
146impl ArgParse for bool {
147 fn arg_parse(s: &str) -> Result<Self, ArgParseError<'_>> {
148 s.parse().map_err(ArgParseError::ParseBoolError)
149 }
150}
151
152impl ArgParse for f32 {
153 fn arg_parse(s: &str) -> Result<Self, ArgParseError<'_>> {
154 s.parse().map_err(ArgParseError::ParseFloatError)
155 }
156}
157
158impl ArgParse for f64 {
159 fn arg_parse(s: &str) -> Result<Self, ArgParseError<'_>> {
160 s.parse().map_err(ArgParseError::ParseFloatError)
161 }
162}
163
164impl ArgParse for char {
165 fn arg_parse(s: &str) -> Result<Self, ArgParseError<'_>> {
166 unescape_char(s)
167 }
168}
169
170#[cfg(feature = "std")]
171impl ArgParse for String {
172 fn arg_parse(s: &str) -> Result<Self, ArgParseError<'_>> {
173 unescape_str(s)
174 }
175}
176
177fn unescape_char(s: &str) -> Result<char, ArgParseError<'_>> {
179 let mut chars = s.chars();
180 if chars.next() != Some('\'') {
181 return Err(ArgParseError::UnescapeError(s));
182 }
183 if chars.next_back() != Some('\'') {
184 return Err(ArgParseError::UnescapeError(s));
185 }
186 if chars.as_str().starts_with('\\') {
187 chars.next(); get_escaped_char(&mut chars).ok_or(ArgParseError::UnescapeError(s))
189 } else {
190 chars
191 .as_str()
192 .parse()
193 .map_err(ArgParseError::ParseCharError)
194 }
195}
196
197#[cfg(feature = "std")]
199fn unescape_str(s: &str) -> Result<String, ArgParseError<'_>> {
200 let mut chars = s.chars();
201 if chars.next() != Some('\"') {
202 return Err(ArgParseError::UnescapeError(s));
203 }
204 if chars.next_back() != Some('\"') {
205 return Err(ArgParseError::UnescapeError(s));
206 }
207
208 let mut res = String::with_capacity(chars.as_str().len());
209
210 while let Some(c) = chars.next() {
211 match c {
212 '\\' => {
213 let c = get_escaped_char(&mut chars).ok_or(ArgParseError::UnescapeError(s))?;
214 res.push(c)
215 }
216 _ => res.push(c),
217 }
218 }
219
220 Ok(res)
221}
222
223fn get_escaped_char(after_backslash: &mut core::str::Chars<'_>) -> Option<char> {
226 match after_backslash.next() {
227 None => None,
228 Some(char) => match char {
229 'n' => Some('\n'),
230 'r' => Some('\r'),
231 't' => Some('\t'),
232 '\\' => Some('\\'),
233 '0' => Some('\0'),
234 '\'' => Some('\''),
235 '\"' => Some('\"'),
236 'x' => parse_ascii(after_backslash),
237 'u' => parse_unicode(after_backslash),
238 _ => None,
239 },
240 }
241}
242
243fn parse_ascii(after_x: &mut core::str::Chars<'_>) -> Option<char> {
245 let hex = after_x.as_str().get(..2)?;
246 let ascii = u32::from_str_radix(hex, 16).ok()?;
247 if ascii > 0x7f {
248 return None;
249 };
250 let res = core::char::from_u32(ascii);
251
252 after_x.nth(1);
254 res
255}
256
257fn parse_unicode(after_u: &mut core::str::Chars<'_>) -> Option<char> {
259 if after_u.next() != Some('{') {
260 return None;
261 }
262 if let Some(hex_end) = after_u.as_str().find('}') {
263 let hex = &after_u.as_str()[..hex_end];
264 let res = u32::from_str_radix(hex, 16)
265 .ok()
266 .and_then(core::char::from_u32);
267
268 after_u.nth(hex_end);
270 res
271 } else {
272 None
273 }
274}
275
276#[cfg(test)]
277mod tests {
278 use super::*;
279
280 fn test_parse_one_arg<T: ArgParse + PartialEq + core::fmt::Debug>(arg: &str, expected: T) {
281 let result: T = parse_1_arg("", arg).unwrap().0;
282 assert_eq!(result, expected);
283 }
284
285 #[test]
286 fn test_floats() {
287 test_parse_one_arg("1", 1f32);
288 test_parse_one_arg("1", 1f64);
289 test_parse_one_arg("-1", -1f32);
290 test_parse_one_arg("-1.0", -1f32);
291 }
292
293 #[test]
294 fn test_ints() {
295 test_parse_one_arg("1", 1u8);
296 test_parse_one_arg("-1", -1i128);
297 }
298
299 #[test]
300 fn test_bool() {
301 test_parse_one_arg("true", true);
302 test_parse_one_arg("false", false);
303 }
304
305 #[test]
306 fn test_char() {
307 test_parse_one_arg("'t'", 't');
308 }
309
310 #[test]
311 fn test_escape_char() {
312 test_parse_one_arg("'\\n'", '\n');
313 }
314
315 #[test]
316 fn test_heart() {
317 test_parse_one_arg("'\\u{2764}'", '❤');
318 }
319
320 #[test]
321 fn test_hex() {
322 test_parse_one_arg("'\\x41'", 'A');
323 }
324
325 #[test]
326 fn test_easy_string() {
327 test_parse_one_arg("\"test\"", String::from("test"));
328 }
329
330 #[test]
331 fn test_complex_string() {
332 test_parse_one_arg(
333 "\" test \\n '\\u{1f980} is \\u{2764}' \\r \\\"täst\\\" \\x41\"",
334 String::from(" test \n '🦀 is ❤' \r \"täst\" A"),
335 );
336 }
337
338 #[test]
339 fn test_string_with_comma() {
340 test_parse_one_arg("\"1, 2\"", String::from("1, 2"));
341 }
342
343 #[test]
344 fn test_parse_five_args() {
345 let result: (u8, u16, u32, u64, u128) = parse_5_args("", "1, 2, 3, 4, 5").unwrap();
346 assert_eq!(result, (1, 2, 3, 4, 5));
347 }
348
349 #[test]
350 fn test_find_separator() {
351 assert_eq!(find_next_separator_index("\",\", \",\""), Some(3));
352 assert_eq!(find_next_separator_index("',', ','"), Some(3));
353 assert_eq!(find_next_separator_index("4, 5"), Some(1));
354 }
355
356 #[test]
357 fn test_too_many_args() {
358 assert_eq!(
359 parse_2_args::<u32, u32>("test", "1, 2, 3, 4").unwrap_err(),
360 InteractiveError::WrongNumberOfArguments {
361 method_name: "test",
362 expected: 2,
363 found: 4
364 }
365 )
366 }
367
368 #[test]
369 fn test_too_few_args() {
370 assert_eq!(
371 parse_2_args::<u32, u32>("test", "1").unwrap_err(),
372 InteractiveError::WrongNumberOfArguments {
373 method_name: "test",
374 expected: 2,
375 found: 1
376 }
377 )
378 }
379}