1use proc_macro2::{Ident, Span, TokenStream};
16use quote::{format_ident, quote, quote_spanned};
17use syn::{
18 punctuated::Punctuated, spanned::Spanned, token::Comma, Data, DataStruct, DeriveInput, Field,
19 Fields, FieldsNamed, Generics,
20};
21
22use crate::item::{Item, Kind, Name};
23use crate::utils::{inner_type, sub_type, Sp, Ty};
24
25pub(crate) fn derive_args(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
26 let ident = &input.ident;
27
28 match input.data {
29 Data::Struct(DataStruct {
30 fields: Fields::Named(ref fields),
31 ..
32 }) => {
33 let name = Name::Derived(ident.clone());
34 let item = Item::from_args_struct(input, name)?;
35 let fields = collect_args_fields(&item, fields)?;
36 gen_for_struct(&item, ident, &input.generics, &fields)
37 }
38 Data::Struct(DataStruct {
39 fields: Fields::Unit,
40 ..
41 }) => {
42 let name = Name::Derived(ident.clone());
43 let item = Item::from_args_struct(input, name)?;
44 let fields = Punctuated::<Field, Comma>::new();
45 let fields = fields
46 .iter()
47 .map(|field| {
48 let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
49 Ok((field, item))
50 })
51 .collect::<Result<Vec<_>, syn::Error>>()?;
52 gen_for_struct(&item, ident, &input.generics, &fields)
53 }
54 _ => abort_call_site!("`#[derive(Args)]` only supports non-tuple structs"),
55 }
56}
57
58pub(crate) fn gen_for_struct(
59 item: &Item,
60 item_name: &Ident,
61 generics: &Generics,
62 fields: &[(&Field, Item)],
63) -> Result<TokenStream, syn::Error> {
64 if !matches!(&*item.kind(), Kind::Command(_)) {
65 abort! { item.kind().span(),
66 "`{}` cannot be used with `command`",
67 item.kind().name(),
68 }
69 }
70
71 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
72
73 let constructor = gen_constructor(fields)?;
74 let updater = gen_updater(fields, true)?;
75 let raw_deprecated = raw_deprecated();
76
77 let app_var = Ident::new("__clap_app", Span::call_site());
78 let augmentation = gen_augment(fields, &app_var, item, false)?;
79 let augmentation_update = gen_augment(fields, &app_var, item, true)?;
80
81 let group_id = if item.skip_group() {
82 quote!(None)
83 } else {
84 let group_id = item.group_id();
85 quote!(Some(clap::Id::from(#group_id)))
86 };
87
88 Ok(quote! {
89 #[allow(
90 dead_code,
91 unreachable_code,
92 unused_variables,
93 unused_braces,
94 unused_qualifications,
95 )]
96 #[allow(
97 clippy::style,
98 clippy::complexity,
99 clippy::pedantic,
100 clippy::restriction,
101 clippy::perf,
102 clippy::deprecated,
103 clippy::nursery,
104 clippy::cargo,
105 clippy::suspicious_else_formatting,
106 clippy::almost_swapped,
107 clippy::redundant_locals,
108 )]
109 #[automatically_derived]
110 impl #impl_generics clap::FromArgMatches for #item_name #ty_generics #where_clause {
111 fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
112 Self::from_arg_matches_mut(&mut __clap_arg_matches.clone())
113 }
114
115 fn from_arg_matches_mut(__clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
116 #raw_deprecated
117 let v = #item_name #constructor;
118 ::std::result::Result::Ok(v)
119 }
120
121 fn update_from_arg_matches(&mut self, __clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<(), clap::Error> {
122 self.update_from_arg_matches_mut(&mut __clap_arg_matches.clone())
123 }
124
125 fn update_from_arg_matches_mut(&mut self, __clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<(), clap::Error> {
126 #raw_deprecated
127 #updater
128 ::std::result::Result::Ok(())
129 }
130 }
131
132 #[allow(
133 dead_code,
134 unreachable_code,
135 unused_variables,
136 unused_braces,
137 unused_qualifications,
138 )]
139 #[allow(
140 clippy::style,
141 clippy::complexity,
142 clippy::pedantic,
143 clippy::restriction,
144 clippy::perf,
145 clippy::deprecated,
146 clippy::nursery,
147 clippy::cargo,
148 clippy::suspicious_else_formatting,
149 clippy::almost_swapped,
150 clippy::redundant_locals,
151 )]
152 #[automatically_derived]
153 impl #impl_generics clap::Args for #item_name #ty_generics #where_clause {
154 fn group_id() -> Option<clap::Id> {
155 #group_id
156 }
157 fn augment_args<'b>(#app_var: clap::Command) -> clap::Command {
158 #augmentation
159 }
160 fn augment_args_for_update<'b>(#app_var: clap::Command) -> clap::Command {
161 #augmentation_update
162 }
163 }
164 })
165}
166
167pub(crate) fn gen_augment(
170 fields: &[(&Field, Item)],
171 app_var: &Ident,
172 parent_item: &Item,
173 override_required: bool,
174) -> Result<TokenStream, syn::Error> {
175 let mut subcommand_specified = false;
176 let mut args = Vec::new();
177 for (field, item) in fields {
178 let kind = item.kind();
179 let genned = match &*kind {
180 Kind::Command(_)
181 | Kind::Value
182 | Kind::Skip(_, _)
183 | Kind::FromGlobal(_)
184 | Kind::ExternalSubcommand => None,
185 Kind::Subcommand(ty) => {
186 if subcommand_specified {
187 abort!(
188 field.span(),
189 "`#[command(subcommand)]` can only be used once per container"
190 );
191 }
192 subcommand_specified = true;
193
194 let subcmd_type = match (**ty, sub_type(&field.ty)) {
195 (Ty::Option, Some(sub_type)) => sub_type,
196 _ => &field.ty,
197 };
198 let implicit_methods = if **ty == Ty::Option {
199 quote!()
200 } else {
201 quote_spanned! { kind.span()=>
202 .subcommand_required(true)
203 .arg_required_else_help(true)
204 }
205 };
206
207 let override_methods = if override_required {
208 quote_spanned! { kind.span()=>
209 .subcommand_required(false)
210 .arg_required_else_help(false)
211 }
212 } else {
213 quote!()
214 };
215
216 Some(quote! {
217 let #app_var = <#subcmd_type as clap::Subcommand>::augment_subcommands( #app_var );
218 let #app_var = #app_var
219 #implicit_methods
220 #override_methods;
221 })
222 }
223 Kind::Flatten(ty) => {
224 let inner_type = match (**ty, sub_type(&field.ty)) {
225 (Ty::Option, Some(sub_type)) => sub_type,
226 _ => &field.ty,
227 };
228
229 let next_help_heading = item.next_help_heading();
230 let next_display_order = item.next_display_order();
231 let flatten_group_assert = if matches!(**ty, Ty::Option) {
232 quote_spanned! { kind.span()=>
233 <#inner_type as clap::Args>::group_id().expect("cannot `#[flatten]` an `Option<Args>` with `#[group(skip)]`");
234 }
235 } else {
236 quote! {}
237 };
238 if override_required {
239 Some(quote_spanned! { kind.span()=>
240 #flatten_group_assert
241 let #app_var = #app_var
242 #next_help_heading
243 #next_display_order;
244 let #app_var = <#inner_type as clap::Args>::augment_args_for_update(#app_var);
245 })
246 } else {
247 Some(quote_spanned! { kind.span()=>
248 #flatten_group_assert
249 let #app_var = #app_var
250 #next_help_heading
251 #next_display_order;
252 let #app_var = <#inner_type as clap::Args>::augment_args(#app_var);
253 })
254 }
255 }
256 Kind::Arg(ty) => {
257 let value_parser = item.value_parser(&field.ty);
258 let action = item.action(&field.ty);
259 let value_name = item.value_name();
260
261 let implicit_methods = match **ty {
262 Ty::Unit => {
263 quote_spanned! { ty.span()=>
265 .value_name(#value_name)
266 #action
267 }
268 }
269 Ty::Option => {
270 quote_spanned! { ty.span()=>
271 .value_name(#value_name)
272 #value_parser
273 #action
274 }
275 }
276
277 Ty::OptionOption => quote_spanned! { ty.span()=>
278 .value_name(#value_name)
279 .num_args(0..=1)
280 #value_parser
281 #action
282 },
283
284 Ty::OptionVec => {
285 if item.is_positional() {
286 quote_spanned! { ty.span()=>
287 .value_name(#value_name)
288 .num_args(1..) #value_parser
290 #action
291 }
292 } else {
293 quote_spanned! { ty.span()=>
294 .value_name(#value_name)
295 #value_parser
296 #action
297 }
298 }
299 }
300
301 Ty::Vec => {
302 if item.is_positional() {
303 quote_spanned! { ty.span()=>
304 .value_name(#value_name)
305 .num_args(1..) #value_parser
307 #action
308 }
309 } else {
310 quote_spanned! { ty.span()=>
311 .value_name(#value_name)
312 #value_parser
313 #action
314 }
315 }
316 }
317
318 Ty::VecVec | Ty::OptionVecVec => {
319 quote_spanned! { ty.span() =>
320 .value_name(#value_name)
321 #value_parser
322 #action
323 }
324 }
325
326 Ty::Other => {
327 let required = item.find_default_method().is_none();
328 let action_value = action.args();
332 quote_spanned! { ty.span()=>
333 .value_name(#value_name)
334 .required(#required && #action_value.takes_values())
335 #value_parser
336 #action
337 }
338 }
339 };
340
341 let id = item.id();
342 let explicit_methods = item.field_methods();
343 let deprecations = if !override_required {
344 item.deprecations()
345 } else {
346 quote!()
347 };
348 let override_methods = if override_required {
349 quote_spanned! { kind.span()=>
350 .required(false)
351 }
352 } else {
353 quote!()
354 };
355
356 Some(quote_spanned! { field.span()=>
357 let #app_var = #app_var.arg({
358 #deprecations
359
360 #[allow(deprecated)]
361 let arg = clap::Arg::new(#id)
362 #implicit_methods;
363
364 let arg = arg
365 #explicit_methods;
366
367 let arg = arg
368 #override_methods;
369
370 arg
371 });
372 })
373 }
374 };
375 args.push(genned);
376 }
377
378 let deprecations = if !override_required {
379 parent_item.deprecations()
380 } else {
381 quote!()
382 };
383 let initial_app_methods = parent_item.initial_top_level_methods();
384 let final_app_methods = parent_item.final_top_level_methods();
385 let group_app_methods = if parent_item.skip_group() {
386 quote!()
387 } else {
388 let group_id = parent_item.group_id();
389 let literal_group_members = fields
390 .iter()
391 .filter_map(|(_field, item)| {
392 let kind = item.kind();
393 if matches!(*kind, Kind::Arg(_)) {
394 Some(item.id())
395 } else {
396 None
397 }
398 })
399 .collect::<Vec<_>>();
400 let literal_group_members_len = literal_group_members.len();
401 let mut literal_group_members = quote! {{
402 let members: [clap::Id; #literal_group_members_len] = [#( clap::Id::from(#literal_group_members) ),* ];
403 members
404 }};
405 let possible_group_members_len = fields
408 .iter()
409 .filter(|(_field, item)| {
410 let kind = item.kind();
411 matches!(*kind, Kind::Flatten(_))
412 })
413 .count();
414 if 0 < possible_group_members_len {
415 literal_group_members = quote! {{
416 let members: [clap::Id; 0] = [];
417 members
418 }};
419 }
420
421 let group_methods = parent_item.group_methods();
422
423 quote!(
424 .group(
425 clap::ArgGroup::new(#group_id)
426 .multiple(true)
427 #group_methods
428 .args(#literal_group_members)
429 )
430 )
431 };
432 Ok(quote! {{
433 #deprecations
434 let #app_var = #app_var
435 #initial_app_methods
436 #group_app_methods
437 ;
438 #( #args )*
439 #app_var #final_app_methods
440 }})
441}
442
443pub(crate) fn gen_constructor(fields: &[(&Field, Item)]) -> Result<TokenStream, syn::Error> {
444 let fields = fields.iter().map(|(field, item)| {
445 let field_name = field.ident.as_ref().unwrap();
446 let kind = item.kind();
447 let arg_matches = format_ident!("__clap_arg_matches");
448 let genned = match &*kind {
449 Kind::Command(_)
450 | Kind::Value
451 | Kind::ExternalSubcommand => {
452 abort! { kind.span(),
453 "`{}` cannot be used with `arg`",
454 kind.name(),
455 }
456 }
457 Kind::Subcommand(ty) => {
458 let subcmd_type = match (**ty, sub_type(&field.ty)) {
459 (Ty::Option, Some(sub_type)) => sub_type,
460 _ => &field.ty,
461 };
462 match **ty {
463 Ty::Option => {
464 quote_spanned! { kind.span()=>
465 #field_name: {
466 if #arg_matches.subcommand_name().map(<#subcmd_type as clap::Subcommand>::has_subcommand).unwrap_or(false) {
467 Some(<#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?)
468 } else {
469 None
470 }
471 }
472 }
473 },
474 Ty::Other => {
475 quote_spanned! { kind.span()=>
476 #field_name: {
477 <#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?
478 }
479 }
480 },
481 Ty::Unit |
482 Ty::Vec |
483 Ty::OptionOption |
484 Ty::OptionVec |
485 Ty::VecVec |
486 Ty::OptionVecVec => {
487 abort!(
488 ty.span(),
489 "{} types are not supported for subcommand",
490 ty.as_str()
491 );
492 }
493 }
494 }
495
496 Kind::Flatten(ty) => {
497 let inner_type = match (**ty, sub_type(&field.ty)) {
498 (Ty::Option, Some(sub_type)) => sub_type,
499 _ => &field.ty,
500 };
501 match **ty {
502 Ty::Other => {
503 quote_spanned! { kind.span()=>
504 #field_name: <#inner_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?
505 }
506 },
507 Ty::Option => {
508 quote_spanned! { kind.span()=>
509 #field_name: {
510 let group_id = <#inner_type as clap::Args>::group_id()
511 .expect("asserted during `Arg` creation");
512 if #arg_matches.contains_id(group_id.as_str()) {
513 Some(
514 <#inner_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?
515 )
516 } else {
517 None
518 }
519 }
520 }
521 },
522 Ty::Unit |
523 Ty::Vec |
524 Ty::OptionOption |
525 Ty::OptionVec |
526 Ty::VecVec |
527 Ty::OptionVecVec => {
528 abort!(
529 ty.span(),
530 "{} types are not supported for flatten",
531 ty.as_str()
532 );
533 }
534 }
535 },
536
537 Kind::Skip(val, _) => match val {
538 None => quote_spanned!(kind.span()=> #field_name: Default::default()),
539 Some(val) => quote_spanned!(kind.span()=> #field_name: (#val).into()),
540 },
541
542 Kind::Arg(ty) | Kind::FromGlobal(ty) => {
543 gen_parsers(item, ty, field_name, field, None)?
544 }
545 };
546 Ok(genned)
547 }).collect::<Result<Vec<_>, syn::Error>>()?;
548
549 Ok(quote! {{
550 #( #fields ),*
551 }})
552}
553
554pub(crate) fn gen_updater(
555 fields: &[(&Field, Item)],
556 use_self: bool,
557) -> Result<TokenStream, syn::Error> {
558 let mut genned_fields = Vec::new();
559 for (field, item) in fields {
560 let field_name = field.ident.as_ref().unwrap();
561 let kind = item.kind();
562
563 let access = if use_self {
564 quote! {
565 #[allow(non_snake_case)]
566 let #field_name = &mut self.#field_name;
567 }
568 } else {
569 quote!()
570 };
571 let arg_matches = format_ident!("__clap_arg_matches");
572
573 let genned = match &*kind {
574 Kind::Command(_) | Kind::Value | Kind::ExternalSubcommand => {
575 abort! { kind.span(),
576 "`{}` cannot be used with `arg`",
577 kind.name(),
578 }
579 }
580 Kind::Subcommand(ty) => {
581 let subcmd_type = match (**ty, sub_type(&field.ty)) {
582 (Ty::Option, Some(sub_type)) => sub_type,
583 _ => &field.ty,
584 };
585
586 let updater = quote_spanned! { ty.span()=>
587 <#subcmd_type as clap::FromArgMatches>::update_from_arg_matches_mut(#field_name, #arg_matches)?;
588 };
589
590 let updater = match **ty {
591 Ty::Option => quote_spanned! { kind.span()=>
592 if let Some(#field_name) = #field_name.as_mut() {
593 #updater
594 } else {
595 *#field_name = Some(<#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(
596 #arg_matches
597 )?);
598 }
599 },
600 _ => quote_spanned! { kind.span()=>
601 #updater
602 },
603 };
604
605 quote_spanned! { kind.span()=>
606 {
607 #access
608 #updater
609 }
610 }
611 }
612
613 Kind::Flatten(ty) => {
614 let inner_type = match (**ty, sub_type(&field.ty)) {
615 (Ty::Option, Some(sub_type)) => sub_type,
616 _ => &field.ty,
617 };
618
619 let updater = quote_spanned! { ty.span()=>
620 <#inner_type as clap::FromArgMatches>::update_from_arg_matches_mut(#field_name, #arg_matches)?;
621 };
622
623 let updater = match **ty {
624 Ty::Option => quote_spanned! { kind.span()=>
625 if let Some(#field_name) = #field_name.as_mut() {
626 #updater
627 } else {
628 *#field_name = Some(<#inner_type as clap::FromArgMatches>::from_arg_matches_mut(
629 #arg_matches
630 )?);
631 }
632 },
633 _ => quote_spanned! { kind.span()=>
634 #updater
635 },
636 };
637
638 quote_spanned! { kind.span()=>
639 {
640 #access
641 #updater
642 }
643 }
644 }
645
646 Kind::Skip(_, _) => quote!(),
647
648 Kind::Arg(ty) | Kind::FromGlobal(ty) => {
649 gen_parsers(item, ty, field_name, field, Some(&access))?
650 }
651 };
652 genned_fields.push(genned);
653 }
654
655 Ok(quote! {
656 #( #genned_fields )*
657 })
658}
659
660fn gen_parsers(
661 item: &Item,
662 ty: &Sp<Ty>,
663 field_name: &Ident,
664 field: &Field,
665 update: Option<&TokenStream>,
666) -> Result<TokenStream, syn::Error> {
667 let span = ty.span();
668 let convert_type = inner_type(&field.ty);
669 let id = item.id();
670 let get_one = quote_spanned!(span=> remove_one::<#convert_type>);
671 let get_many = quote_spanned!(span=> remove_many::<#convert_type>);
672 let get_occurrences = quote_spanned!(span=> remove_occurrences::<#convert_type>);
673
674 let arg_matches = format_ident!("__clap_arg_matches");
678
679 let field_value = match **ty {
680 Ty::Unit => {
681 quote_spanned! { ty.span()=>
682 ()
683 }
684 }
685
686 Ty::Option => {
687 quote_spanned! { ty.span()=>
688 #arg_matches.#get_one(#id)
689 }
690 }
691
692 Ty::OptionOption => quote_spanned! { ty.span()=>
693 if #arg_matches.contains_id(#id) {
694 Some(
695 #arg_matches.#get_one(#id)
696 )
697 } else {
698 None
699 }
700 },
701
702 Ty::OptionVec => quote_spanned! { ty.span()=>
703 if #arg_matches.contains_id(#id) {
704 Some(#arg_matches.#get_many(#id)
705 .map(|v| v.collect::<Vec<_>>())
706 .unwrap_or_else(Vec::new))
707 } else {
708 None
709 }
710 },
711
712 Ty::Vec => {
713 quote_spanned! { ty.span()=>
714 #arg_matches.#get_many(#id)
715 .map(|v| v.collect::<Vec<_>>())
716 .unwrap_or_else(Vec::new)
717 }
718 }
719
720 Ty::VecVec => quote_spanned! { ty.span()=>
721 #arg_matches.#get_occurrences(#id)
722 .map(|g| g.map(::std::iter::Iterator::collect).collect::<Vec<Vec<_>>>())
723 .unwrap_or_else(Vec::new)
724 },
725
726 Ty::OptionVecVec => quote_spanned! { ty.span()=>
727 #arg_matches.#get_occurrences(#id)
728 .map(|g| g.map(::std::iter::Iterator::collect).collect::<Vec<Vec<_>>>())
729 },
730
731 Ty::Other => {
732 match id {
735 Name::Assigned(_) => {
736 quote_spanned! { ty.span()=>
737 #arg_matches.#get_one(#id)
738 .ok_or_else(|| clap::Error::raw(clap::error::ErrorKind::MissingRequiredArgument, format!("The following required argument was not provided: {}", #id)))?
739 }
740 }
741 Name::Derived(_) => {
742 quote_spanned! { ty.span()=>
743 #arg_matches.#get_one(#id)
744 .ok_or_else(|| clap::Error::raw(clap::error::ErrorKind::MissingRequiredArgument, concat!("The following required argument was not provided: ", #id)))?
745 }
746 }
747 }
748 }
749 };
750
751 let genned = if let Some(access) = update {
752 quote_spanned! { field.span()=>
753 if #arg_matches.contains_id(#id) {
754 #access
755 *#field_name = #field_value
756 }
757 }
758 } else {
759 quote_spanned!(field.span()=> #field_name: #field_value )
760 };
761 Ok(genned)
762}
763
764#[cfg(feature = "raw-deprecated")]
765pub(crate) fn raw_deprecated() -> TokenStream {
766 quote! {}
767}
768
769#[cfg(not(feature = "raw-deprecated"))]
770pub(crate) fn raw_deprecated() -> TokenStream {
771 quote! {
772 #![allow(deprecated)] }
775}
776
777pub(crate) fn collect_args_fields<'a>(
778 item: &'a Item,
779 fields: &'a FieldsNamed,
780) -> Result<Vec<(&'a Field, Item)>, syn::Error> {
781 fields
782 .named
783 .iter()
784 .map(|field| {
785 let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
786 Ok((field, item))
787 })
788 .collect()
789}