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