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