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