Skip to main content

cmdparsing/
lib.rs

1#[macro_export]
2macro_rules! helper {
3    (exists_or_zero;) => {
4        1
5    };
6    (exists_or_zero;$t:tt) => {
7        $t
8    };
9    (exists;,$then:ty) => {
10        $then
11    };
12    (exists;$t:tt, $then:ty) => {
13        Vec<$then>
14    };
15    (exists_flag;,$then:ty) => {
16        Option<$then>
17    };
18    (exists_flag;$t:tt, $then:ty) => {
19        Vec<$then>
20    };
21    (exists_add;,$var:ident, $type:ty, $d:ident, $db:ident) => {
22        $var = <$type>::from($d.get($db).expect("this is an error on line 22, please report this bug to https://github.com/chickencuber/clilib").clone());
23        $d.remove($db);
24    };
25    (exists_add;$t:tt,$var:ident, $type:ty, $d:ident, $db:ident) => {
26        for _ in 0..$t {
27            $var.push(<$type>::from($d.get($db).expect("this is an error on line 27, please report this bug to https://github.com/chickencuber/clilib").clone()));
28            $d.remove($db);
29        }
30    };
31    (exists_add_flag;,$var:ident, $type:ty, $d:ident, $db:expr) => {
32        $var = Some(<$type>::from($d.get($db).expect("this is an error on line 32, please report this bug to https://github.com/chickencuber/clilib").clone()));
33        $d.remove($db);
34    };
35    (exists_add_flag;$t:tt,$var:ident, $type:ty, $d:ident, $db:expr) => {
36        for _ in 0..$t {
37            $var.push(<$type>::from($d.get($db).expect("this is an error on line 37, please report this bug to https://github.com/chickencuber/clilib").clone()));
38            $d.remove($db);
39        }
40    };
41    (exists_declare;,$var:ident, $type:ty) => {
42        let $var: $type;
43    };
44    (exists_declare;$t:tt,$var:ident, $type:ty) => {
45        let mut $var: Vec<$type> = Vec::new();
46    };
47    (exists_declare_flag;,$var:ident, $type:ty) => {
48        let mut $var: Option<$type> = None;
49    };
50    (exists_declare_flag;$t:tt,$var:ident, $type:ty) => {
51        let mut $var: Vec<$type> = Vec::new();
52    };
53}
54
55#[macro_export]
56macro_rules! cmd {
57    ($name:ident; help:$help:expr; :$default:expr; $($fname:expr=> $($str:literal)|*),*$(,)?) => {
58        pub struct $name;
59        impl $name {
60            pub fn run(v: Vec<String>) {
61                if v.len() == 0 {
62                    $default(v);
63                    return;
64                }
65                let cmd = v.get(0).expect(&format!("{}", $help)).clone();
66                if cmd == "help" {
67                    println!("{}", $help);
68                    std::process::exit(0);
69                } else
70                    $(
71                        if $(cmd == $str)||* {
72                            $fname(v);
73                        } else
74                    )* {
75                        $default(v);
76                    }
77            }
78        }
79    };
80    ($name:ident; help:$help:expr; .$cmd:ty; :$default:expr; $($fname:expr=> $($str:literal)|*),*$(,)?) => {
81        pub struct $name;
82        impl $name {
83            pub fn run(mut v: Vec<String>) {
84                let mut cmd: Option<String> = None;
85                let mut allow_flags = false;
86
87                for i in 0..v.len() {
88                    let a = &v[i];
89                    if allow_flags {
90                        cmd = Some(a.clone());
91                        v.remove(i);
92                        break;
93                    }
94                    if a == "--" {
95                        allow_flags = true;
96                        continue;
97                    }
98                    if a.starts_with("-") {
99                        continue;
100                    }
101                    cmd = Some(a.clone());
102                    v.remove(i);
103                    break;
104                }
105                if let Some(cmd) = cmd {
106                    if cmd == "help" {
107                        println!("{}", $help);
108                        std::process::exit(0);
109                    } else
110                        $(
111                            if $(cmd == $str)||* {
112                                let args = <$cmd>::from(v.clone());
113                                $fname(args);
114                            } else
115                        )* {
116                            let args = <$cmd>::from(v.clone());
117                            $default(args);
118                            }
119                } else {
120                    let args = <$cmd>::from(v.clone());
121                    $default(args);
122                }
123            }
124        }
125    };
126    ($name:ident; help:$help:expr; $(.$cmd:ty;)? $($fname:expr=> $($str:literal)|*),*$(,)?) => {
127        fn _default<T>(_: T) {
128            eprintln!("{}", $help);
129            std::process::exit(101);
130        }
131        $crate::cmd!{
132            $name:ident;
133            help:$help;
134            $(.$cmd;)?
135            :_default;
136            $($fname => $($str)|*),*
137        }
138    };
139    (help:$help:expr; $(.$cmd:ty;)? $(:$default:expr;)? $($fname:expr=> $($str:literal)|*),*$(,)?) => {
140        fn main() {
141            $crate::cmd!{
142                _Main;
143                help:$help;
144                     $(.$cmd;)?
145                         $(:$default;)?
146                         $($fname => $($str)|*),*
147            }
148            _Main;
149            _Main::run(std::env::args().skip(1).collect());
150        }
151    };
152}
153
154#[macro_export]
155macro_rules! define {
156    ($name:ident; help: $help:expr; flags {
157        $($fname:ident: $ftype:ty = $($flag:literal)|* $(=> [$fnum:literal])?),*$(,)?
158    }; args {
159        $($aname:ident: $atype:ty $(=> [$num:literal])?),*$(,)?
160    };
161    $(rest => $rname:ident: $rtype:ty;)?) => {
162        #[derive(Debug, Clone)]
163        pub struct $name {
164            $(pub $fname: tt_call::tt_if!{
165                condition = [{tt_equal::tt_equal}]
166                    input = [{ $ftype bool }]
167                    true = [{
168                        bool
169                    }]
170                false = [{
171                    $crate::helper!(exists_flag;$($fnum)?, $ftype)
172                }]
173            },)*
174            $(
175                pub $aname: $crate::helper!(exists;$($num)?, $atype),
176            )*
177                $(
178                    pub $rname: Vec<$rtype>
179                )?
180        }
181        impl $name {
182            pub fn from(mut __args: Vec<String>) -> Self{
183                let mut __handle_flags = true;
184                $(
185                    tt_call::tt_if!{
186                        condition = [{tt_equal::tt_equal}]
187                            input = [{ $ftype bool }]
188                            true = [{
189                                let mut $fname: bool = false;
190                            }]
191                        false = [{
192                            $crate::helper!(exists_declare_flag;$($fnum)?, $fname, $ftype);
193                        }]
194                    }
195                )*
196                    $(
197                        $crate::helper!(exists_declare;$($num)?,$aname, $atype);
198                    )*
199                    $(
200                        let mut $rname: Vec<$rtype> = Vec::new();
201                    )?
202                    if __args.contains(&"-help".to_string()) || __args.contains(&"--help".to_string()) {
203                        eprintln!("{}", $help);
204                        std::process::exit(0);
205                    }
206                let mut __i = 0;
207                $(
208                    if __args.len() == 0 {
209                        eprintln!("missing argument: '{}'", stringify!($aname));
210                        std::process::exit(101);
211                    }
212                    while __args.get(__i).expect("there is an error on line 146, please report this bug to https://github.com/chickencuber/clilib").starts_with("-") && __handle_flags {
213                        let v = Self::has_args(__args.get(__i).expect("there is an error on line 147, please report this bug to https://github.com/chickencuber/clilib").clone());
214                        __args.get_mut(__i).expect("there is an error on line 148, please report this bug to https://github.com/chickencuber/clilib").insert(0, '-');
215                        if v.0 {
216                            __i += v.1;
217                            if __args.get(__i).expect("there is an error on line 151, please report this bug to https://github.com/chickencuber/clilib").starts_with("-") && __handle_flags {
218                                eprintln!("the flag requires an argument");
219                                std::process::exit(101);
220                            }
221                        }
222                        __i += 1;
223                    }
224
225                    $crate::helper!(exists_add; $($num)?, $aname, $atype, __args, __i);
226                )*
227                    $(
228                        while __args.len() - __i > 0 {
229                            if __args.get(__i).expect("there is an error on line 163, please report this bug to https://github.com/chickencuber/clilib").starts_with("-") && __handle_flags {
230                                if $rname.len() > 0 {
231                                    break;
232                                }
233                                while __args.get(__i).unwrap_or(&String::new()).starts_with("-") && __handle_flags {
234                                    let v = Self::has_args(__args.get(__i).expect("there is an error on line 168, please report this bug to https://github.com/chickencuber/clilib").clone());
235                                    if __args[__i] == "--" {
236                                        __handle_flags = false;
237                                        __args.remove(__i);
238                                        break;
239                                    }
240                                    __args.get_mut(__i).expect("there is an error on line 169, please report this bug to https://github.com/chickencuber/clilib").insert(0, '-');
241                                    if v.0 {
242                                        __i += v.1;
243                                        if __args.get(__i).unwrap_or(&String::from("-")).starts_with("-") && __handle_flags {
244                                            eprintln!("the flag requires an argument");
245                                            std::process::exit(101);
246                                        }
247                                    }
248                                    __i += 1;
249                                }
250                            }
251
252                            if let Some(s) = __args.get(__i) {
253                                $rname.push(<$rtype>::from(s.clone()));
254                                __args.remove(__i);
255                            }
256
257                        }
258                )?
259                    while __args.len() > 0 {
260                        let mut __ch = __args.get(0).expect("there is an error on line 186, please report this bug to https://github.com/chickencuber/clilib").clone();
261                        if !(__ch.starts_with("-") && __handle_flags) {
262                            eprintln!("too many arguments");
263                            std::process::exit(101);
264                        }
265                        __ch.remove(0);
266                        if __ch.starts_with("-") && __handle_flags {
267                            __ch.remove(0);
268                        }
269                        $(
270                            if $(__ch == $flag)||* {
271                                tt_call::tt_if!{
272                                    condition = [{tt_equal::tt_equal}]
273                                        input = [{ $ftype bool }]
274                                        true = [{
275                                            $fname = true;
276                                        }]
277                                    false = [{
278                                        if __args[1].starts_with("-") && __handle_flags {
279                                            eprintln!("flags requires an argument");
280                                            std::process::exit(101);
281                                        }
282                                        $crate::helper!(exists_add_flag;$($fnum)?, $fname, $ftype, __args, 1);
283                                    }]
284                                }
285                                __args.remove(0);
286                                continue;
287                            } else
288                        )*
289                            if (__ch == "-") {
290                                __handle_flags = false;
291                                __args.remove(0);
292                                break
293                            }
294                            eprintln!("invalid flag {}", __ch);
295                        std::process::exit(101);
296                    }
297                return Self {
298                    $($fname,)*
299                        $($aname,)*
300                        $($rname)?
301                }
302            }
303            fn has_args(v: String) -> (bool, usize) {
304                $(
305                    if $(v == concat!("-", $flag) || v == concat!("--", $flag))||* {
306                        tt_call::tt_if!{
307                            condition = [{tt_equal::tt_equal}]
308                                input = [{ $ftype bool }]
309                                true = [{
310                                    return (false, 0);
311                                }]
312                            false = [{
313                                return (true, $crate::helper!(exists_or_zero;$($fnum)?));
314                            }]
315                        }
316                    } else
317                )*
318                    if(v == "--") {
319                        return (false, 0);
320                    }
321                eprintln!("invalid flag {}", v);
322                std::process::exit(101);
323            }
324        }
325    };
326}