Skip to main content

arg_derive/
lib.rs

1//! Command line argument parser derive
2
3#![allow(clippy::style)]
4
5extern crate proc_macro;
6
7mod utils;
8use utils::*;
9
10use proc_macro::TokenStream;
11use quote::quote;
12
13use core::fmt::Write;
14
15struct Argument {
16    field_name: String,
17    name: String,
18    desc: String,
19    required: bool,
20    is_optional: bool,
21    default: Option<String>,
22}
23
24#[derive(PartialEq, Eq, Debug)]
25enum OptValueType {
26    Help,
27    Bool,
28    Value,
29    MultiValue,
30}
31
32struct Opt {
33    arg: Argument,
34    long: String,
35    short: Option<String>,
36    typ: OptValueType,
37}
38
39struct Command {
40    variant_name: String,
41    command_name: String,
42    desc: String,
43}
44
45const CONCAT_DEFAULT_PROG_NAME_ARGS: &str = "env!(\"CARGO_PKG_NAME\"), \" \", env!(\"CARGO_PKG_VERSION\")";
46const FROM_FN: &str = "core::str::FromStr::from_str";
47const TAB: &str = "    ";
48const PARSER_TRAIT: &str = "arg::Args";
49const DEFAULT_INIT: &str = "Default::default()";
50const INVALID_ARG_TYPE_STRING: &str = "Attribute accepts only str";
51const INVALID_REQUIRED_BOOL: &str = "Attribute required cannot be applied to bool switch";
52const UNKNOWN_ARG_ATTR: &str = "Unknown attribute is used";
53const ARG_INVALID_CHARS: &[char] = &[' ', '\t'];
54const ARG_NAME_SPACE_ERROR: &str = "Name contains space character";
55
56fn parse_segment(segment: &syn::PathSegment) -> OptValueType {
57    if segment.ident == "bool" {
58        OptValueType::Bool
59    } else if segment.ident == "Vec" {
60        OptValueType::MultiValue
61    } else {
62        OptValueType::Value
63    }
64}
65
66fn from_enum(ast: &syn::DeriveInput, payload: &syn::DataEnum) -> TokenStream {
67    let mut about_prog = String::new();
68    for attr in ast.attrs.iter() {
69        match &attr.meta {
70            syn::Meta::NameValue(value) => if value.path.is_ident("doc") {
71                let literal = match &value.value {
72                    syn::Expr::Lit(literal) => &literal.lit,
73                    _ => return syn::Error::new_spanned(value.clone(), "Attribute should be liberal").to_compile_error().into()
74                };
75                if let syn::Lit::Str(ref text) = literal {
76                    about_prog.push_str(&text.value());
77                    about_prog.push_str("\n");
78                }
79            },
80            _ => (),
81        }
82    }
83    about_prog.pop();
84
85    let mut commands = Vec::new();
86    for variant in payload.variants.iter() {
87        let mut desc = String::new();
88        let variant_name = variant.ident.to_string();
89        if variant_name.is_empty() {
90            return syn::Error::new_spanned(&variant.ident, "Oi, mate, You cannot have enum variant without name").to_compile_error().into()
91        }
92        let command_name = to_hyphenated_lower_case(&variant_name);
93        if command_name.eq_ignore_ascii_case("help") {
94            return syn::Error::new_spanned(&variant.ident, "Oi, mate, You cannot use variant 'Help'").to_compile_error().into()
95        }
96
97        for attr in variant.attrs.iter() {
98            match &attr.meta {
99                syn::Meta::NameValue(value) => if value.path.is_ident("doc") {
100                    let literal = match &value.value {
101                        syn::Expr::Lit(literal) => &literal.lit,
102                        _ => return syn::Error::new_spanned(value.clone(), "Attribute should be liberal").to_compile_error().into()
103                    };
104
105                    if let syn::Lit::Str(ref text) = literal {
106                        desc.push_str(&text.value());
107                        desc.push_str(" ");
108                    }
109                },
110                _ => continue
111            }
112        }
113
114        let field = match &variant.fields {
115            syn::Fields::Unit => return syn::Error::new_spanned(&variant.fields, "Unit variant cannot be used").to_compile_error().into(),
116            syn::Fields::Named(_) => return syn::Error::new_spanned(&variant.fields, "I'm too lazy to support named variant").to_compile_error().into(),
117            syn::Fields::Unnamed(fields) => {
118                if fields.unnamed.empty_or_trailing() {
119                    return syn::Error::new_spanned(&fields, "MUST specify single field").to_compile_error().into();
120                } else if fields.unnamed.len() > 1 {
121                    return syn::Error::new_spanned(fields, "MUST not specify more than 1 field").to_compile_error().into();
122                } else {
123                    fields.unnamed.first().unwrap()
124                }
125            },
126        };
127
128        match &field.ty {
129            syn::Type::Path(ref ty) => {
130                let ty = ty.path.segments.last().expect("To have at least one segment");
131                if ty.ident == "Option" {
132                    return syn::Error::new_spanned(ty, "Command cannot be optional").to_compile_error().into()
133                } else {
134                    match parse_segment(ty) {
135                        OptValueType::Bool => return syn::Error::new_spanned(ty, "Command value cannot be boolean").to_compile_error().into(),
136                        OptValueType::MultiValue => return syn::Error::new_spanned(ty, "Command value Vec<_>").to_compile_error().into(),
137                        _ => (),
138                    }
139                }
140            },
141            ty => {
142                return syn::Error::new_spanned(ty, "Expected simple ident or path").to_compile_error().into()
143            }
144        }
145
146        commands.push(Command {
147            command_name,
148            variant_name,
149            desc
150        })
151    }
152
153    if commands.is_empty() {
154        return syn::Error::new_spanned(ast, "Enum must have at least one variant").to_compile_error().into()
155    }
156
157    let (impl_gen, type_gen, where_clause) = ast.generics.split_for_impl();
158
159    let help_msg = {
160        use std::io::Write;
161        use tabwriter::TabWriter;
162
163        let mut tw = TabWriter::new(vec![]);
164
165        let _ = writeln!(tw, "COMMANDS:");
166        for command in commands.iter() {
167            let _ = writeln!(tw, "\t{}\t{}", command.command_name, command.desc);
168        }
169
170        let _ = tw.flush();
171
172        String::from_utf8(tw.into_inner().unwrap()).unwrap()
173    };
174
175    let mut result = String::new();
176    let _ = writeln!(result, "{} {} for {}{} {{", quote!(impl#impl_gen), PARSER_TRAIT, ast.ident, quote!(#type_gen #where_clause));
177
178    let _ = writeln!(result, "{}const HELP: &'static str = \"{}\";", TAB, help_msg);
179
180    //from_args START
181    let _ = writeln!(result, "{}fn from_args<'a, T: IntoIterator<Item = &'a str>>(_args_: T) -> Result<Self, arg::ParseKind<'a>> {{", TAB);
182
183    let _ = writeln!(result, "{0}{0}let mut _args_ = _args_.into_iter();\n", TAB);
184
185    //args START
186    let _ = writeln!(result, "{0}{0}while let Some(_arg_) = _args_.next() {{", TAB);
187
188    //help
189    let _ = writeln!(result, "{0}{0}{0}if _arg_.eq_ignore_ascii_case(\"help\") {{", TAB);
190    let _ = writeln!(result, "{0}{0}{0}{0}return Err(arg::ParseKind::Top(arg::ParseError::HelpRequested(Self::HELP)));", TAB);
191    let _ = write!(result, "{0}{0}{0}}}", TAB);
192
193    let mut allowed_commands = String::new();
194    for command in commands.iter() {
195        allowed_commands.push_str(command.command_name.as_str());
196        allowed_commands.push(',');
197        allowed_commands.push(' ');
198
199        //arg START
200        let _ = writeln!(result, " else if _arg_.eq_ignore_ascii_case(\"{}\") {{", command.command_name);
201
202        let _ = writeln!(result, "{0}{0}{0}{0}match {1}::from_args(_args_) {{", TAB, PARSER_TRAIT);
203        let _ = writeln!(result, "{0}{0}{0}{0}{0}Ok(res) => return Ok(Self::{1}(res)),", TAB, command.variant_name);
204        let _ = writeln!(result, "{0}{0}{0}{0}{0}Err(arg::ParseKind::Top(error)) => return Err(arg::ParseKind::Sub(\"{1}\", error)),", TAB, command.command_name);
205        let _ = writeln!(result, "{0}{0}{0}{0}{0}Err(arg::ParseKind::Sub(name, error)) => return Err(arg::ParseKind::Sub(name, error)),", TAB);
206        let _ = writeln!(result, "{0}{0}{0}{0}}}", TAB);
207
208        //arg END
209        let _ = write!(result, "{0}{0}{0}}}", TAB);
210    }
211
212    //invalid arg
213    let _ = writeln!(result, " else {{");
214    let _ = writeln!(result, "{0}{0}{0}{0}return Err(arg::ParseKind::Top(arg::ParseError::InvalidArgValue(\"command\", _arg_)))", TAB);
215    let _ = write!(result, "{0}{0}{0}}}", TAB);
216
217    //args END
218    let _ = writeln!(result, "\n{0}{0}}}", TAB);
219
220    allowed_commands.pop();
221    allowed_commands.pop();
222    //Missing sub-command
223    let _ = writeln!(result, "{0}{0}Err(arg::ParseKind::Top(arg::ParseError::HelpRequested(\"Missing command, possible values: [{1}]\nSee 'help' for details\")))", TAB, allowed_commands);
224
225    //from_args END
226    let _ = writeln!(result, "{}}}", TAB);
227
228    let _ = writeln!(result, "}}");
229
230    if let Ok(val) = std::env::var("ARG_RS_PRINT_PARSER") {
231        match val.trim() {
232            "0" | "false" => (),
233            _ => println!("{result}"),
234        }
235    }
236    result.parse().expect("To parse generated code")
237}
238
239fn from_struct(ast: &syn::DeriveInput, payload: &syn::DataStruct) -> TokenStream {
240    let mut infer_prog_name = false;
241    let mut about_prog = String::new();
242    for attr in ast.attrs.iter() {
243        match &attr.meta {
244            syn::Meta::NameValue(value) => if value.path.is_ident("doc") {
245                let literal = match &value.value {
246                    syn::Expr::Lit(literal) => &literal.lit,
247                    _ => return syn::Error::new_spanned(attr, "Attribute should be liberal").to_compile_error().into()
248                };
249                if let syn::Lit::Str(ref text) = literal {
250                    about_prog.push_str(&text.value());
251                    about_prog.push('\n');
252                }
253            },
254            syn::Meta::List(list) => if list.path.is_ident("arg") {
255                let nested = match attr.parse_args_with(syn::punctuated::Punctuated::<syn::Meta, syn::Token![,]>::parse_terminated) {
256                    Ok(nested) => nested,
257                    Err(error) => {
258                        let error = format!("arg attribute should be list of attributes: {error}");
259                        return syn::Error::new_spanned(list, error).to_compile_error().into();
260                    }
261                };
262                for value_attr in nested {
263                    match value_attr {
264                        syn::Meta::Path(value_attr) => if value_attr.is_ident("infer_name") {
265                            infer_prog_name = true;
266                        } else {
267                            let ident = utils::FormatOptionalIdent(value_attr.get_ident());
268                            return syn::Error::new_spanned(&value_attr, format!("Unknown attribute: '{ident}'. Expected: infer_name")).to_compile_error().into();
269                        },
270                        unexpected => return syn::Error::new_spanned(&unexpected, "Unexpected attribute").to_compile_error().into(),
271                    }
272                }
273            },
274            _ => (),
275        }
276    }
277
278    about_prog.pop();
279
280    let mut options = Vec::new();
281    let mut arguments = Vec::new();
282
283    options.push(Opt {
284        arg: Argument {
285            field_name: "_".to_owned(),
286            name: "help".to_owned(),
287            desc: "Prints this help information".to_owned(),
288            required: false,
289            is_optional: false,
290            default: None,
291        },
292        short: Some("h".to_owned()),
293        long: "help".to_owned(),
294        typ: OptValueType::Help,
295    });
296
297    let mut sub_command = None;
298    let mut multi_argument = None;
299
300    for field in payload.fields.iter() {
301        let field_name = field.ident.as_ref().unwrap().to_string();
302        let name = field.ident.as_ref().unwrap().to_string().trim_matches(|ch| !char::is_alphanumeric(ch)).to_owned();
303        let mut desc = String::new();
304        let mut short = None;
305        let mut long = None;
306        let mut required = false;
307        let mut is_sub = false;
308
309        let (is_optional, typ) = match field.ty {
310            syn::Type::Path(ref ty) => {
311                let ty = ty.path.segments.last().expect("To have at least one segment");
312
313                if ty.ident == "Option" {
314                    let ty = match &ty.arguments {
315                        syn::PathArguments::AngleBracketed(ref args) => match args.args.len() {
316                            0 => return syn::Error::new_spanned(&ty.ident, "Oi, mate, Option is without type arguments. Fix it").to_compile_error().into(),
317                            1 => match args.args.first().unwrap() {
318                                syn::GenericArgument::Type(syn::Type::Path(ty)) => parse_segment(ty.path.segments.last().expect("To have at least one segment")),
319                                _ => return syn::Error::new_spanned(&ty.ident, "Oi, mate, Option should have type argument, but got some other shite. Fix it").to_compile_error().into(),
320                            },
321                            _ => return syn::Error::new_spanned(&ty.ident, "Oi, mate, Option has too many type arguments. Fix it").to_compile_error().into()
322                        },
323                        syn::PathArguments::None => return syn::Error::new_spanned(&ty.ident, "Oi, mate, Option is without type arguments. Fix it").to_compile_error().into(),
324                        syn::PathArguments::Parenthesized(_) => return syn::Error::new_spanned(&ty.ident, "Oi, mate, you got wrong brackets for your Option . Fix it").to_compile_error().into(),
325                    };
326
327                    (true, ty)
328                } else {
329                    (false, parse_segment(ty))
330                }
331            },
332            _ => (false, OptValueType::Value),
333        };
334
335        if is_optional && typ == OptValueType::MultiValue {
336            return syn::Error::new_spanned(field, "Option<Vec<_>> makes no sense. Just use plain Vec<_>").to_compile_error().into();
337        }
338
339        let mut default = None;
340
341        for attr in field.attrs.iter() {
342            match &attr.meta {
343                syn::Meta::NameValue(value) => if value.path.is_ident("doc") {
344                    let literal = match &value.value {
345                        syn::Expr::Lit(literal) => &literal.lit,
346                        _ => return syn::Error::new_spanned(attr, "Attribute should be liberal").to_compile_error().into()
347                    };
348                    if let syn::Lit::Str(ref text) = literal {
349                        desc.push_str(&text.value());
350                        desc.push(' ');
351                    }
352                },
353                syn::Meta::List(value) => if value.path.is_ident("arg") {
354                    let nested = match attr.parse_args_with(syn::punctuated::Punctuated::<syn::Meta, syn::Token![,]>::parse_terminated) {
355                        Ok(nested) => nested,
356                        Err(error) => {
357                            let error = format!("arg attribute should be list of attributes: {error}");
358                            return syn::Error::new_spanned(value, error).to_compile_error().into();
359                        }
360                    };
361
362                    for value_attr in nested {
363                        match value_attr {
364                            syn::Meta::Path(value_attr) => if value_attr.is_ident("short") {
365                                short = Some(format!("{}", name.chars().next().unwrap()).to_lowercase());
366                            } else if value_attr.is_ident("long") {
367                                long = Some(name.to_lowercase());
368                            } else if value_attr.is_ident("default_value") {
369                                default = Some(DEFAULT_INIT.to_owned());
370                            } else if value_attr.is_ident("required") {
371                                required = true;
372                            } else if value_attr.is_ident("sub") {
373                                if typ == OptValueType::Value {
374                                    is_sub = true;
375                                } else {
376                                    return syn::Error::new_spanned(value_attr, "Sub-command must be simple value").to_compile_error().into();
377                                }
378                            }
379                            syn::Meta::NameValue(value_attr) => if value_attr.path.is_ident("short") {
380                                let literal = match &value_attr.value {
381                                    syn::Expr::Lit(literal) => &literal.lit,
382                                    _ => return syn::Error::new_spanned(attr, "Attribute should be liberal").to_compile_error().into()
383                                };
384
385                                if let syn::Lit::Str(ref text) = literal {
386                                    let value_attr_text = text.value();
387
388                                    if value_attr_text.contains(ARG_INVALID_CHARS) {
389                                        return syn::Error::new_spanned(literal.clone(), ARG_NAME_SPACE_ERROR).to_compile_error().into();
390                                    }
391
392                                    short = Some(value_attr_text);
393                                } else {
394                                    return syn::Error::new_spanned(value_attr.path.clone(), INVALID_ARG_TYPE_STRING).to_compile_error().into();
395                                }
396                            } else if value_attr.path.is_ident("long") {
397                                let literal = match &value_attr.value {
398                                    syn::Expr::Lit(literal) => &literal.lit,
399                                    _ => return syn::Error::new_spanned(attr, "Attribute should be liberal").to_compile_error().into()
400                                };
401
402                                if let syn::Lit::Str(ref text) = literal {
403                                    let value_attr_text = text.value();
404
405                                    if value_attr_text.contains(ARG_INVALID_CHARS) {
406                                        return syn::Error::new_spanned(literal.clone(), ARG_NAME_SPACE_ERROR).to_compile_error().into();
407                                    }
408
409                                    long = Some(value_attr_text)
410                                } else {
411                                    return syn::Error::new_spanned(value_attr.path.clone(), INVALID_ARG_TYPE_STRING).to_compile_error().into();
412                                }
413                            } else if value_attr.path.is_ident("default_value") {
414                                let literal = match &value_attr.value {
415                                    syn::Expr::Lit(literal) => &literal.lit,
416                                    _ => return syn::Error::new_spanned(attr, "Attribute should be liberal").to_compile_error().into()
417                                };
418
419                                if let syn::Lit::Str(ref text) = literal {
420                                    default = Some(text.value());
421                                } else {
422                                    return syn::Error::new_spanned(value_attr.path.clone(), INVALID_ARG_TYPE_STRING).to_compile_error().into();
423                                }
424                            } else {
425                                return syn::Error::new_spanned(value_attr.path.clone(), UNKNOWN_ARG_ATTR).to_compile_error().into();
426                            }
427                            _ => {
428                            },
429                        }
430                    } //nested meta
431                },
432                _ => (),
433            }
434        }
435
436        desc.pop();
437
438        if required && default.is_some() {
439            return syn::Error::new_spanned(field.ident.clone(), "Marked as required, but default value is provided?").to_compile_error().into();
440        } else if is_optional && default.is_some() {
441            return syn::Error::new_spanned(field.ident.clone(), "Optional, but default value is provided?").to_compile_error().into();
442        } else if is_sub && is_optional {
443            return syn::Error::new_spanned(field.ident.clone(), "Sub-command cannot be optional").to_compile_error().into();
444        } else if is_sub && default.is_some() {
445            return syn::Error::new_spanned(field.ident.clone(), "Sub-command cannot have default value").to_compile_error().into();
446        } else if !required && !is_optional && default.is_none() {
447            default = Some(DEFAULT_INIT.to_owned());
448        }
449
450        if short.is_none() && long.is_none() {
451            if typ == OptValueType::MultiValue {
452                if multi_argument.is_some() {
453                    return syn::Error::new_spanned(field.ident.clone(), "Second argument collection. There can be only one").to_compile_error().into();
454                } else if sub_command.is_some() {
455                    return syn::Error::new_spanned(field.ident.clone(), "Multi-argument collection and sub-command are mutually exclusive").to_compile_error().into();
456                }
457
458                multi_argument = Some(Argument {
459                    field_name,
460                    name,
461                    desc,
462                    required,
463                    is_optional,
464                    default,
465                });
466
467            } else if is_sub {
468                if sub_command.is_some() {
469                    return syn::Error::new_spanned(field.ident.clone(), "Second sub-command. There can be only one").to_compile_error().into();
470                } else if multi_argument.is_some() {
471                    return syn::Error::new_spanned(field.ident.clone(), "Sub-command and multi-argument collection are mutually exclusive").to_compile_error().into();
472                }
473
474                sub_command = Some(Argument {
475                    field_name,
476                    name,
477                    desc,
478                    required: true,
479                    is_optional: false,
480                    default: None,
481                });
482            } else {
483                arguments.push(Argument {
484                    field_name,
485                    name,
486                    desc,
487                    required,
488                    is_optional,
489                    default,
490                })
491            }
492        } else {
493            //Switches
494            if OptValueType::Bool == typ && required {
495                //boolean switch makes no sense as required
496                return syn::Error::new_spanned(field.ident.clone(), INVALID_REQUIRED_BOOL).to_compile_error().into();
497            }
498
499            let long = match long {
500                Some(long) => long,
501                None => name.clone()
502            };
503
504            options.push(Opt {
505                arg: Argument {
506                    field_name,
507                    name,
508                    desc,
509                    required,
510                    is_optional,
511                    default,
512                },
513                short,
514                long,
515                typ
516            })
517        }
518    }
519
520    let (impl_gen, type_gen, where_clause) = ast.generics.split_for_impl();
521
522    let help_msg = {
523        use std::io::Write;
524        use tabwriter::TabWriter;
525
526        let mut tw = TabWriter::new(vec![]);
527
528        let _ = write!(tw, "{}
529
530USAGE:", about_prog);
531
532        if !options.is_empty() {
533            let _ = write!(tw, " [OPTIONS]");
534        }
535
536        for argument in arguments.iter() {
537            let _ = if argument.required {
538                write!(tw, " <{}>", argument.name)
539            } else {
540                write!(tw, " [{}]", argument.name)
541            };
542        }
543
544        if let Some(argument) = multi_argument.as_ref() {
545            let _ = if argument.required {
546                write!(tw, " <{}>...", argument.name)
547            } else {
548                write!(tw, " [{}]...", argument.name)
549            };
550        } else if let Some(argument) = sub_command.as_ref() {
551            let _ = write!(tw, " <{}>", argument.name);
552        }
553
554        if !options.is_empty() {
555            let _ = write!(tw, "\n\nOPTIONS:\n");
556        }
557
558        for option in options.iter() {
559            let _ = write!(tw, "\t");
560            if let Some(short) = option.short.as_ref() {
561                let _ = write!(tw, "-{},", short);
562            }
563            let _ = write!(tw, "\t");
564
565            let _ = write!(tw, "--{}", option.long);
566
567            let _ = match option.typ {
568                OptValueType::MultiValue => write!(tw, " <{}>...", option.arg.name),
569                OptValueType::Value => write!(tw, " <{}>", option.arg.name),
570                _ => Ok(()),
571            };
572
573            let _ = writeln!(tw, "\t{}", option.arg.desc);
574        }
575
576        if !arguments.is_empty() || multi_argument.is_some() || sub_command.is_some() {
577            let _ = write!(tw, "\nARGS:\n");
578        }
579
580        for argument in arguments.iter() {
581            let _ = if argument.required {
582                writeln!(tw, "\t<{}>\t{}", argument.name, argument.desc)
583            } else {
584                writeln!(tw, "\t[{}]\t{}", argument.name, argument.desc)
585            };
586        }
587
588        if let Some(argument) = multi_argument.as_ref() {
589            let _ = writeln!(tw, "\t<{}>...\t{}", argument.name, argument.desc);
590        } else if let Some(command) = sub_command.as_ref() {
591            let _ = writeln!(tw, "\t<{}>\t{}", command.name, command.desc);
592        }
593
594        let _ = tw.flush();
595
596        String::from_utf8(tw.into_inner().unwrap()).unwrap()
597    };
598
599    let mut result = String::new();
600    let _ = writeln!(result, "{} {} for {}{} {{", quote!(impl#impl_gen), PARSER_TRAIT, ast.ident, quote!(#type_gen #where_clause));
601    if infer_prog_name {
602        let _ = writeln!(result, "{}const HELP: &'static str = core::concat!({CONCAT_DEFAULT_PROG_NAME_ARGS}, \"\n\", \"{}\");", TAB, help_msg);
603    } else {
604        let _ = writeln!(result, "{}const HELP: &'static str = \"{}\";", TAB, help_msg);
605    }
606
607    let _ = writeln!(result, "{}fn from_args<'a, T: IntoIterator<Item = &'a str>>(_args_: T) -> Result<Self, arg::ParseKind<'a>> {{", TAB);
608
609    for option in options.iter() {
610        if option.arg.field_name == "_" {
611            continue;
612        }
613
614        let _ = match option.typ {
615            OptValueType::MultiValue => writeln!(result, "{0}{0}let mut {1} = Vec::new();", TAB, option.arg.field_name),
616            OptValueType::Bool => writeln!(result, "{0}{0}let mut {1} = false;", TAB, option.arg.field_name),
617            _ => writeln!(result, "{0}{0}let mut {1} = None;", TAB, option.arg.field_name),
618        };
619    }
620
621    for argument in arguments.iter() {
622        let _ = writeln!(result, "{0}{0}let mut {1} = None;", TAB, argument.field_name);
623    }
624
625    if let Some(argument) = multi_argument.as_ref() {
626        let _ = writeln!(result, "{0}{0}let mut {1} = Vec::new();", TAB, argument.field_name);
627    } else if let Some(command) = sub_command.as_ref() {
628        let _ = writeln!(result, "{0}{0}let mut {1} = None;", TAB, command.field_name);
629    }
630
631    let _ = writeln!(result, "{0}{0}let mut _args_ = _args_.into_iter();\n", TAB);
632    let _ = writeln!(result, "{0}{0}while let Some(_arg_) = _args_.next() {{", TAB);
633
634    //options
635    let _ = writeln!(result, "{0}{0}{0}if let Some(_arg_) = _arg_.strip_prefix('-') {{", TAB);
636    let _ = writeln!(result, "{0}{0}{0}{0}match _arg_ {{", TAB);
637    let _ = writeln!(result, "{0}{0}{0}{0}{0}\"h\" | \"-help\" => return Err(arg::ParseKind::Top(arg::ParseError::HelpRequested(Self::HELP))),", TAB);
638    let _ = writeln!(result, "{0}{0}{0}{0}{0}\"\" => (),", TAB);
639
640    for option in options.iter() {
641        if option.arg.field_name == "_" {
642            continue;
643        }
644
645        let _ = write!(result, "{0}{0}{0}{0}{0}", TAB);
646
647        if let Some(short) = option.short.as_ref() {
648            let _ = write!(result, "\"{}\" | ", short);
649        }
650
651        let _ = write!(result, "\"-{}\" => ", option.long);
652
653        let _ = match option.typ {
654            OptValueType::Help => panic!("Option Help is invalid here. Bug report it"),
655            OptValueType::Bool => write!(result, "{{ {0} = !{0}; continue }},", option.arg.field_name),
656            OptValueType::Value => write!(result, "match _args_.next() {{
657{0}{0}{0}{0}{0}{0}Some(_next_arg_) => match {1}(_next_arg_) {{
658{0}{0}{0}{0}{0}{0}{0}Ok(value) => {{ {2} = Some(value); continue }},
659{0}{0}{0}{0}{0}{0}{0}Err(_) => return Err(arg::ParseKind::Top(arg::ParseError::InvalidFlagValue(\"{3}\", _next_arg_))),
660{0}{0}{0}{0}{0}{0}}},
661{0}{0}{0}{0}{0}{0}None => return Err(arg::ParseKind::Top(arg::ParseError::MissingValue(\"{3}\"))),
662{0}{0}{0}{0}{0}}}", TAB, FROM_FN, option.arg.field_name, option.arg.name),
663            OptValueType::MultiValue => write!(result, "match _args_.next() {{
664{0}{0}{0}{0}{0}{0}Some(_next_arg_) => match {1}(_next_arg_) {{
665{0}{0}{0}{0}{0}{0}{0}Ok(value) => {{ {2}.push(value); continue }},
666{0}{0}{0}{0}{0}{0}{0}Err(_) => return Err(arg::ParseKind::Top(arg::ParseError::InvalidFlagValue(\"{3}\", _next_arg_))),
667{0}{0}{0}{0}{0}{0}}},
668{0}{0}{0}{0}{0}{0}None => return Err(arg::ParseKind::Top(arg::ParseError::MissingValue(\"{3}\"))),
669{0}{0}{0}{0}{0}}}", TAB, FROM_FN, option.arg.field_name, option.arg.name),
670        };
671        result.push('\n');
672    }
673    let _ = writeln!(result, "{0}{0}{0}{0}{0}_ => return Err(arg::ParseKind::Top(arg::ParseError::UnknownFlag(_arg_))),", TAB);
674
675    let _ = writeln!(result, "{0}{0}{0}{0}}}", TAB);
676    let _ = writeln!(result, "{0}{0}{0}}}", TAB);
677    //rest args
678    for (idx, arg) in arguments.iter().enumerate() {
679        if idx == 0 {
680            let _ = writeln!(result, "{0}{0}{0}if {1}.is_none() {{", TAB, arg.field_name);
681        } else {
682            let _ = writeln!(result, "{0}{0}{0}}} else if {1}.is_none() {{", TAB, arg.field_name);
683        }
684        let _ = writeln!(result, "{0}{0}{0}{0}match {1}(_arg_) {{", TAB, FROM_FN);
685        let _ = writeln!(result, "{0}{0}{0}{0}{0}Ok(_res_) => {1} = Some(_res_),", TAB, arg.field_name);
686        let _ = writeln!(result, "{0}{0}{0}{0}{0}Err(_) => return Err(arg::ParseKind::Top(arg::ParseError::InvalidArgValue(\"{1}\", _arg_))),", TAB, arg.field_name);
687        let _ = writeln!(result, "{0}{0}{0}{0}}}", TAB);
688    }
689    //too many args?
690    if !arguments.is_empty() {
691        let _ = writeln!(result, "{0}{0}{0}}} else {{", TAB);
692    }
693
694    if let Some(arg) = multi_argument.as_ref() {
695        let _ = writeln!(result, "{0}{0}{0}{0}match {1}(_arg_) {{", TAB, FROM_FN);
696        let _ = writeln!(result, "{0}{0}{0}{0}{0}Ok(_res_) => {1}.push(_res_),", TAB, arg.field_name);
697        let _ = writeln!(result, "{0}{0}{0}{0}{0}Err(_) => return Err(arg::ParseKind::Top(arg::ParseError::InvalidArgValue(\"{1}\", _arg_))),", TAB, arg.field_name);
698        let _ = writeln!(result, "{0}{0}{0}{0}}}", TAB);
699    } else if let Some(command) = sub_command.as_ref() {
700        let _ = writeln!(result, "{0}{0}{0}{0}match {1}::from_args(core::iter::once(_arg_).chain(_args_)) {{", TAB, PARSER_TRAIT);
701        let _ = writeln!(result, "{0}{0}{0}{0}{0}Ok(_res_) => {{ {1} = Some(_res_); break; }},", TAB, command.field_name);
702        let _ = writeln!(result, "{0}{0}{0}{0}{0}Err(arg::ParseKind::Top(arg::ParseError::InvalidArgValue(_, invalid_value))) => return Err(arg::ParseKind::Top(arg::ParseError::InvalidArgValue(\"{1}\", invalid_value))),", TAB, command.field_name);
703        let _ = writeln!(result, "{0}{0}{0}{0}{0}Err(arg::ParseKind::Top(_)) => return Err(arg::ParseKind::Top(arg::ParseError::RequiredArgMissing(\"{1}\"))),", TAB, command.field_name);
704        let _ = writeln!(result, "{0}{0}{0}{0}{0}Err(arg::ParseKind::Sub(name, error)) => return Err(arg::ParseKind::Sub(name, error)),", TAB);
705        let _ = writeln!(result, "{0}{0}{0}{0}}}", TAB);
706    } else {
707        let _ = writeln!(result, "{0}{0}{0}{0} return Err(arg::ParseKind::Top(arg::ParseError::TooManyArgs));", TAB);
708    }
709    //exit args
710    if !arguments.is_empty() {
711        let _ = writeln!(result, "{0}{0}{0}}}", TAB);
712        let _ = writeln!(result, "{0}{0}}}", TAB);
713    } else {
714        let _ = writeln!(result, "{0}{0}}}", TAB);
715    }
716
717    //Set defaults
718    for option in options.iter() {
719        if option.arg.field_name == "_" {
720            continue;
721        }
722
723        let _ = match option.typ {
724            OptValueType::MultiValue => Ok(()),
725            OptValueType::Bool => Ok(()),
726            _ => match option.arg.default {
727                Some(ref default) => writeln!(result, "{0}{0}let {1} = if let Some(value) = {1} {{ value }} else {{ {2} }};", TAB, option.arg.field_name, default),
728                None => match option.arg.is_optional {
729                    true => Ok(()),
730                    false => writeln!(result, "{0}{0}let {1} = if let Some(value) = {1} {{ value }} else {{ return Err(arg::ParseKind::Top(arg::ParseError::RequiredArgMissing(\"{2}\"))) }};", TAB, option.arg.field_name, option.arg.name),
731                },
732            },
733        };
734    }
735
736    for arg in arguments.iter() {
737        let _ = match arg.default {
738            Some(ref default) => writeln!(result, "{0}{0}let {1} = if let Some(value) = {1} {{ value }} else {{ {2} }};", TAB, arg.field_name, default),
739            None => match arg.is_optional {
740                true => Ok(()),
741                false => writeln!(result, "{0}{0}let {1} = if let Some(value) = {1} {{ value }} else {{ return Err(arg::ParseKind::Top(arg::ParseError::RequiredArgMissing(\"{2}\"))) }};", TAB, arg.field_name, arg.name),
742            }
743        };
744    }
745
746    if let Some(command) = sub_command.as_ref() {
747        let _ = writeln!(result, "{0}{0}let {1} = match {1} {{", TAB, command.field_name);
748        let _ = writeln!(result, "{0}{0}{0}Some({1}) => {1},", TAB, command.field_name);
749        //Generate sub-command help
750        let _ = writeln!(result, "{0}{0}{0}None => match {1}::from_args([]) {{", TAB, PARSER_TRAIT);
751        let _ = writeln!(result, "{0}{0}{0}{0}Ok({1}) => {1},", TAB, command.field_name);
752        let _ = writeln!(result, "{0}{0}{0}{0}Err(error) => return Err(error),", TAB);
753        let _ = writeln!(result, "{0}{0}{0}}}", TAB);
754        //Finalize sub-command handling
755        let _ = writeln!(result, "{0}{0}}};", TAB);
756    }
757
758    //Fill result
759    let _ = writeln!(result, "{0}{0}Ok(Self {{", TAB);
760
761    for option in options.iter() {
762        if option.arg.field_name == "_" {
763            continue;
764        }
765
766        let _ = if option.arg.is_optional && option.typ == OptValueType::Bool {
767            writeln!(result, "{0}{0}{0}{1}: Some({1}),", TAB, option.arg.field_name)
768        } else {
769            writeln!(result, "{0}{0}{0}{1},", TAB, option.arg.field_name)
770        };
771    }
772
773    for arg in arguments.iter() {
774        let _ = writeln!(result, "{0}{0}{0}{1},", TAB, arg.field_name);
775    }
776
777    if let Some(arg) = multi_argument.as_ref() {
778        let _ = writeln!(result, "{0}{0}{0}{1},", TAB, arg.field_name);
779    } else if let Some(arg) = sub_command.as_ref() {
780        let _ = writeln!(result, "{0}{0}{0}{1},", TAB, arg.field_name);
781    }
782
783    let _ = writeln!(result, "{0}{0}}})", TAB);
784
785    //Exit fn
786    let _ = writeln!(result, "{}}}", TAB);
787
788    let _ = writeln!(result, "}}");
789
790    if let Ok(val) = std::env::var("ARG_RS_PRINT_PARSER") {
791        match val.trim() {
792            "0" | "false" => (),
793            _ => println!("{result}"),
794        }
795    }
796    result.parse().expect("To parse generated code")
797}
798
799#[proc_macro_derive(Args, attributes(parser, arg))]
800pub fn parser_derive(input: TokenStream) -> TokenStream {
801    const INVALID_INPUT_TYPE: &str = "Unsupported parser input type. Expect: struct";
802    let ast: syn::DeriveInput = syn::parse(input).unwrap();
803
804    match ast.data {
805        syn::Data::Struct(ref data) => from_struct(&ast, data),
806        syn::Data::Enum(ref data) => from_enum(&ast, data),
807        _ => syn::Error::new_spanned(ast.ident, INVALID_INPUT_TYPE).to_compile_error().into(),
808    }
809}