• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
2 // Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
3 // Ana Hobden (@hoverbear) <operator@hoverbear.org>
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10 //
11 // This work was derived from Structopt (https://github.com/TeXitoi/structopt)
12 // commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
13 // MIT/Apache 2.0 license.
14 
15 use proc_macro2::{Ident, Span, TokenStream};
16 use proc_macro_error::{abort, abort_call_site};
17 use quote::{format_ident, quote, quote_spanned};
18 use syn::ext::IdentExt;
19 use syn::{
20     punctuated::Punctuated, spanned::Spanned, token::Comma, Data, DataStruct, DeriveInput, Field,
21     Fields, Generics,
22 };
23 
24 use crate::dummies;
25 use crate::item::{Item, Kind, Name};
26 use crate::utils::{inner_type, sub_type, Sp, Ty};
27 
derive_args(input: &DeriveInput) -> TokenStream28 pub fn derive_args(input: &DeriveInput) -> TokenStream {
29     let ident = &input.ident;
30 
31     dummies::args(ident);
32 
33     match input.data {
34         Data::Struct(DataStruct {
35             fields: Fields::Named(ref fields),
36             ..
37         }) => {
38             let name = Name::Derived(ident.clone());
39             let item = Item::from_args_struct(input, name);
40             let fields = fields
41                 .named
42                 .iter()
43                 .map(|field| {
44                     let item = Item::from_args_field(field, item.casing(), item.env_casing());
45                     (field, item)
46                 })
47                 .collect::<Vec<_>>();
48             gen_for_struct(&item, ident, &input.generics, &fields)
49         }
50         Data::Struct(DataStruct {
51             fields: Fields::Unit,
52             ..
53         }) => {
54             let name = Name::Derived(ident.clone());
55             let item = Item::from_args_struct(input, name);
56             let fields = Punctuated::<Field, Comma>::new();
57             let fields = fields
58                 .iter()
59                 .map(|field| {
60                     let item = Item::from_args_field(field, item.casing(), item.env_casing());
61                     (field, item)
62                 })
63                 .collect::<Vec<_>>();
64             gen_for_struct(&item, ident, &input.generics, &fields)
65         }
66         _ => abort_call_site!("`#[derive(Args)]` only supports non-tuple structs"),
67     }
68 }
69 
gen_for_struct( item: &Item, item_name: &Ident, generics: &Generics, fields: &[(&Field, Item)], ) -> TokenStream70 pub fn gen_for_struct(
71     item: &Item,
72     item_name: &Ident,
73     generics: &Generics,
74     fields: &[(&Field, Item)],
75 ) -> TokenStream {
76     if !matches!(&*item.kind(), Kind::Command(_)) {
77         abort! { item.kind().span(),
78             "`{}` cannot be used with `command`",
79             item.kind().name(),
80         }
81     }
82 
83     let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
84 
85     let constructor = gen_constructor(fields);
86     let updater = gen_updater(fields, true);
87     let raw_deprecated = raw_deprecated();
88 
89     let app_var = Ident::new("__clap_app", Span::call_site());
90     let augmentation = gen_augment(fields, &app_var, item, false);
91     let augmentation_update = gen_augment(fields, &app_var, item, true);
92 
93     let group_id = if item.skip_group() {
94         quote!(None)
95     } else {
96         let group_id = item.ident().unraw().to_string();
97         quote!(Some(clap::Id::from(#group_id)))
98     };
99 
100     quote! {
101         #[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
102         #[allow(
103             clippy::style,
104             clippy::complexity,
105             clippy::pedantic,
106             clippy::restriction,
107             clippy::perf,
108             clippy::deprecated,
109             clippy::nursery,
110             clippy::cargo,
111             clippy::suspicious_else_formatting,
112             clippy::almost_swapped,
113         )]
114         impl #impl_generics clap::FromArgMatches for #item_name #ty_generics #where_clause {
115             fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
116                 Self::from_arg_matches_mut(&mut __clap_arg_matches.clone())
117             }
118 
119             fn from_arg_matches_mut(__clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
120                 #raw_deprecated
121                 let v = #item_name #constructor;
122                 ::std::result::Result::Ok(v)
123             }
124 
125             fn update_from_arg_matches(&mut self, __clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<(), clap::Error> {
126                 self.update_from_arg_matches_mut(&mut __clap_arg_matches.clone())
127             }
128 
129             fn update_from_arg_matches_mut(&mut self, __clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<(), clap::Error> {
130                 #raw_deprecated
131                 #updater
132                 ::std::result::Result::Ok(())
133             }
134         }
135 
136         #[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
137         #[allow(
138             clippy::style,
139             clippy::complexity,
140             clippy::pedantic,
141             clippy::restriction,
142             clippy::perf,
143             clippy::deprecated,
144             clippy::nursery,
145             clippy::cargo,
146             clippy::suspicious_else_formatting,
147             clippy::almost_swapped,
148         )]
149         impl #impl_generics clap::Args for #item_name #ty_generics #where_clause {
150             fn group_id() -> Option<clap::Id> {
151                 #group_id
152             }
153             fn augment_args<'b>(#app_var: clap::Command) -> clap::Command {
154                 #augmentation
155             }
156             fn augment_args_for_update<'b>(#app_var: clap::Command) -> clap::Command {
157                 #augmentation_update
158             }
159         }
160     }
161 }
162 
163 /// Generate a block of code to add arguments/subcommands corresponding to
164 /// the `fields` to an cmd.
gen_augment( fields: &[(&Field, Item)], app_var: &Ident, parent_item: &Item, override_required: bool, ) -> TokenStream165 pub fn gen_augment(
166     fields: &[(&Field, Item)],
167     app_var: &Ident,
168     parent_item: &Item,
169     override_required: bool,
170 ) -> TokenStream {
171     let mut subcommand_specified = false;
172     let args = fields.iter().filter_map(|(field, item)| {
173         let kind = item.kind();
174         match &*kind {
175             Kind::Command(_)
176             | Kind::Value
177             | Kind::Skip(_, _)
178             | Kind::FromGlobal(_)
179             | Kind::ExternalSubcommand => None,
180             Kind::Subcommand(ty) => {
181                 if subcommand_specified {
182                     abort!(field.span(), "`#[command(subcommand)]` can only be used once per container");
183                 }
184                 subcommand_specified = true;
185 
186                 let subcmd_type = match (**ty, sub_type(&field.ty)) {
187                     (Ty::Option, Some(sub_type)) => sub_type,
188                     _ => &field.ty,
189                 };
190                 let implicit_methods = if **ty == Ty::Option {
191                     quote!()
192                 } else {
193                     quote_spanned! { kind.span()=>
194                         .subcommand_required(true)
195                         .arg_required_else_help(true)
196                     }
197                 };
198 
199                 let override_methods = if override_required {
200                     quote_spanned! { kind.span()=>
201                         .subcommand_required(false)
202                         .arg_required_else_help(false)
203                     }
204                 } else {
205                     quote!()
206                 };
207 
208                 Some(quote! {
209                     let #app_var = <#subcmd_type as clap::Subcommand>::augment_subcommands( #app_var );
210                     let #app_var = #app_var
211                         #implicit_methods
212                         #override_methods;
213                 })
214             }
215             Kind::Flatten(ty) => {
216                 let inner_type = match (**ty, sub_type(&field.ty)) {
217                     (Ty::Option, Some(sub_type)) => sub_type,
218                     _ => &field.ty,
219                 };
220 
221                 let next_help_heading = item.next_help_heading();
222                 let next_display_order = item.next_display_order();
223                 if override_required {
224                     Some(quote_spanned! { kind.span()=>
225                         let #app_var = #app_var
226                             #next_help_heading
227                             #next_display_order;
228                         let #app_var = <#inner_type as clap::Args>::augment_args_for_update(#app_var);
229                     })
230                 } else {
231                     Some(quote_spanned! { kind.span()=>
232                         let #app_var = #app_var
233                             #next_help_heading
234                             #next_display_order;
235                         let #app_var = <#inner_type as clap::Args>::augment_args(#app_var);
236                     })
237                 }
238             }
239             Kind::Arg(ty) => {
240                 let value_parser = item.value_parser(&field.ty);
241                 let action = item.action(&field.ty);
242                 let value_name = item.value_name();
243 
244                 let implicit_methods = match **ty {
245                     Ty::Unit => {
246                         // Leaving out `value_parser` as it will always fail
247                         quote_spanned! { ty.span()=>
248                             .value_name(#value_name)
249                             #action
250                         }
251                     }
252                     Ty::Option => {
253                         quote_spanned! { ty.span()=>
254                             .value_name(#value_name)
255                             #value_parser
256                             #action
257                         }
258                     }
259 
260                     Ty::OptionOption => quote_spanned! { ty.span()=>
261                         .value_name(#value_name)
262                         .num_args(0..=1)
263                         #value_parser
264                         #action
265                     },
266 
267                     Ty::OptionVec => {
268                         if item.is_positional() {
269                             quote_spanned! { ty.span()=>
270                                 .value_name(#value_name)
271                                 .num_args(1..)  // action won't be sufficient for getting multiple
272                                 #value_parser
273                                 #action
274                             }
275                         } else {
276                             quote_spanned! { ty.span()=>
277                                 .value_name(#value_name)
278                                 #value_parser
279                                 #action
280                             }
281                         }
282                     }
283 
284                     Ty::Vec => {
285                         if item.is_positional() {
286                             quote_spanned! { ty.span()=>
287                                 .value_name(#value_name)
288                                 .num_args(1..)  // action won't be sufficient for getting multiple
289                                 #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::VecVec | Ty::OptionVecVec => {
302                         quote_spanned! { ty.span() =>
303                             .value_name(#value_name)
304                             #value_parser
305                             #action
306                         }
307                     }
308 
309                     Ty::Other => {
310                         let required = item.find_default_method().is_none();
311                         // `ArgAction::takes_values` is assuming `ArgAction::default_value` will be
312                         // set though that won't always be true but this should be good enough,
313                         // otherwise we'll report an "arg required" error when unwrapping.
314                         let action_value = action.args();
315                         quote_spanned! { ty.span()=>
316                             .value_name(#value_name)
317                             .required(#required && #action_value.takes_values())
318                             #value_parser
319                             #action
320                         }
321                     }
322                 };
323 
324                 let id = item.id();
325                 let explicit_methods = item.field_methods();
326                 let deprecations = if !override_required {
327                     item.deprecations()
328                 } else {
329                     quote!()
330                 };
331                 let override_methods = if override_required {
332                     quote_spanned! { kind.span()=>
333                         .required(false)
334                     }
335                 } else {
336                     quote!()
337                 };
338 
339                 Some(quote_spanned! { field.span()=>
340                     let #app_var = #app_var.arg({
341                         #deprecations
342 
343                         #[allow(deprecated)]
344                         let arg = clap::Arg::new(#id)
345                             #implicit_methods;
346 
347                         let arg = arg
348                             #explicit_methods;
349 
350                         let arg = arg
351                             #override_methods;
352 
353                         arg
354                     });
355                 })
356             }
357         }
358     });
359 
360     let deprecations = if !override_required {
361         parent_item.deprecations()
362     } else {
363         quote!()
364     };
365     let initial_app_methods = parent_item.initial_top_level_methods();
366     let final_app_methods = parent_item.final_top_level_methods();
367     let group_app_methods = if parent_item.skip_group() {
368         quote!()
369     } else {
370         let group_id = parent_item.ident().unraw().to_string();
371         let literal_group_members = fields
372             .iter()
373             .filter_map(|(_field, item)| {
374                 let kind = item.kind();
375                 if matches!(*kind, Kind::Arg(_)) {
376                     Some(item.id())
377                 } else {
378                     None
379                 }
380             })
381             .collect::<Vec<_>>();
382         let literal_group_members_len = literal_group_members.len();
383         let mut literal_group_members = quote! {{
384             let members: [clap::Id; #literal_group_members_len] = [#( clap::Id::from(#literal_group_members) ),* ];
385             members
386         }};
387         // HACK: Validation isn't ready yet for nested arg groups, so just don't populate the group in
388         // that situation
389         let possible_group_members_len = fields
390             .iter()
391             .filter(|(_field, item)| {
392                 let kind = item.kind();
393                 matches!(*kind, Kind::Flatten(_))
394             })
395             .count();
396         if 0 < possible_group_members_len {
397             literal_group_members = quote! {{
398                 let members: [clap::Id; 0] = [];
399                 members
400             }};
401         }
402 
403         quote!(
404             .group(
405                 clap::ArgGroup::new(#group_id)
406                     .multiple(true)
407                     .args(#literal_group_members)
408             )
409         )
410     };
411     quote! {{
412         #deprecations
413         let #app_var = #app_var
414             #initial_app_methods
415             #group_app_methods
416             ;
417         #( #args )*
418         #app_var #final_app_methods
419     }}
420 }
421 
gen_constructor(fields: &[(&Field, Item)]) -> TokenStream422 pub fn gen_constructor(fields: &[(&Field, Item)]) -> TokenStream {
423     let fields = fields.iter().map(|(field, item)| {
424         let field_name = field.ident.as_ref().unwrap();
425         let kind = item.kind();
426         let arg_matches = format_ident!("__clap_arg_matches");
427         match &*kind {
428             Kind::Command(_)
429             | Kind::Value
430             | Kind::ExternalSubcommand => {
431                 abort! { kind.span(),
432                     "`{}` cannot be used with `arg`",
433                     kind.name(),
434                 }
435             }
436             Kind::Subcommand(ty) => {
437                 let subcmd_type = match (**ty, sub_type(&field.ty)) {
438                     (Ty::Option, Some(sub_type)) => sub_type,
439                     _ => &field.ty,
440                 };
441                 match **ty {
442                     Ty::Option => {
443                         quote_spanned! { kind.span()=>
444                             #field_name: {
445                                 if #arg_matches.subcommand_name().map(<#subcmd_type as clap::Subcommand>::has_subcommand).unwrap_or(false) {
446                                     Some(<#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?)
447                                 } else {
448                                     None
449                                 }
450                             }
451                         }
452                     },
453                     Ty::Other => {
454                         quote_spanned! { kind.span()=>
455                             #field_name: {
456                                 <#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?
457                             }
458                         }
459                     },
460                     Ty::Unit |
461                     Ty::Vec |
462                     Ty::OptionOption |
463                     Ty::OptionVec |
464                     Ty::VecVec |
465                     Ty::OptionVecVec => {
466                         abort!(
467                             ty.span(),
468                             "{} types are not supported for subcommand",
469                             ty.as_str()
470                         );
471                     }
472                 }
473             }
474 
475             Kind::Flatten(ty) => {
476                 let inner_type = match (**ty, sub_type(&field.ty)) {
477                     (Ty::Option, Some(sub_type)) => sub_type,
478                     _ => &field.ty,
479                 };
480                 match **ty {
481                     Ty::Other => {
482                         quote_spanned! { kind.span()=>
483                             #field_name: <#inner_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?
484                         }
485                     },
486                     Ty::Option => {
487                         quote_spanned! { kind.span()=>
488                             #field_name: {
489                                 let group_id = <#inner_type as clap::Args>::group_id()
490                                     .expect("`#[arg(flatten)]`ed field type implements `Args::group_id`");
491                                 if #arg_matches.contains_id(group_id.as_str()) {
492                                     Some(
493                                         <#inner_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?
494                                     )
495                                 } else {
496                                     None
497                                 }
498                             }
499                         }
500                     },
501                     Ty::Unit |
502                     Ty::Vec |
503                     Ty::OptionOption |
504                     Ty::OptionVec |
505                     Ty::VecVec |
506                     Ty::OptionVecVec => {
507                         abort!(
508                             ty.span(),
509                             "{} types are not supported for flatten",
510                             ty.as_str()
511                         );
512                     }
513                 }
514             },
515 
516             Kind::Skip(val, _) => match val {
517                 None => quote_spanned!(kind.span()=> #field_name: Default::default()),
518                 Some(val) => quote_spanned!(kind.span()=> #field_name: (#val).into()),
519             },
520 
521             Kind::Arg(ty) | Kind::FromGlobal(ty) => {
522                 gen_parsers(item, ty, field_name, field, None)
523             }
524         }
525     });
526 
527     quote! {{
528         #( #fields ),*
529     }}
530 }
531 
gen_updater(fields: &[(&Field, Item)], use_self: bool) -> TokenStream532 pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> TokenStream {
533     let fields = fields.iter().map(|(field, item)| {
534         let field_name = field.ident.as_ref().unwrap();
535         let kind = item.kind();
536 
537         let access = if use_self {
538             quote! {
539                 #[allow(non_snake_case)]
540                 let #field_name = &mut self.#field_name;
541             }
542         } else {
543             quote!()
544         };
545         let arg_matches = format_ident!("__clap_arg_matches");
546 
547         match &*kind {
548             Kind::Command(_)
549             | Kind::Value
550             | Kind::ExternalSubcommand => {
551                 abort! { kind.span(),
552                     "`{}` cannot be used with `arg`",
553                     kind.name(),
554                 }
555             }
556             Kind::Subcommand(ty) => {
557                 let subcmd_type = match (**ty, sub_type(&field.ty)) {
558                     (Ty::Option, Some(sub_type)) => sub_type,
559                     _ => &field.ty,
560                 };
561 
562                 let updater = quote_spanned! { ty.span()=>
563                     <#subcmd_type as clap::FromArgMatches>::update_from_arg_matches_mut(#field_name, #arg_matches)?;
564                 };
565 
566                 let updater = match **ty {
567                     Ty::Option => quote_spanned! { kind.span()=>
568                         if let Some(#field_name) = #field_name.as_mut() {
569                             #updater
570                         } else {
571                             *#field_name = Some(<#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(
572                                 #arg_matches
573                             )?);
574                         }
575                     },
576                     _ => quote_spanned! { kind.span()=>
577                         #updater
578                     },
579                 };
580 
581                 quote_spanned! { kind.span()=>
582                     {
583                         #access
584                         #updater
585                     }
586                 }
587             }
588 
589             Kind::Flatten(ty) => {
590                 let inner_type = match (**ty, sub_type(&field.ty)) {
591                     (Ty::Option, Some(sub_type)) => sub_type,
592                     _ => &field.ty,
593                 };
594 
595                 let updater = quote_spanned! { ty.span()=>
596                     <#inner_type as clap::FromArgMatches>::update_from_arg_matches_mut(#field_name, #arg_matches)?;
597                 };
598 
599                 let updater = match **ty {
600                     Ty::Option => quote_spanned! { kind.span()=>
601                         if let Some(#field_name) = #field_name.as_mut() {
602                             #updater
603                         } else {
604                             *#field_name = Some(<#inner_type as clap::FromArgMatches>::from_arg_matches_mut(
605                                 #arg_matches
606                             )?);
607                         }
608                     },
609                     _ => quote_spanned! { kind.span()=>
610                         #updater
611                     },
612                 };
613 
614                 quote_spanned! { kind.span()=>
615                     {
616                         #access
617                         #updater
618                     }
619                 }
620             },
621 
622             Kind::Skip(_, _) => quote!(),
623 
624             Kind::Arg(ty) | Kind::FromGlobal(ty) => gen_parsers(item, ty, field_name, field, Some(&access)),
625         }
626     });
627 
628     quote! {
629         #( #fields )*
630     }
631 }
632 
gen_parsers( item: &Item, ty: &Sp<Ty>, field_name: &Ident, field: &Field, update: Option<&TokenStream>, ) -> TokenStream633 fn gen_parsers(
634     item: &Item,
635     ty: &Sp<Ty>,
636     field_name: &Ident,
637     field: &Field,
638     update: Option<&TokenStream>,
639 ) -> TokenStream {
640     let span = ty.span();
641     let convert_type = inner_type(&field.ty);
642     let id = item.id();
643     let get_one = quote_spanned!(span=> remove_one::<#convert_type>);
644     let get_many = quote_spanned!(span=> remove_many::<#convert_type>);
645     let get_occurrences = quote_spanned!(span=> remove_occurrences::<#convert_type>);
646 
647     // Give this identifier the same hygiene
648     // as the `arg_matches` parameter definition. This
649     // allows us to refer to `arg_matches` within a `quote_spanned` block
650     let arg_matches = format_ident!("__clap_arg_matches");
651 
652     let field_value = match **ty {
653         Ty::Unit => {
654             quote_spanned! { ty.span()=>
655                 ()
656             }
657         }
658 
659         Ty::Option => {
660             quote_spanned! { ty.span()=>
661                 #arg_matches.#get_one(#id)
662             }
663         }
664 
665         Ty::OptionOption => quote_spanned! { ty.span()=>
666             if #arg_matches.contains_id(#id) {
667                 Some(
668                     #arg_matches.#get_one(#id)
669                 )
670             } else {
671                 None
672             }
673         },
674 
675         Ty::OptionVec => quote_spanned! { ty.span()=>
676             if #arg_matches.contains_id(#id) {
677                 Some(#arg_matches.#get_many(#id)
678                     .map(|v| v.collect::<Vec<_>>())
679                     .unwrap_or_else(Vec::new))
680             } else {
681                 None
682             }
683         },
684 
685         Ty::Vec => {
686             quote_spanned! { ty.span()=>
687                 #arg_matches.#get_many(#id)
688                     .map(|v| v.collect::<Vec<_>>())
689                     .unwrap_or_else(Vec::new)
690             }
691         }
692 
693         Ty::VecVec => quote_spanned! { ty.span()=>
694             #arg_matches.#get_occurrences(#id)
695                 .map(|g| g.map(::std::iter::Iterator::collect).collect::<Vec<Vec<_>>>())
696                 .unwrap_or_else(Vec::new)
697         },
698 
699         Ty::OptionVecVec => quote_spanned! { ty.span()=>
700             #arg_matches.#get_occurrences(#id)
701                 .map(|g| g.map(::std::iter::Iterator::collect).collect::<Vec<Vec<_>>>())
702         },
703 
704         Ty::Other => {
705             quote_spanned! { ty.span()=>
706                 #arg_matches.#get_one(#id)
707                     .ok_or_else(|| clap::Error::raw(clap::error::ErrorKind::MissingRequiredArgument, format!("The following required argument was not provided: {}", #id)))?
708             }
709         }
710     };
711 
712     if let Some(access) = update {
713         quote_spanned! { field.span()=>
714             if #arg_matches.contains_id(#id) {
715                 #access
716                 *#field_name = #field_value
717             }
718         }
719     } else {
720         quote_spanned!(field.span()=> #field_name: #field_value )
721     }
722 }
723 
724 #[cfg(feature = "raw-deprecated")]
raw_deprecated() -> TokenStream725 pub fn raw_deprecated() -> TokenStream {
726     quote! {}
727 }
728 
729 #[cfg(not(feature = "raw-deprecated"))]
raw_deprecated() -> TokenStream730 pub fn raw_deprecated() -> TokenStream {
731     quote! {
732         #![allow(deprecated)]  // Assuming any deprecation in here will be related to a deprecation in `Args`
733 
734     }
735 }
736