1use std::collections::HashMap;
2use std::slice::Iter;
3use std::str::FromStr;
4
5pub struct CommandLine<'a> {
6 commands: HashMap<&'a str, Command<'a>>,
7}
8
9impl<'a> CommandLine<'a> {
10 pub fn new() -> Self {
11 Self {
12 commands: HashMap::new(),
13 }
14 }
15
16 pub fn register(&mut self, command: Command<'a>) -> &mut Self {
17 let name_copy = command.name.clone();
18
19 self.commands.insert(command.name, command);
20 CommandLine::check_subcommands_ids(self.commands.get_mut(name_copy).unwrap(), false);
21
22 self
23 }
24
25 fn check_subcommands_ids(command: &'a mut Command, from_sub: bool) {
26 const PARENT_ID_DELIMITER: char = '.';
27
28 for sub in command.subcommands.iter_mut() {
29 if from_sub {
30 sub.id.insert_str(0, &command.id);
31 sub.id.insert(command.id.len(), PARENT_ID_DELIMITER);
32 }
33
34 if !sub.subcommands.is_empty() {
35 CommandLine::check_subcommands_ids(sub, true);
36 }
37 }
38 }
39
40 pub fn deregister(&mut self, command: Command<'a>) {
41 self.commands.remove(command.name);
42 }
43
44 pub fn lookup(&self, name: &str) -> Option<&Command<'a>> {
45 self.commands.get(name)
46 }
47
48 pub fn lookup_mut(&mut self, name: &str) -> Option<&mut Command<'a>> {
49 self.commands.get_mut(name)
50 }
51
52 pub fn run(&mut self, input: &str) -> ExecResult {
53 let parsed_syntax_res = parsing::parse(self, input);
54
55 if parsed_syntax_res.is_err() {
56 return ExecResult::Err(parsed_syntax_res.err().unwrap());
57 }
58
59 let parsed_syntax = parsed_syntax_res.ok().unwrap();
60
61 ExecResult::Ok {
62 subcommand: parsed_syntax.subcommand_id,
63 command: parsed_syntax.command_name,
64 parameters: parsed_syntax.parameters,
65 options: parsed_syntax.options,
66 }
67 }
68}
69
70pub enum ExecResult<'a> {
71 Err(ParseError),
72 Ok {
73 command: &'a str,
74 subcommand: &'a str,
75 parameters: Vec<Parameter>,
76 options: Vec<Opt>,
77 },
78}
79
80pub enum ParameterVal {
81 Text(String),
82 I32(i32),
83 U32(u32),
84 List(Vec<Parameter>),
85}
86
87pub struct Parameter {
88 pub val: ParameterVal,
89}
90
91impl std::fmt::Display for Parameter {
92 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
93 match &self.val {
94 ParameterVal::Text(s) => write!(f, "{}", s.as_str()),
95 ParameterVal::I32(n) => write!(f, "{}", n.to_string()),
96 ParameterVal::U32(n) => write!(f, "{}", n.to_string()),
97 ParameterVal::List(l) => {
98 let mut res = String::with_capacity(l.len() * 3);
99 let mut i = 1;
100 res.push('[');
101
102 for e in l {
103 res.push_str(&e.to_string());
104 i += 1;
105 if i != l.len() {
106 res.push(',');
107 res.push(' ');
108 }
109 }
110
111 res.push(']');
112 f.write_str(res.as_str())
113 }
114 }
115 }
116}
117
118#[derive(PartialEq, Clone)]
119pub enum ParameterKind {
120 Text,
121 I32,
122 U32,
123 List(Box<ParameterKind>),
124 None,
125}
126
127const PARAM_KIND_TEXT_STR: &str = "text";
128const PARAM_KIND_I32_STR: &str = "i32";
129const PARAM_KIND_U32_STR: &str = "u32";
130const PARAM_KIND_LIST_STR: &str = "list";
131const PARAM_KIND_NONE_STR: &str = "none";
132
133impl std::fmt::Display for ParameterKind {
134 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
135 let name = match self {
136 ParameterKind::Text => PARAM_KIND_TEXT_STR,
137 ParameterKind::I32 => PARAM_KIND_I32_STR,
138 ParameterKind::U32 => PARAM_KIND_U32_STR,
139 ParameterKind::List(_) => PARAM_KIND_LIST_STR,
140 ParameterKind::None => PARAM_KIND_NONE_STR,
141 };
142 f.write_str(name)
143 }
144}
145
146impl FromStr for ParameterKind {
147 type Err = ();
148
149 fn from_str(s: &str) -> Result<Self, Self::Err> {
150 match s {
151 PARAM_KIND_TEXT_STR => Ok(ParameterKind::Text),
152 PARAM_KIND_I32_STR => Ok(ParameterKind::I32),
153 PARAM_KIND_U32_STR => Ok(ParameterKind::U32),
154 _ => {
155 if s.starts_with(PARAM_KIND_LIST_STR) {
156 let mut raw_element_type = String::new();
157 let mut started_type = false;
158
159 for c in s.chars() {
160 match c {
161 '(' => started_type = true,
162 ')' => break,
163 _ => {
164 if started_type {
165 raw_element_type.push(c);
166 }
167 }
168 }
169 }
170
171 let element_type = ParameterKind::from_str(raw_element_type.as_str());
172
173 if element_type.is_ok() {
174 return Ok(ParameterKind::List(Box::new(element_type.unwrap())));
175 }
176 }
177
178 return Err(());
179 }
180 }
181 }
182}
183
184pub struct OptionData {
185 pub short_flags: Vec<char>,
186 pub long_flags: Vec<String>,
187 pub param_kind: ParameterKind,
188}
189
190pub struct Opt {
191 data: std::rc::Rc<OptionData>,
192 parameter: Option<Parameter>,
193}
194
195impl Opt {
196 pub fn parameter(&self) -> &Parameter {
197 self.parameter.as_ref().unwrap()
198 }
199}
200
201pub enum ParseError {
202 UnknownCommand,
203 MissingSubcommand,
204 MissingParameter(ParameterKind),
205 InvalidParameter(String),
206 UnnecessaryFlag(String),
207 UnnecessaryParameter(String),
208 InvalidSyntax(usize),
209}
210
211pub struct Command<'a> {
212 aliases: Vec<&'a str>,
213 id: String,
214 name: &'a str,
215 subcommands: Vec<Command<'a>>,
216 param_types: Vec<ParameterKind>,
218 options: Vec<std::rc::Rc<OptionData>>,
219}
220
221impl<'a> Command<'a> {
222 pub fn new(aliases: Vec<&'a str>) -> Self {
223 let tmp_aliases = aliases.clone();
224 let name = tmp_aliases
225 .get(0)
226 .expect("at least one alias must be specified");
227
228 if name.is_empty() {
229 panic!("at least one alias must be specified");
230 }
231
232 Self {
233 aliases,
234 name,
235 id: name.to_string(),
236 subcommands: Vec::new(),
237 param_types: Vec::new(),
238 options: Vec::new(),
239 }
240 }
241
242 pub fn add_subcommand(mut self, subcommand: Command<'a>) -> Self {
243 self.subcommands.push(subcommand);
244 self
245 }
246
247 pub fn set_syntax_format(mut self, format: &str) -> Self {
248 let parse_result = parsing::parse_params_and_options(format);
249
250 if let Err(e) = parse_result {
251 panic!(e);
252 }
253
254 let (mut param_types, mut options) = parse_result.unwrap();
255
256 self.param_types.append(&mut param_types);
257 self.options.append(&mut options);
258 self
259 }
260}
261
262pub trait ParamValueShortcut {
263 fn as_str(&self) -> &str;
264
265 fn as_i32(&self) -> i32;
266
267 fn as_u32(&self) -> u32;
268
269 fn as_multi(&self) -> &[Parameter];
270}
271
272impl ParamValueShortcut for Parameter {
273 fn as_str(&self) -> &str {
274 if let ParameterVal::Text(v) = &self.val {
275 return v.as_str();
276 } else {
277 panic!()
278 }
279 }
280
281 fn as_i32(&self) -> i32 {
282 if let ParameterVal::I32(v) = self.val {
283 return v;
284 } else {
285 panic!()
286 }
287 }
288
289 fn as_u32(&self) -> u32 {
290 if let ParameterVal::U32(v) = self.val {
291 return v;
292 } else {
293 panic!()
294 }
295 }
296
297 fn as_multi(&self) -> &[Parameter] {
298 if let ParameterVal::List(v) = &self.val {
299 return v;
300 } else {
301 panic!()
302 }
303 }
304}
305
306pub trait OptionAccessor {
307 fn by_short_flag(&self, flag: char) -> Option<&Opt>;
308
309 fn by_long_flag(&self, flag: &str) -> Option<&Opt>;
310}
311
312impl OptionAccessor for Vec<Opt> {
313 fn by_short_flag(&self, flag: char) -> Option<&Opt> {
314 self.iter().find(|f| f.data.short_flags.contains(&flag))
315 }
316
317 fn by_long_flag(&self, flag: &str) -> Option<&Opt> {
318 self.iter().find(|f| f.data.long_flags.contains(&flag.to_owned()))
319 }
320}
321
322pub trait ParamAccessor {
323 fn poll(&mut self) -> &Parameter;
324
325 fn poll_str(&mut self) -> &str;
326
327 fn poll_i32(&mut self) -> i32;
328
329 fn poll_multi(&mut self) -> &[Parameter];
330
331 fn poll_multi_str(&mut self) -> Vec<&str>;
332
333 fn poll_multi_i32(&mut self) -> Vec<i32>;
334}
335
336impl<'a> ParamAccessor for Iter<'a, Parameter> {
337 fn poll(&mut self) -> &Parameter {
338 self.next().unwrap()
339 }
340
341 fn poll_str(&mut self) -> &str {
342 self.poll().as_str()
343 }
344
345 fn poll_i32(&mut self) -> i32 {
346 self.poll().as_i32()
347 }
348
349 fn poll_multi(&mut self) -> &[Parameter] {
350 self.poll().as_multi()
351 }
352
353 fn poll_multi_str(&mut self) -> Vec<&str> {
354 self.poll_multi().iter().map(|p| p.as_str()).collect()
355 }
356
357 fn poll_multi_i32(&mut self) -> Vec<i32> {
358 self.poll_multi().iter().map(|p| p.as_i32()).collect()
359 }
360}
361
362mod parsing {
363 use super::*;
364
365 pub struct SyntaxTree<'a> {
366 pub command_name: &'a str,
367 pub subcommand_id: &'a str, pub parameters: Vec<Parameter>,
369 pub options: Vec<Opt>,
370 }
371
372 pub fn parse<'b, 'a: 'b>(
373 command_line: &'b mut CommandLine<'a>,
374 input: &str,
375 ) -> Result<SyntaxTree<'b>, ParseError> {
376 let param_parts = parse_input_parts(input);
377 let mut param_parts = param_parts.into_iter();
378 let command_name_res = param_parts.next();
379
380 if command_name_res.is_none() {
381 return Err(ParseError::InvalidSyntax(0));
382 }
383
384 let command_name = command_name_res.unwrap();
385 let command_res = command_line.lookup_mut(command_name.as_str());
386
387 if command_res.is_none() {
388 return Err(ParseError::UnknownCommand);
389 }
390
391 let command = command_res.unwrap();
392 let mut peekable_parts = param_parts.peekable();
393 let (mut selected_subcommand_id, mut param_types, mut options_data) =
394 ("", &command.param_types, &command.options);
395
396 if !command.subcommands.is_empty() {
397 let selected_subcommand_lookup_res =
398 command.subcommands.iter().try_fold(Option::None, |_, cmd| {
399 let mut tmp_peekable_parts = peekable_parts.clone();
400 let possible_alias_res = tmp_peekable_parts.next();
401
402 if possible_alias_res.is_none() {
403 return Err(Option::None);
404 }
405
406 let possible_alias = possible_alias_res.unwrap();
407
408 for alias in cmd.aliases.iter() {
409 if alias == &possible_alias {
410 if cmd.subcommands.is_empty() {
411 peekable_parts = tmp_peekable_parts;
412
413 return Err(Option::Some((cmd.id.as_str(), cmd)));
414 } else {
415 let (found_match, res) =
416 check_subcommands(&mut tmp_peekable_parts, cmd);
417
418 if found_match {
419 peekable_parts = tmp_peekable_parts;
420 }
421
422 return res;
423 }
424 }
425 }
426
427 return Ok(Option::None);
428 });
429
430 let selected_subcommand_pair_res = if selected_subcommand_lookup_res.is_ok() {
431 selected_subcommand_lookup_res.ok().unwrap()
432 } else {
433 selected_subcommand_lookup_res.err().unwrap()
434 };
435
436 if let Some((id, cmd)) = selected_subcommand_pair_res {
437 selected_subcommand_id = id;
438 param_types = &cmd.param_types;
439 options_data = &cmd.options;
440 } else {
441 return Result::Err(ParseError::MissingSubcommand);
442 }
443 }
444
445 let mut parameters: Vec<Parameter> = Vec::new();
446 let mut options: Vec<Opt> = Vec::new();
447 let mut pending_opt: Option<std::rc::Rc<OptionData>> = Option::None;
448 let mut param_idx = 0;
449
450 'parts_loop: while let Some(part) = peekable_parts.next() {
451 if pending_opt.is_some() {
452 let opt = pending_opt.unwrap();
453 let result = parse_param(part.to_owned(), &opt.param_kind);
454
455 if result.is_none() {
456 return Err(ParseError::InvalidParameter(part.to_owned()));
457 }
458
459 options.push(Opt {
460 data: opt,
461 parameter: Option::Some(result.unwrap()),
462 });
463
464 pending_opt = Option::None;
465
466 continue;
467 }
468
469 let mut flag_type = FlagType::None;
470
471 for (c_i, c) in part.chars().enumerate() {
472 if c == '-' {
473 if c_i == 0 {
474 flag_type = FlagType::Short;
475
476 continue;
477 }
478
479 if flag_type == FlagType::Short && c_i == 1 {
480 flag_type = FlagType::Long;
481
482 break;
483 }
484 }
485
486 if flag_type == FlagType::Short {
487 let found_option = options_data
488 .iter()
489 .find(|o| o.short_flags.contains(&c))
490 .cloned();
491
492 if found_option.is_none() {
493 return Err(ParseError::UnnecessaryFlag(c.to_string())); }
495
496 let found_option = found_option.unwrap();
497
498 if found_option.param_kind == ParameterKind::None {
499 options.push(Opt {
500 data: found_option,
501 parameter: Option::None,
502 });
503 } else {
504 let raw_param = part.get(c_i + 1..).unwrap();
505
506 if raw_param.is_empty() {
507 pending_opt = Option::Some(found_option);
508 } else {
509 let result = parse_param(raw_param.to_owned(), &found_option.param_kind);
510
511 if result.is_none() {
512 return Err(ParseError::InvalidParameter(raw_param.to_owned()));
513 }
514
515 options.push(Opt {
516 data: found_option,
517 parameter: Option::Some(result.unwrap()),
518 });
519 }
520
521 continue 'parts_loop;
522 }
523 }
524 }
525
526 if flag_type == FlagType::Long {
527 let mut part = part.clone();
528 part.remove(0);
529 part.remove(0);
530 let found_option = options_data
531 .iter()
532 .find(|o| o.long_flags.contains(&part))
533 .cloned();
534
535 if found_option.is_none() {
536 return Err(ParseError::UnnecessaryFlag(part));
537 }
538
539 let found_option = found_option.unwrap();
540
541 if found_option.param_kind == ParameterKind::None {
542 options.push(Opt {
543 data: found_option,
544 parameter: Option::None,
545 });
546 } else {
547 pending_opt = Option::Some(found_option);
548 }
549
550 continue;
551 }
552
553 let param_type_res = param_types.get(param_idx);
554
555 if param_type_res.is_none() {
556 return Err(ParseError::UnnecessaryParameter(part.to_string()));
557 }
558
559 let param_type = param_type_res.unwrap();
560 let result = parse_param(part.clone(), ¶m_type);
561
562 if result.is_none() {
563 return Err(ParseError::InvalidParameter(part.to_owned()));
564 }
565
566 parameters.push(result.unwrap());
567 param_idx += 1;
568 }
569
570 if pending_opt.is_some() {
571 let opt = pending_opt.unwrap();
572 return Err(ParseError::MissingParameter(opt.param_kind.clone()));
573 }
574
575 return Ok(SyntaxTree {
576 command_name: command.name,
577 subcommand_id: selected_subcommand_id,
578 parameters,
579 options,
580 });
581 }
582
583 fn check_subcommands<'a>(
584 input_parts: &mut std::iter::Peekable<std::vec::IntoIter<String>>,
585 cmd: &'a Command<'a>,
586 ) -> (
587 bool,
588 Result<Option<(&'a str, &'a Command<'a>)>, Option<(&'a str, &'a Command<'a>)>>,
589 ) {
590 if let Some(possible_alias) = input_parts.next() {
591 for sub_sub in cmd.subcommands.iter() {
592 for alias in sub_sub.aliases.iter() {
593 if alias == &possible_alias {
594 return (true, Result::Err(Option::Some((&sub_sub.id, &sub_sub))));
595 }
596 }
597
598 let sub_sub_subcmds_result = check_subcommands(input_parts, &sub_sub);
599
600 if let Err(res) = sub_sub_subcmds_result.1 {
601 if res.is_some() {
602 return sub_sub_subcmds_result;
603 }
604 }
605 }
606 } else {
607 return (false, Result::Err(Option::None));
608 }
609
610 return (false, Result::Ok(Option::None));
611 }
612
613 #[derive(PartialEq)]
614 enum FlagType {
615 None,
616 Short,
617 Long,
618 }
619
620 #[derive(PartialEq)]
621 enum OptionParseStatus {
622 Ready,
623 Waiting,
624 NeedsParameter,
625 }
626
627 pub fn parse_params_and_options(
628 format: &str,
629 ) -> Result<(Vec<ParameterKind>, Vec<std::rc::Rc<OptionData>>), String> {
630 let mut parameter_kinds = Vec::new();
631 let mut options = Vec::new();
632 let mut pending_kind = ParameterKind::None;
633 let mut parse_opts = false;
634 let mut waiting_opt_desc = false;
635 let mut opt_status = OptionParseStatus::Ready;
636 let mut opt_flag_builder = String::new();
637 let mut opt_short_flags = Vec::new();
638 let mut opt_long_flags = Vec::new();
639
640 for c in format.chars() {
641 if c.is_whitespace() {
642 continue;
643 }
644
645 let mut ignore_curr = false;
646
647 if pending_kind != ParameterKind::None {
648 if is_list_param(c) {
649 pending_kind = ParameterKind::List(Box::new(pending_kind));
650 ignore_curr = true;
651 }
652
653 if opt_status == OptionParseStatus::NeedsParameter {
654 options.push(std::rc::Rc::new(OptionData {
655 long_flags: opt_long_flags.clone(),
656 short_flags: opt_short_flags.clone(),
657 param_kind: pending_kind.clone(),
658 }));
659
660 opt_long_flags.clear();
661 opt_short_flags.clear();
662 opt_status = OptionParseStatus::Ready;
663 } else {
664 parameter_kinds.push(pending_kind);
665 }
666
667 pending_kind = ParameterKind::None;
668 }
669
670 if c == '-' {
671 parse_opts = true;
672
673 continue;
674 }
675
676 if parse_opts {
677 if !waiting_opt_desc {
678 if c == '(' {
679 if opt_status == OptionParseStatus::Waiting {
680 options.push(std::rc::Rc::new(OptionData {
681 long_flags: opt_long_flags.clone(),
682 short_flags: opt_short_flags.clone(),
683 param_kind: pending_kind.clone(),
684 }));
685
686 opt_long_flags.clear();
687 opt_short_flags.clear();
688 }
689
690 waiting_opt_desc = true;
691
692 continue;
693 } else {
694 if opt_status == OptionParseStatus::Waiting {
695 let kind = get_param_kind(c);
696
697 if kind == ParameterKind::None {
698 return Result::Err(
699 "unexpected character after option".to_owned(),
700 );
701 } else {
702 pending_kind = kind;
703 opt_status = OptionParseStatus::NeedsParameter;
704 }
705
706 continue;
707 }
708
709 return Result::Err(format!("unexpected character: {}", c));
710 }
711 } else {
712 if c == ',' {
713 let opt_flag_len = opt_flag_builder.len();
714
715 if opt_flag_len > 1 {
716 opt_long_flags.push(opt_flag_builder.to_owned());
717 } else if opt_flag_len == 1 {
718 opt_short_flags.push(opt_flag_builder.chars().next().unwrap());
719 }
720
721 opt_flag_builder.clear();
722 } else if c == ')' {
723 let opt_flag_len = opt_flag_builder.len();
724
725 if opt_flag_len > 1 {
726 opt_long_flags.push(opt_flag_builder.to_owned());
727 } else if opt_flag_len == 1 {
728 opt_short_flags.push(opt_flag_builder.chars().next().unwrap());
729 }
730
731 opt_flag_builder.clear();
732 waiting_opt_desc = false;
733 opt_status = OptionParseStatus::Waiting;
734 } else {
735 opt_flag_builder.push(c);
736 }
737 }
738 } else if !ignore_curr {
739 let kind = get_param_kind(c);
740
741 if kind == ParameterKind::None {
742 return Result::Err(format!("invalid parameter kind: {}", c));
743 } else {
744 pending_kind = kind;
745 }
746 }
747 }
748
749 if !opt_flag_builder.is_empty() {
750 let opt_flag_len = opt_flag_builder.len();
751
752 if opt_flag_len > 1 {
753 opt_long_flags.push(opt_flag_builder.to_owned());
754 } else if opt_flag_len == 1 {
755 opt_short_flags.push(opt_flag_builder.chars().next().unwrap());
756 }
757 }
758
759 if opt_status != OptionParseStatus::Ready {
760 options.push(std::rc::Rc::new(OptionData {
761 long_flags: opt_long_flags,
762 short_flags: opt_short_flags,
763 param_kind: pending_kind.clone(),
764 }));
765 } else if pending_kind != ParameterKind::None {
766 parameter_kinds.push(pending_kind);
767 }
768
769 return Result::Ok((parameter_kinds, options));
770 }
771
772 fn is_list_param(c: char) -> bool {
773 c == '[' || c == '{'
774 }
775
776 fn get_param_kind(c: char) -> ParameterKind {
777 match c {
778 's' => ParameterKind::Text,
779 'i' => ParameterKind::I32,
780 'u' => ParameterKind::U32,
781 _ => ParameterKind::None,
782 }
783 }
784
785 fn parse_input_parts(input: &str) -> Vec<String> {
786 let mut param_parts = Vec::new();
787 let mut curr_part = String::new();
788 let mut is_in_compound_param = false;
789 let mut was_in_compound_param = false;
790 let mut last_char_was_backslash = false;
791
792 for (i, c) in input.chars().enumerate() {
793 if last_char_was_backslash {
794 if c == '\\' {
795 curr_part.push('\\');
796 } else {
797 curr_part.push(c);
798 }
799
800 last_char_was_backslash = false;
801 } else {
802 match c {
803 '[' => {
804 curr_part.push(c);
805 is_in_compound_param = true;
806 }
807 ']' => {
808 curr_part.push(c);
809
810 if is_in_compound_param {
811 param_parts.push(curr_part.to_owned());
812 curr_part.clear();
813 is_in_compound_param = false;
814 }
815 }
816 '"' => {
817 is_in_compound_param = !is_in_compound_param;
818
819 if !is_in_compound_param {
820 param_parts.push(curr_part.to_owned());
821 curr_part.clear();
822 was_in_compound_param = true;
823 }
824 }
825 ' ' => {
826 if is_in_compound_param {
827 curr_part.push(c);
828 } else if !was_in_compound_param {
829 param_parts.push(curr_part.to_owned());
830 curr_part.clear();
831 } else {
832 was_in_compound_param = false;
833 }
834 }
835 '\\' => {
836 last_char_was_backslash = true;
837 }
838 _ => {
839 curr_part.push(c);
840 }
841 }
842 }
843
844 if i == input.len() - 1 && !curr_part.is_empty() {
845 param_parts.push(curr_part.to_owned());
846
847 break;
848 }
849 }
850
851 param_parts
852 }
853
854 fn parse_param(raw_param: String, param_type: &ParameterKind) -> Option<Parameter> {
855 match param_type {
856 ParameterKind::Text => {
857 return Some(Parameter {
858 val: ParameterVal::Text(raw_param),
859 });
860 }
861 ParameterKind::I32 => {
862 let num_parse_res = raw_param.parse::<i32>();
863
864 if num_parse_res.is_err() {
865 return None;
866 }
867
868 return Some(Parameter {
869 val: ParameterVal::I32(num_parse_res.unwrap()),
870 });
871 }
872 ParameterKind::U32 => {
873 let num_parse_res = raw_param.parse::<u32>();
874
875 if num_parse_res.is_err() {
876 return None;
877 }
878
879 return Some(Parameter {
880 val: ParameterVal::U32(num_parse_res.unwrap()),
881 });
882 }
883 ParameterKind::List(t) => {
884 const ELEMENT_DELIMITER: char = ',';
885
886 let mut chars = raw_param.chars();
887 let open_char = chars.next().unwrap();
888
889 if open_char != '{' && open_char != '[' {
890 return None;
891 }
892
893 let close_char = chars.next_back().unwrap();
894
895 if close_char != '}' && close_char != ']' {
896 return None;
897 }
898
899 let raw_param = chars.as_str();
900 let raw_elements: Vec<&str> = raw_param.split(ELEMENT_DELIMITER).collect();
901 let mut elements: Vec<Parameter> = Vec::new();
902
903 for e in raw_elements {
904 let e = e.trim();
905 let res = parse_param(e.to_string(), t);
906
907 if res.is_none() {
908 return None;
909 }
910
911 elements.push(res.unwrap());
912 }
913
914 return Some(Parameter {
915 val: ParameterVal::List(elements),
916 });
917 }
918 _ => {}
919 };
920
921 return None;
922 }
923}