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