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}
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 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 let _ = writeln!(result, "{0}{0}while let Some(_arg_) = _args_.next() {{", TAB);
264
265 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 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 let _ = write!(result, "{0}{0}{0}}}", TAB);
287 }
288
289 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 let _ = writeln!(result, "\n{0}{0}}}", TAB);
296
297 allowed_commands.pop();
298 allowed_commands.pop();
299 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 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 } },
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 short.is_none() && long.is_none() {
575 if env_key.is_none() && env_value_default && !typ.is_multi_value() && !is_sub {
576 env_key = Some(name.to_uppercase());
577 }
578
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 if OptValueType::Bool == typ && required {
630 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
640 if env_key.is_none() && env_value_default {
641 env_key = Some(name.to_uppercase());
642 }
643
644 options.push(Opt {
645 arg: Argument {
646 field_name,
647 name,
648 desc,
649 required,
650 is_optional,
651 default,
652 env_key,
653 },
654 short,
655 long,
656 typ
657 })
658 }
659 }
660
661 let (impl_gen, type_gen, where_clause) = ast.generics.split_for_impl();
662
663 let help_msg = {
664 use std::io::Write;
665 use tabwriter::TabWriter;
666
667 let mut tw = TabWriter::new(vec![]);
668
669 let _ = write!(tw, "{}
670
671USAGE:", about_prog);
672
673 if !options.is_empty() {
674 let _ = write!(tw, " [OPTIONS]");
675 }
676
677 for argument in arguments.iter() {
678 let _ = if argument.required {
679 write!(tw, " <{}>", argument.name)
680 } else {
681 write!(tw, " [{}]", argument.name)
682 };
683 }
684
685 if let Some(argument) = multi_argument.as_ref() {
686 let _ = if argument.required {
687 write!(tw, " <{}>...", argument.name)
688 } else {
689 write!(tw, " [{}]...", argument.name)
690 };
691 } else if let Some(argument) = sub_command.as_ref() {
692 let _ = write!(tw, " <{}>", argument.name);
693 }
694
695 if !options.is_empty() {
696 let _ = write!(tw, "\n\nOPTIONS:\n");
697 }
698
699 for option in options.iter() {
700 let _ = write!(tw, "\t");
701 if let Some(short) = option.short.as_ref() {
702 let _ = write!(tw, "-{},", short);
703 }
704 let _ = write!(tw, "\t");
705
706 let _ = write!(tw, "--{}", option.long);
707
708 let _ = match option.typ {
709 OptValueType::MultiValue => write!(tw, " <{}>...", option.arg.name),
710 OptValueType::Value => write!(tw, " <{}>", option.arg.name),
711 _ => Ok(()),
712 };
713
714
715 let desc = option.arg.env_desc_fmt(&env_prefix);
716 let _ = writeln!(tw, "\t{desc}");
717 }
718
719 if !arguments.is_empty() || multi_argument.is_some() || sub_command.is_some() {
720 let _ = write!(tw, "\nARGS:\n");
721 }
722
723 for argument in arguments.iter() {
724 let arg_desc = argument.env_desc_fmt(&env_prefix);
725 let _ = if argument.required {
726 writeln!(tw, "\t<{}>\t{arg_desc}", argument.name)
727 } else {
728 writeln!(tw, "\t[{}]\t{arg_desc}", argument.name)
729 };
730 }
731
732 if let Some(argument) = multi_argument.as_ref() {
733 let arg_desc = argument.desc.trim();
734 let _ = writeln!(tw, "\t<{}>...\t{arg_desc}", argument.name);
735 } else if let Some(command) = sub_command.as_ref() {
736 let arg_desc = command.desc.trim();
737 let _ = writeln!(tw, "\t<{}>\t{arg_desc}", command.name);
738 }
739
740 let _ = tw.flush();
741
742 String::from_utf8(tw.into_inner().unwrap()).unwrap()
743 };
744
745 let mut result = String::new();
746 let _ = writeln!(result, "{} {} for {}{} {{", quote!(impl#impl_gen), PARSER_TRAIT, ast.ident, quote!(#type_gen #where_clause));
747 if infer_prog_name {
748 let _ = writeln!(result, "{}const HELP: &'static str = core::concat!({CONCAT_DEFAULT_PROG_NAME_ARGS}, \"\n\", \"{}\");", TAB, help_msg);
749 } else {
750 let _ = writeln!(result, "{}const HELP: &'static str = \"{}\";", TAB, help_msg);
751 }
752
753 let _ = writeln!(result, "{}fn from_args<'a, T: IntoIterator<Item = &'a str>>(_args_: T) -> Result<Self, arg::ParseKind<'a>> {{", TAB);
754
755 for option in options.iter() {
756 if option.arg.field_name == "_" {
757 continue;
758 }
759
760 let _ = match option.typ {
761 OptValueType::MultiValue => writeln!(result, "{0}{0}let mut {1} = Vec::new();", TAB, option.arg.field_name),
762 OptValueType::Bool => match &option.arg.env_key {
763 Some(env_key) => {
764 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);
765 let _ = writeln!(result, "{0}{0}{0}match {FROM_FN}(_env_val_.trim()) {{", TAB);
766 let _ = writeln!(result, "{0}{0}{0}{0}Ok(_env_val_) => _env_val_,", TAB);
767 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);
768 let _ = writeln!(result, "{0}{0}{0}}}", TAB);
769 let _ = writeln!(result, "{0}{0}}} else {{", TAB);
770 let _ = writeln!(result, "{0}{0}{0}false", TAB);
771 writeln!(result, "{0}{0}}};", TAB)
772 },
773 None => writeln!(result, "{0}{0}let mut {1} = false;", TAB, option.arg.field_name),
774 },
775 _ => writeln!(result, "{0}{0}let mut {1} = None;", TAB, option.arg.field_name),
776 };
777 }
778
779 for argument in arguments.iter() {
780 let _ = writeln!(result, "{0}{0}let mut {1} = None;", TAB, argument.field_name);
781 }
782
783 if let Some(argument) = multi_argument.as_ref() {
784 let _ = writeln!(result, "{0}{0}let mut {1} = Vec::new();", TAB, argument.field_name);
785 } else if let Some(command) = sub_command.as_ref() {
786 let _ = writeln!(result, "{0}{0}let mut {1} = None;", TAB, command.field_name);
787 }
788
789 let _ = writeln!(result, "{0}{0}let mut _args_ = _args_.into_iter();\n", TAB);
790 let _ = writeln!(result, "{0}{0}while let Some(_arg_) = _args_.next() {{", TAB);
791
792 let _ = writeln!(result, "{0}{0}{0}if let Some(_arg_) = _arg_.strip_prefix('-') {{", TAB);
794 let _ = writeln!(result, "{0}{0}{0}{0}match _arg_ {{", TAB);
795 let _ = writeln!(result, "{0}{0}{0}{0}{0}\"h\" | \"-help\" => return Err(arg::ParseKind::Top(arg::ParseError::HelpRequested(Self::HELP))),", TAB);
796 let _ = writeln!(result, "{0}{0}{0}{0}{0}\"\" => (),", TAB);
797
798 for option in options.iter() {
799 if option.arg.field_name == "_" {
800 continue;
801 }
802
803 let _ = write!(result, "{0}{0}{0}{0}{0}", TAB);
804
805 if let Some(short) = option.short.as_ref() {
806 let _ = write!(result, "\"{}\" | ", short);
807 }
808
809 let _ = write!(result, "\"-{}\" => ", option.long);
810
811 let _ = match option.typ {
812 OptValueType::Help => panic!("Option Help is invalid here. Bug report it"),
813 OptValueType::Bool => write!(result, "{{ {0} = !{0}; continue }},", option.arg.field_name),
814 OptValueType::Value => write!(result, "match _args_.next() {{
815{0}{0}{0}{0}{0}{0}Some(_next_arg_) => match {1}(_next_arg_) {{
816{0}{0}{0}{0}{0}{0}{0}Ok(value) => {{ {2} = Some(value); continue }},
817{0}{0}{0}{0}{0}{0}{0}Err(_) => return Err(arg::ParseKind::Top(arg::ParseError::InvalidFlagValue(\"{3}\", _next_arg_))),
818{0}{0}{0}{0}{0}{0}}},
819{0}{0}{0}{0}{0}{0}None => return Err(arg::ParseKind::Top(arg::ParseError::MissingValue(\"{3}\"))),
820{0}{0}{0}{0}{0}}}", TAB, FROM_FN, option.arg.field_name, option.arg.name),
821 OptValueType::MultiValue => 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}.push(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 };
829 result.push('\n');
830 }
831 let _ = writeln!(result, "{0}{0}{0}{0}{0}_ => return Err(arg::ParseKind::Top(arg::ParseError::UnknownFlag(_arg_))),", TAB);
832
833 let _ = writeln!(result, "{0}{0}{0}{0}}}", TAB);
834 let _ = writeln!(result, "{0}{0}{0}}}", TAB);
835 for (idx, arg) in arguments.iter().enumerate() {
837 if idx == 0 {
838 let _ = writeln!(result, "{0}{0}{0}if {1}.is_none() {{", TAB, arg.field_name);
839 } else {
840 let _ = writeln!(result, "{0}{0}{0}}} else if {1}.is_none() {{", TAB, arg.field_name);
841 }
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} = Some(_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 }
847 if !arguments.is_empty() {
849 let _ = writeln!(result, "{0}{0}{0}}} else {{", TAB);
850 }
851
852 if let Some(arg) = multi_argument.as_ref() {
853 let _ = writeln!(result, "{0}{0}{0}{0}match {1}(_arg_) {{", TAB, FROM_FN);
854 let _ = writeln!(result, "{0}{0}{0}{0}{0}Ok(_res_) => {1}.push(_res_),", TAB, arg.field_name);
855 let _ = writeln!(result, "{0}{0}{0}{0}{0}Err(_) => return Err(arg::ParseKind::Top(arg::ParseError::InvalidArgValue(\"{1}\", _arg_))),", TAB, arg.field_name);
856 let _ = writeln!(result, "{0}{0}{0}{0}}}", TAB);
857 } else if let Some(command) = sub_command.as_ref() {
858 let _ = writeln!(result, "{0}{0}{0}{0}match {1}::from_args(core::iter::once(_arg_).chain(_args_)) {{", TAB, PARSER_TRAIT);
859 let _ = writeln!(result, "{0}{0}{0}{0}{0}Ok(_res_) => {{ {1} = Some(_res_); break; }},", TAB, command.field_name);
860 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);
861 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);
862 let _ = writeln!(result, "{0}{0}{0}{0}{0}Err(arg::ParseKind::Sub(name, error)) => return Err(arg::ParseKind::Sub(name, error)),", TAB);
863 let _ = writeln!(result, "{0}{0}{0}{0}}}", TAB);
864 } else {
865 let _ = writeln!(result, "{0}{0}{0}{0} return Err(arg::ParseKind::Top(arg::ParseError::TooManyArgs));", TAB);
866 }
867 if !arguments.is_empty() {
869 let _ = writeln!(result, "{0}{0}{0}}}", TAB);
870 let _ = writeln!(result, "{0}{0}}}", TAB);
871 } else {
872 let _ = writeln!(result, "{0}{0}}}", TAB);
873 }
874
875 for option in options.iter() {
877 if option.arg.field_name == "_" {
878 continue;
879 }
880
881 let env_init = option.arg.env_init_fmt(&env_prefix, &option.arg.field_name);
882 let _ = match option.typ {
883 OptValueType::MultiValue => {
884 if let Some(env_key) = option.arg.env_key.as_ref() {
885 let _ = writeln!(result, "\n{0}{0}if let Ok(_env_val_) = ::std::env::var(\"{env_prefix}{env_key}\") {{", TAB);
886 let _ = writeln!(result, "{0}{0}{0}for _env_val_ in _env_val_.split(',') {{", TAB);
887 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);
888 let _ = writeln!(result, "{0}{0}{0}}}", TAB);
889 let _ = writeln!(result, "{0}{0}}}\n", TAB);
890 }
891 Ok(())
892 },
893 OptValueType::Bool => Ok(()),
894 _ => match option.arg.default {
895 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),
896 None => match option.arg.is_optional {
897 true => if let Some(env_key) = option.arg.env_key.as_ref() {
898 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)
899 } else {
900 Ok(())
901 },
902 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),
903 },
904 },
905 };
906 }
907
908 for arg in arguments.iter() {
909 let env_init = arg.env_init_fmt(&env_prefix, &arg.field_name);
910 let _ = match arg.default {
911 Some(ref default) => writeln!(result, "{0}{0}let {1} = if let Some(value) = {1} {{ value }} {env_init} else {{ {2} }};", TAB, arg.field_name, default),
912 None => match arg.is_optional {
913 true => Ok(()),
914 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),
915 }
916 };
917 }
918
919 if let Some(command) = sub_command.as_ref() {
920 let _ = writeln!(result, "{0}{0}let {1} = match {1} {{", TAB, command.field_name);
921 let _ = writeln!(result, "{0}{0}{0}Some({1}) => {1},", TAB, command.field_name);
922 let _ = writeln!(result, "{0}{0}{0}None => match {1}::from_args([]) {{", TAB, PARSER_TRAIT);
924 let _ = writeln!(result, "{0}{0}{0}{0}Ok({1}) => {1},", TAB, command.field_name);
925 let _ = writeln!(result, "{0}{0}{0}{0}Err(error) => return Err(error),", TAB);
926 let _ = writeln!(result, "{0}{0}{0}}}", TAB);
927 let _ = writeln!(result, "{0}{0}}};", TAB);
929 }
930
931 let _ = writeln!(result, "{0}{0}Ok(Self {{", TAB);
933
934 for option in options.iter() {
935 if option.arg.field_name == "_" {
936 continue;
937 }
938
939 let _ = if option.arg.is_optional && option.typ == OptValueType::Bool {
940 writeln!(result, "{0}{0}{0}{1}: Some({1}),", TAB, option.arg.field_name)
941 } else {
942 writeln!(result, "{0}{0}{0}{1},", TAB, option.arg.field_name)
943 };
944 }
945
946 for arg in arguments.iter() {
947 let _ = writeln!(result, "{0}{0}{0}{1},", TAB, arg.field_name);
948 }
949
950 if let Some(arg) = multi_argument.as_ref() {
951 let _ = writeln!(result, "{0}{0}{0}{1},", TAB, arg.field_name);
952 } else if let Some(arg) = sub_command.as_ref() {
953 let _ = writeln!(result, "{0}{0}{0}{1},", TAB, arg.field_name);
954 }
955
956 let _ = writeln!(result, "{0}{0}}})", TAB);
957
958 let _ = writeln!(result, "{}}}", TAB);
960
961 let _ = writeln!(result, "}}");
962
963 if let Ok(val) = std::env::var("ARG_RS_PRINT_PARSER") {
964 match val.trim() {
965 "0" | "false" => (),
966 _ => println!("{result}"),
967 }
968 }
969 result.parse().expect("To parse generated code")
970}
971
972#[proc_macro_derive(Args, attributes(parser, arg))]
973pub fn parser_derive(input: TokenStream) -> TokenStream {
974 const INVALID_INPUT_TYPE: &str = "Unsupported parser input type. Expect: struct";
975 let ast: syn::DeriveInput = syn::parse(input).unwrap();
976
977 match ast.data {
978 syn::Data::Struct(ref data) => from_struct(&ast, data),
979 syn::Data::Enum(ref data) => from_enum(&ast, data),
980 _ => syn::Error::new_spanned(ast.ident, INVALID_INPUT_TYPE).to_compile_error().into(),
981 }
982}