arg_derive/
lib.rs

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