commit/
lib.rs

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    //branches_need_sort: bool,
217    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, // Option<&'a str>,
368        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())); //TODO: ignore?
494                    }
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(), &param_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}