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