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").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").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").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").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; $($fname:ident => $($str:literal)|*),*$(,)?) => {
58        struct $name;   
59        impl $name {
60            pub fn run(v: Vec<String>) {
61                let cmd = v.get(0).expect($help).clone();
62                if cmd == "help" {
63                    println!($help);
64                    std::process::exit(0);
65                } else 
66                    $(
67                        if $(cmd == $str)||* {
68                            $fname(v);
69                        } else
70                    )* {
71                        eprintln!($help);
72                        std::process::exit(101);
73                    }
74            }
75        }
76    };
77    (help:$help:expr; $($fname:ident => $($str:literal)|*),*$(,)?) => {
78        fn main() {
79            _Main::run(std::env::args().skip(1).collect());
80        }
81        $crate::cmd!{
82            _Main; 
83            help:$help;
84                 $($fname => $($str)|*),*
85        }
86    };
87}
88
89#[macro_export]
90macro_rules! define {
91    ($name:ident; help: $help:expr; flags {
92        $($fname:ident: $ftype:ty = $($flag:literal)|* $(=> [$fnum:literal])?),*$(,)?
93    }; args {
94        $($aname:ident: $atype:ty $(=> [$num:literal])?),*$(,)? 
95    };
96    $(rest => $rname:ident: $rtype:ty;)?) => {
97        #[derive(Debug, Clone)]
98        struct $name {
99            $($fname: tt_call::tt_if!{
100                condition = [{tt_equal::tt_equal}]
101                    input = [{ $ftype bool }]
102                    true = [{
103                        bool
104                    }]
105                false = [{
106                    $crate::helper!(exists_flag;$($fnum)?, $ftype) 
107                }]
108            },)*
109            $(
110                $aname: $crate::helper!(exists;$($num)?, $atype),
111            )*
112                $(
113                    $rname: Vec<$rtype>
114                )?
115        } 
116        impl $name {
117            pub fn from(mut __args: Vec<String>) -> Self{
118                let mut __handle_flags = true;
119                $(
120                    tt_call::tt_if!{
121                        condition = [{tt_equal::tt_equal}]
122                            input = [{ $ftype bool }]
123                            true = [{
124                                let mut $fname: bool = false;
125                            }]
126                        false = [{
127                            $crate::helper!(exists_declare_flag;$($fnum)?, $fname, $ftype);
128                        }]
129                    }
130                )*
131                    $(
132                        $crate::helper!(exists_declare;$($num)?,$aname, $atype);
133                    )*
134                    $(
135                        let mut $rname: Vec<$rtype> = Vec::new();
136                    )?
137                    if __args.contains(&"-help".to_string()) || __args.contains(&"--help".to_string()) {
138                        eprintln!($help);
139                        std::process::exit(0);
140                    }
141                let mut __i = 0;
142                $(
143                    if __args.len() == 0 {
144                        eprintln!("missing argument: '{}'", stringify!($aname));
145                        std::process::exit(101);
146                    }
147                    while __args.get(__i).expect("there is an error on line 146, please report this bug").starts_with("-") && __handle_flags {
148                        let v = Self::has_args(__args.get(__i).expect("there is an error on line 147, please report this bug").clone());
149                        __args.get_mut(__i).expect("there is an error on line 148, please report this bug").insert(0, '-');
150                        if v.0 {
151                            __i += v.1;
152                            if __args.get(__i).expect("there is an error on line 151, please report this bug").starts_with("-") && __handle_flags {
153                                eprintln!("the flag requires an argument");
154                                std::process::exit(101);
155                            }
156                        }
157                        __i += 1;
158                    }
159
160                    $crate::helper!(exists_add; $($num)?, $aname, $atype, __args, __i);
161                )*
162                    $(
163                        while __args.len() - __i > 0 {
164                            if __args.get(__i).expect("there is an error on line 163, please report this bug").starts_with("-") && __handle_flags {
165                                if $rname.len() > 0 {
166                                    break;
167                                }
168                                while __args.get(__i).unwrap_or(&String::new()).starts_with("-") && __handle_flags {
169                                    let v = Self::has_args(__args.get(__i).expect("there is an error on line 168, please report this bug").clone());
170                                    if __args[__i] == "--" {
171                                        __handle_flags = false;
172                                        __args.remove(__i);
173                                        break;
174                                    } 
175                                    __args.get_mut(__i).expect("there is an error on line 169, please report this bug").insert(0, '-');
176                                    if v.0 {
177                                        __i += v.1;
178                                        if __args.get(__i).unwrap_or(&String::from("-")).starts_with("-") && __handle_flags {
179                                            eprintln!("the flag requires an argument");
180                                            std::process::exit(101);
181                                        }
182                                    }
183                                    __i += 1;
184                                }
185                            }
186
187                            if let Some(s) = __args.get(__i) {
188                                $rname.push(<$rtype>::from(s.clone()));
189                                __args.remove(__i);
190                            }
191
192                        }
193                )?
194                    while __args.len() > 0 {
195                        let mut __ch = __args.get(0).expect("there is an error on line 186, please report this bug").clone();
196                        if !(__ch.starts_with("-") && __handle_flags) {
197                            eprintln!("too many arguments");
198                            std::process::exit(101);
199                        }
200                        __ch.remove(0);
201                        if __ch.starts_with("-") && __handle_flags {
202                            __ch.remove(0);
203                        }
204                        $(
205                            if $(__ch == $flag)||* {
206                                tt_call::tt_if!{
207                                    condition = [{tt_equal::tt_equal}]
208                                        input = [{ $ftype bool }]
209                                        true = [{
210                                            $fname = true;
211                                        }]
212                                    false = [{
213                                        if __args[1].starts_with("-") && __handle_flags {
214                                            eprintln!("flags requires an argument");
215                                            std::process::exit(101);
216                                        }
217                                        $crate::helper!(exists_add_flag;$($fnum)?, $fname, $ftype, __args, 1);
218                                    }]
219                                }   
220                                __args.remove(0);
221                                continue;
222                            } else
223                        )*
224                            if (__ch == "-") {
225                                __handle_flags = false;
226                                __args.remove(0);
227                                break
228                            }
229                            eprintln!("invalid flag {}", __ch);
230                        std::process::exit(101);
231                    }
232                return Self {
233                    $($fname,)*
234                        $($aname,)*
235                        $($rname)?
236                }
237            }
238            fn has_args(v: String) -> (bool, usize) {
239                $(
240                    if $(v == concat!("-", $flag) || v == concat!("--", $flag))||* {
241                        tt_call::tt_if!{
242                            condition = [{tt_equal::tt_equal}]
243                                input = [{ $ftype bool }]
244                                true = [{
245                                    return (false, 0);
246                                }]
247                            false = [{
248                                return (true, $crate::helper!(exists_or_zero;$($fnum)?));
249                            }]
250                        }   
251                    } else
252                )*
253                    if(v == "--") {
254                        return (false, 0);
255                    }
256                eprintln!("invalid flag {}", v);
257                std::process::exit(101);
258            }
259        }
260    };
261}