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}