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 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 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 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 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 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 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 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 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 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 let _ = writeln!(result, "{0}{0}}};", TAB);
924 }
925
926 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 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}