aopt/opt/
parser.rs

1use super::{ConstrctInfo, OptParser};
2use crate::opt::Index;
3use crate::Error;
4
5/// Parse the option string with given prefixes, return an [`ConstrctInfo`].
6///
7/// The struct of the option string are:
8///
9/// ```plaintext
10/// [--option][=][type][!*][@index]
11///      |     |    |   |   |
12///      |     |    |   |   |
13///      |     |    |   |   |
14///      |     |    |   |   The index part of option. Here are all the possible string:
15///      |     |    |   |   @0 means first position
16///      |     |    |   |   @-0 means last position
17///      |     |    |   |   @[1, 2, 3] means the position 1, 2 and 3
18///      |     |    |   |   @-[1, 2] means except the position 1, 2
19///      |     |    |   |   @>2 means position that bigger than 2
20///      |     |    |   |   @<3 means position less than 3
21///      |     |    |   |   @* means all the position
22///      |     |    |   |
23///      |     |    |   Indicate the option wether is force required(!) or not(*).
24///      |     |    |
25///      |     |    |
26///      |     |    |
27///      |     |    The type name of option.
28///      |     |    
29///      |     The delimiter of option name and type.
30///      |
31///      The option name part, it must be provide by user.
32/// ```
33///
34/// # Example
35///
36/// ```rust
37/// # use aopt::prelude::*;
38/// # use aopt::Error;
39/// #
40/// # fn main() -> Result<(), Error> {
41///     let parser = StrParser::default();
42///     let ret = parser.parse_opt("--aopt=t!".into())?;
43///
44///     assert_eq!(ret.name() , Some("--aopt"));
45///     assert_eq!(ret.ctor(), Some("t"));
46///     assert_eq!(ret.force(), Some(true));
47///     assert_eq!(ret.index(), None);
48///
49///     let ret = parser.parse_opt("bopt=t@[1,2,3]".into())?;
50///
51///     assert_eq!(ret.name(), Some("bopt"));
52///     assert_eq!(ret.ctor(), Some("t"));
53///     assert_eq!(ret.force(), None);
54///     assert_eq!(ret.index(), Some(&Index::list(vec![1, 2, 3])));
55///
56/// #   Ok(())
57/// # }
58/// ```
59///
60/// For more examples, please reference test case [`test_option_str_parser`](../../src/aopt/set/parser.rs.html#542).
61///
62#[derive(Debug, Default)]
63pub struct StrParser;
64
65impl StrParser {
66    pub fn new() -> Self {
67        Self {}
68    }
69
70    pub fn parse_creator_string(&self, dat: &str) -> Result<ConstrctInfo, Error> {
71        use neure::prelude::*;
72
73        let start = re::start();
74        let end = re::end();
75        let name = ['=', '!', '*', '@', ';', ':'].not().repeat_one_more();
76        let aliases = name.sep(";");
77        let parser = name.opt().if_then(";", aliases);
78
79        let ctor = neu::alphabetic().repeat_one_more();
80        let parser = parser.if_then("=", ctor);
81
82        let opt = "!".or("*").opt();
83        let parser = parser.then(opt);
84
85        let index = '@'.or(':').not().repeat_one_more();
86        let parser = parser.if_then("@", index);
87
88        let help = re::consume_all();
89        let parser = parser.if_then(":", help);
90
91        let parser = start.then(parser).then(end);
92
93        let to_string = |v: &str| v.trim().to_string();
94
95        let ((_, (((((name, aliases), ctor), opt), index), help)), _) = CharsCtx::new(dat)
96            .ctor(&parser)
97            .map_err(|_| Error::create_str(dat, "can not parsing string"))?;
98
99        let mut ci = ConstrctInfo::default();
100
101        ci = ci.with_name(name.map(to_string));
102        ci = ci.with_alias(aliases.map(|v| v.iter().copied().map(to_string).collect()));
103        ci = ci.with_ctor(ctor.map(to_string));
104        ci = ci.with_force(opt.map(|v| v == "!"));
105        ci = ci.with_index(if let Some(index) = index {
106            Some(Index::parse(index)?)
107        } else {
108            None
109        });
110        ci = ci.with_help(help.map(to_string));
111        Ok(ci)
112    }
113}
114
115impl OptParser for StrParser {
116    type Output = ConstrctInfo;
117
118    type Error = Error;
119
120    fn parse_opt(&self, pattern: &str) -> Result<Self::Output, Self::Error> {
121        if pattern.trim().is_empty() {
122            Ok(Self::Output::default())
123        } else {
124            self.parse_creator_string(pattern)
125        }
126    }
127}
128
129#[cfg(test)]
130mod test {
131
132    use crate::prelude::*;
133
134    #[test]
135    fn test_str_parser() {
136        let options = [
137            "-b",
138            "--bool",
139            "bool",
140            "-b;--bool",
141            "-?;-h;--help",
142            "--bool;-b",
143            "b;bool",
144            "-b;bool",
145            "-/b;--/bool",
146            "-/b;bool",
147            "-b=i",
148            "--bool=u",
149            "bool=s",
150            "-b;--bool=b",
151            "-?;-h;--help=p",
152            "--bool;-b=c",
153            "b;bool=m",
154            "-b;bool=f",
155            "-/b;--/bool=i",
156            "-/b;bool=a",
157            "",
158        ];
159        let options_test = [
160            (Some("-b"), None, None),
161            (Some("--bool"), None, None),
162            (Some("bool"), None, None),
163            (Some("-b"), Some(vec!["--bool"]), None),
164            (Some("-?"), Some(vec!["-h", "--help"]), None),
165            (Some("--bool"), Some(vec!["-b"]), None),
166            (Some("b"), Some(vec!["bool"]), None),
167            (Some("-b"), Some(vec!["bool"]), None),
168            (Some("-/b"), Some(vec!["--/bool"]), None),
169            (Some("-/b"), Some(vec!["bool"]), None),
170            (Some("-b"), None, Some("i")),
171            (Some("--bool"), None, Some("u")),
172            (Some("bool"), None, Some("s")),
173            (Some("-b"), Some(vec!["--bool"]), Some("b")),
174            (Some("-?"), Some(vec!["-h", "--help"]), Some("p")),
175            (Some("--bool"), Some(vec!["-b"]), Some("c")),
176            (Some("b"), Some(vec!["bool"]), Some("m")),
177            (Some("-b"), Some(vec!["bool"]), Some("f")),
178            (Some("-/b"), Some(vec!["--/bool"]), Some("i")),
179            (Some("-/b"), Some(vec!["bool"]), Some("a")),
180            (None, None, None),
181        ];
182        let helps = [": This is an option help message", ""];
183        let helps_test = [Some("This is an option help message"), None];
184        let forces = ["!", "*", ""];
185        let forces_test = [Some(true), Some(false), None];
186        let positions = [
187            "@1",
188            "@68",
189            "@-6",
190            "@+42",
191            "@1..5",
192            "@..8",
193            "@2..",
194            "@[1,3,5]",
195            "@+[2,3,4]",
196            "@-[3,56]",
197            "@*",
198            "",
199        ];
200        let positions_test = [
201            Some(Index::forward(1)),
202            Some(Index::forward(68)),
203            Some(Index::backward(6)),
204            Some(Index::forward(42)),
205            Some(Index::range(Some(1), Some(5))),
206            Some(Index::range(None, Some(8))),
207            Some(Index::range(Some(2), None)),
208            Some(Index::list(vec![1, 3, 5])),
209            Some(Index::list(vec![2, 3, 4])),
210            Some(Index::except(vec![3, 56])),
211            Some(Index::anywhere()),
212            None,
213        ];
214        let parser = StrParser;
215
216        for (option, option_test) in options.iter().zip(options_test.iter()) {
217            for (help, help_test) in helps.iter().zip(helps_test.iter()) {
218                for (force, force_test) in forces.iter().zip(forces_test.iter()) {
219                    for (position, position_test) in positions.iter().zip(positions_test.iter()) {
220                        let creator = format!("{}{}{}{}", option, force, position, help);
221
222                        println!("\"{}\",", creator);
223                        if let Ok(cap) = parser.parse_opt(&creator) {
224                            assert_eq!(option_test.0, cap.name());
225                            assert_eq!(
226                                option_test.1,
227                                cap.alias().map(|v| v.iter().map(|v| v.as_ref()).collect())
228                            );
229                            assert_eq!(help_test, &cap.help());
230                            assert_eq!(force_test, &cap.force());
231                            assert_eq!(position_test.as_ref(), cap.index());
232                            assert_eq!(option_test.2, cap.ctor());
233                        } else {
234                            assert!(
235                                option_test.0.is_none(),
236                                "{}{}{}{}",
237                                option,
238                                force,
239                                position,
240                                help
241                            );
242                            assert!(option_test.1.is_none());
243                        }
244                    }
245                }
246            }
247        }
248    }
249}