1 // Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8 
9 //! This crate is custom derive for `StructOpt`. It should not be used
10 //! directly. See [structopt documentation](https://docs.rs/structopt)
11 //! for the usage of `#[derive(StructOpt)]`.
12 
13 #![allow(clippy::large_enum_variant)]
14 // FIXME: remove when and if our MSRV hits 1.42
15 #![allow(clippy::match_like_matches_macro)]
16 #![forbid(unsafe_code)]
17 
18 extern crate proc_macro;
19 
20 mod attrs;
21 mod doc_comments;
22 mod parse;
23 mod spanned;
24 mod ty;
25 
26 use crate::{
27     attrs::{Attrs, CasingStyle, Kind, Name, ParserKind},
28     spanned::Sp,
29     ty::{is_simple_ty, sub_type, subty_if_name, Ty},
30 };
31 
32 use proc_macro2::{Span, TokenStream};
33 use proc_macro_error::{abort, abort_call_site, proc_macro_error, set_dummy};
34 use quote::{format_ident, quote, quote_spanned};
35 use syn::{punctuated::Punctuated, spanned::Spanned, token::Comma, *};
36 
37 /// Default casing style for generated arguments.
38 const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab;
39 
40 /// Default casing style for environment variables
41 const DEFAULT_ENV_CASING: CasingStyle = CasingStyle::ScreamingSnake;
42 
43 /// Output for the `gen_xxx()` methods were we need more than a simple stream of tokens.
44 ///
45 /// The output of a generation method is not only the stream of new tokens but also the attribute
46 /// information of the current element. These attribute information may contain valuable information
47 /// for any kind of child arguments.
48 struct GenOutput {
49     tokens: TokenStream,
50     attrs: Attrs,
51 }
52 
53 /// Generates the `StructOpt` impl.
54 #[proc_macro_derive(StructOpt, attributes(structopt))]
55 #[proc_macro_error]
structopt(input: proc_macro::TokenStream) -> proc_macro::TokenStream56 pub fn structopt(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
57     let input: DeriveInput = syn::parse(input).unwrap();
58     let gen = impl_structopt(&input);
59     gen.into()
60 }
61 
62 /// Generate a block of code to add arguments/subcommands corresponding to
63 /// the `fields` to an app.
gen_augmentation( fields: &Punctuated<Field, Comma>, app_var: &Ident, parent_attribute: &Attrs, ) -> TokenStream64 fn gen_augmentation(
65     fields: &Punctuated<Field, Comma>,
66     app_var: &Ident,
67     parent_attribute: &Attrs,
68 ) -> TokenStream {
69     let mut subcmds = fields.iter().filter_map(|field| {
70         let attrs = Attrs::from_field(
71             field,
72             Some(parent_attribute),
73             parent_attribute.casing(),
74             parent_attribute.env_casing(),
75         );
76         let kind = attrs.kind();
77         if let Kind::Subcommand(ty) = &*kind {
78             let subcmd_type = match (**ty, sub_type(&field.ty)) {
79                 (Ty::Option, Some(sub_type)) => sub_type,
80                 _ => &field.ty,
81             };
82             let required = if **ty == Ty::Option {
83                 quote!()
84             } else {
85                 quote_spanned! { kind.span()=>
86                     let #app_var = #app_var.setting(
87                         ::structopt::clap::AppSettings::SubcommandRequiredElseHelp
88                     );
89                 }
90             };
91 
92             let span = field.span();
93             let ts = quote! {
94                 let #app_var = <#subcmd_type as ::structopt::StructOptInternal>::augment_clap(
95                     #app_var
96                 );
97                 #required
98             };
99             Some((span, ts))
100         } else {
101             None
102         }
103     });
104 
105     let subcmd = subcmds.next().map(|(_, ts)| ts);
106     if let Some((span, _)) = subcmds.next() {
107         abort!(
108             span,
109             "multiple subcommand sets are not allowed, that's the second"
110         );
111     }
112 
113     let args = fields.iter().filter_map(|field| {
114         let attrs = Attrs::from_field(
115             field,
116             Some(parent_attribute),
117             parent_attribute.casing(),
118             parent_attribute.env_casing(),
119         );
120         let kind = attrs.kind();
121         match &*kind {
122             Kind::ExternalSubcommand => abort!(
123                 kind.span(),
124                 "`external_subcommand` is only allowed on enum variants"
125             ),
126             Kind::Subcommand(_) | Kind::Skip(_) => None,
127             Kind::Flatten => {
128                 let ty = &field.ty;
129                 Some(quote_spanned! { kind.span()=>
130                     let #app_var = <#ty as ::structopt::StructOptInternal>::augment_clap(#app_var);
131                     let #app_var = if <#ty as ::structopt::StructOptInternal>::is_subcommand() {
132                         #app_var.setting(::structopt::clap::AppSettings::SubcommandRequiredElseHelp)
133                     } else {
134                         #app_var
135                     };
136                 })
137             }
138             Kind::Arg(ty) => {
139                 let convert_type = match **ty {
140                     Ty::Vec | Ty::Option => sub_type(&field.ty).unwrap_or(&field.ty),
141                     Ty::OptionOption | Ty::OptionVec => {
142                         sub_type(&field.ty).and_then(sub_type).unwrap_or(&field.ty)
143                     }
144                     _ => &field.ty,
145                 };
146 
147                 let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences;
148                 let flag = *attrs.parser().kind == ParserKind::FromFlag;
149 
150                 let parser = attrs.parser();
151                 let func = &parser.func;
152                 let validator = match *parser.kind {
153                     ParserKind::TryFromStr => quote_spanned! { func.span()=>
154                         .validator(|s| {
155                             #func(s.as_str())
156                             .map(|_: #convert_type| ())
157                             .map_err(|e| e.to_string())
158                         })
159                     },
160                     ParserKind::TryFromOsStr => quote_spanned! { func.span()=>
161                         .validator_os(|s| #func(&s).map(|_: #convert_type| ()))
162                     },
163                     _ => quote!(),
164                 };
165 
166                 let modifier = match **ty {
167                     Ty::Bool => quote_spanned! { ty.span()=>
168                         .takes_value(false)
169                         .multiple(false)
170                     },
171 
172                     Ty::Option => quote_spanned! { ty.span()=>
173                         .takes_value(true)
174                         .multiple(false)
175                         #validator
176                     },
177 
178                     Ty::OptionOption => quote_spanned! { ty.span()=>
179                             .takes_value(true)
180                             .multiple(false)
181                             .min_values(0)
182                             .max_values(1)
183                             #validator
184                     },
185 
186                     Ty::OptionVec => quote_spanned! { ty.span()=>
187                         .takes_value(true)
188                         .multiple(true)
189                         .min_values(0)
190                         #validator
191                     },
192 
193                     Ty::Vec => quote_spanned! { ty.span()=>
194                         .takes_value(true)
195                         .multiple(true)
196                         #validator
197                     },
198 
199                     Ty::Other if occurrences => quote_spanned! { ty.span()=>
200                         .takes_value(false)
201                         .multiple(true)
202                     },
203 
204                     Ty::Other if flag => quote_spanned! { ty.span()=>
205                         .takes_value(false)
206                         .multiple(false)
207                     },
208 
209                     Ty::Other => {
210                         let required = !attrs.has_method("default_value");
211                         quote_spanned! { ty.span()=>
212                             .takes_value(true)
213                             .multiple(false)
214                             .required(#required)
215                             #validator
216                         }
217                     }
218                 };
219 
220                 let name = attrs.cased_name();
221                 let methods = attrs.field_methods();
222 
223                 Some(quote_spanned! { field.span()=>
224                     let #app_var = #app_var.arg(
225                         ::structopt::clap::Arg::with_name(#name)
226                             #modifier
227                             #methods
228                     );
229                 })
230             }
231         }
232     });
233 
234     let app_methods = parent_attribute.top_level_methods();
235     let version = parent_attribute.version();
236     quote! {{
237         let #app_var = #app_var#app_methods;
238         #( #args )*
239         #subcmd
240         #app_var#version
241     }}
242 }
243 
gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream244 fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream {
245     // This ident is used in several match branches below,
246     // and the `quote[_spanned]` invocations have different spans.
247     //
248     // Given that this ident is used in several places and
249     // that the branches are located inside of a loop, it is possible that
250     // this ident will be given _different_ spans in different places, and
251     // thus will not be the _same_ ident anymore. To make sure the `matches`
252     // is always the same, we factor it out.
253     let matches = format_ident!("matches");
254 
255     let fields = fields.iter().map(|field| {
256         let attrs = Attrs::from_field(
257             field,
258             Some(parent_attribute),
259             parent_attribute.casing(),
260             parent_attribute.env_casing(),
261         );
262         let field_name = field.ident.as_ref().unwrap();
263         let kind = attrs.kind();
264         match &*kind {
265             Kind::ExternalSubcommand => abort!(
266                 kind.span(),
267                 "`external_subcommand` is allowed only on enum variants"
268             ),
269 
270             Kind::Subcommand(ty) => {
271                 let subcmd_type = match (**ty, sub_type(&field.ty)) {
272                     (Ty::Option, Some(sub_type)) => sub_type,
273                     _ => &field.ty,
274                 };
275                 let unwrapper = match **ty {
276                     Ty::Option => quote!(),
277                     _ => quote_spanned!( ty.span()=> .unwrap() ),
278                 };
279                 quote_spanned! { kind.span()=>
280                     #field_name: <#subcmd_type as ::structopt::StructOptInternal>::from_subcommand(
281                         #matches.subcommand())
282                         #unwrapper
283                 }
284             }
285 
286             Kind::Flatten => quote_spanned! { kind.span()=>
287                 #field_name: ::structopt::StructOpt::from_clap(#matches)
288             },
289 
290             Kind::Skip(val) => match val {
291                 None => quote_spanned!(kind.span()=> #field_name: Default::default()),
292                 Some(val) => quote_spanned!(kind.span()=> #field_name: (#val).into()),
293             },
294 
295             Kind::Arg(ty) => {
296                 use crate::attrs::ParserKind::*;
297 
298                 let parser = attrs.parser();
299                 let func = &parser.func;
300                 let span = parser.kind.span();
301                 let (value_of, values_of, parse) = match *parser.kind {
302                     FromStr => (
303                         quote_spanned!(span=> value_of),
304                         quote_spanned!(span=> values_of),
305                         func.clone(),
306                     ),
307                     TryFromStr => (
308                         quote_spanned!(span=> value_of),
309                         quote_spanned!(span=> values_of),
310                         quote_spanned!(func.span()=> |s| #func(s).unwrap()),
311                     ),
312                     FromOsStr => (
313                         quote_spanned!(span=> value_of_os),
314                         quote_spanned!(span=> values_of_os),
315                         func.clone(),
316                     ),
317                     TryFromOsStr => (
318                         quote_spanned!(span=> value_of_os),
319                         quote_spanned!(span=> values_of_os),
320                         quote_spanned!(func.span()=> |s| #func(s).unwrap()),
321                     ),
322                     FromOccurrences => (
323                         quote_spanned!(span=> occurrences_of),
324                         quote!(),
325                         func.clone(),
326                     ),
327                     FromFlag => (quote!(), quote!(), func.clone()),
328                 };
329 
330                 let flag = *attrs.parser().kind == ParserKind::FromFlag;
331                 let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences;
332                 let name = attrs.cased_name();
333                 let convert_type = match **ty {
334                     Ty::Vec | Ty::Option => sub_type(&field.ty).unwrap_or(&field.ty),
335                     Ty::OptionOption | Ty::OptionVec => {
336                         sub_type(&field.ty).and_then(sub_type).unwrap_or(&field.ty)
337                     }
338                     _ => &field.ty,
339                 };
340                 let field_value = match **ty {
341                     Ty::Bool => quote_spanned!(ty.span()=> #matches.is_present(#name)),
342 
343                     Ty::Option => quote_spanned! { ty.span()=>
344                         #matches.#value_of(#name)
345                             .map(#parse)
346                     },
347 
348                     Ty::OptionOption => quote_spanned! { ty.span()=>
349                         if #matches.is_present(#name) {
350                             Some(#matches.#value_of(#name).map(#parse))
351                         } else {
352                             None
353                         }
354                     },
355 
356                     Ty::OptionVec => quote_spanned! { ty.span()=>
357                         if #matches.is_present(#name) {
358                             Some(#matches.#values_of(#name)
359                                  .map_or_else(Vec::new, |v| v.map::<#convert_type, _>(#parse).collect()))
360                         } else {
361                             None
362                         }
363                     },
364 
365                     Ty::Vec => quote_spanned! { ty.span()=>
366                         #matches.#values_of(#name)
367                             .map_or_else(Vec::new, |v| v.map::<#convert_type, _>(#parse).collect())
368                     },
369 
370                     Ty::Other if occurrences => quote_spanned! { ty.span()=>
371                         #parse(#matches.#value_of(#name))
372                     },
373 
374                     Ty::Other if flag => quote_spanned! { ty.span()=>
375                         #parse(#matches.is_present(#name))
376                     },
377 
378                     Ty::Other => quote_spanned! { ty.span()=>
379                         #matches.#value_of(#name)
380                             .map(#parse)
381                             .unwrap()
382                     },
383                 };
384 
385                 quote_spanned!(field.span()=> #field_name: #field_value )
386             }
387         }
388     });
389 
390     quote! {{
391         #( #fields ),*
392     }}
393 }
394 
gen_from_clap( struct_name: &Ident, fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs, ) -> TokenStream395 fn gen_from_clap(
396     struct_name: &Ident,
397     fields: &Punctuated<Field, Comma>,
398     parent_attribute: &Attrs,
399 ) -> TokenStream {
400     let field_block = gen_constructor(fields, parent_attribute);
401 
402     quote! {
403         fn from_clap(matches: &::structopt::clap::ArgMatches) -> Self {
404             #struct_name #field_block
405         }
406     }
407 }
408 
gen_clap(attrs: &[Attribute]) -> GenOutput409 fn gen_clap(attrs: &[Attribute]) -> GenOutput {
410     let name = std::env::var("CARGO_PKG_NAME").ok().unwrap_or_default();
411 
412     let attrs = Attrs::from_struct(
413         Span::call_site(),
414         attrs,
415         Name::Assigned(quote!(#name)),
416         None,
417         Sp::call_site(DEFAULT_CASING),
418         Sp::call_site(DEFAULT_ENV_CASING),
419         false,
420     );
421     let tokens = {
422         let name = attrs.cased_name();
423         quote!(::structopt::clap::App::new(#name))
424     };
425 
426     GenOutput { tokens, attrs }
427 }
428 
gen_clap_struct(struct_attrs: &[Attribute]) -> GenOutput429 fn gen_clap_struct(struct_attrs: &[Attribute]) -> GenOutput {
430     let initial_clap_app_gen = gen_clap(struct_attrs);
431     let clap_tokens = initial_clap_app_gen.tokens;
432 
433     let augmented_tokens = quote! {
434         fn clap<'a, 'b>() -> ::structopt::clap::App<'a, 'b> {
435             let app = #clap_tokens;
436             <Self as ::structopt::StructOptInternal>::augment_clap(app)
437         }
438     };
439 
440     GenOutput {
441         tokens: augmented_tokens,
442         attrs: initial_clap_app_gen.attrs,
443     }
444 }
445 
gen_augment_clap(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream446 fn gen_augment_clap(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream {
447     let app_var = Ident::new("app", Span::call_site());
448     let augmentation = gen_augmentation(fields, &app_var, parent_attribute);
449     quote! {
450         fn augment_clap<'a, 'b>(
451             #app_var: ::structopt::clap::App<'a, 'b>
452         ) -> ::structopt::clap::App<'a, 'b> {
453             #augmentation
454         }
455     }
456 }
457 
gen_clap_enum(enum_attrs: &[Attribute]) -> GenOutput458 fn gen_clap_enum(enum_attrs: &[Attribute]) -> GenOutput {
459     let initial_clap_app_gen = gen_clap(enum_attrs);
460     let clap_tokens = initial_clap_app_gen.tokens;
461 
462     let tokens = quote! {
463         fn clap<'a, 'b>() -> ::structopt::clap::App<'a, 'b> {
464             let app = #clap_tokens
465                 .setting(::structopt::clap::AppSettings::SubcommandRequiredElseHelp);
466             <Self as ::structopt::StructOptInternal>::augment_clap(app)
467         }
468     };
469 
470     GenOutput {
471         tokens,
472         attrs: initial_clap_app_gen.attrs,
473     }
474 }
475 
gen_augment_clap_enum( variants: &Punctuated<Variant, Comma>, parent_attribute: &Attrs, ) -> TokenStream476 fn gen_augment_clap_enum(
477     variants: &Punctuated<Variant, Comma>,
478     parent_attribute: &Attrs,
479 ) -> TokenStream {
480     use syn::Fields::*;
481 
482     let subcommands = variants.iter().filter_map(|variant| {
483         let attrs = Attrs::from_struct(
484             variant.span(),
485             &variant.attrs,
486             Name::Derived(variant.ident.clone()),
487             Some(parent_attribute),
488             parent_attribute.casing(),
489             parent_attribute.env_casing(),
490             true,
491         );
492 
493         let kind = attrs.kind();
494         match &*kind {
495             Kind::Skip(_) => None,
496 
497             Kind::ExternalSubcommand => {
498                 let app_var = Ident::new("app", Span::call_site());
499                 Some(quote_spanned! { attrs.kind().span()=>
500                     let #app_var = #app_var.setting(
501                         ::structopt::clap::AppSettings::AllowExternalSubcommands
502                     );
503                 })
504             },
505 
506             Kind::Flatten => {
507                 match variant.fields {
508                     Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
509                         let ty = &unnamed[0];
510                         Some(quote! {
511                             let app = <#ty as ::structopt::StructOptInternal>::augment_clap(app);
512                         })
513                     },
514                     _ => abort!(
515                         variant,
516                         "`flatten` is usable only with single-typed tuple variants"
517                     ),
518                 }
519             },
520 
521             _ => {
522                 let app_var = Ident::new("subcommand", Span::call_site());
523                 let from_attrs = attrs.top_level_methods();
524                 let version = attrs.version();
525 
526                 let arg_block = match variant.fields {
527                     // If the variant is named, then gen_augmentation already generates the
528                     // top level methods (#from_attrs) and version.
529                     Named(ref fields) => gen_augmentation(&fields.named, &app_var, &attrs),
530                     Unit => quote!( #app_var#from_attrs#version ),
531                     Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
532                         let ty = &unnamed[0];
533                         quote_spanned! { ty.span()=>
534                             {
535                                 let #app_var = <#ty as ::structopt::StructOptInternal>::augment_clap(
536                                     #app_var
537                                 );
538                                 if <#ty as ::structopt::StructOptInternal>::is_subcommand() {
539                                     #app_var.setting(
540                                         ::structopt::clap::AppSettings::SubcommandRequiredElseHelp
541                                     )
542                                 } else {
543                                     #app_var
544                                 }#from_attrs#version
545                             }
546                         }
547                     }
548                     Unnamed(..) => abort!(variant, "non single-typed tuple enums are not supported"),
549                 };
550 
551                 let name = attrs.cased_name();
552                 Some(quote! {
553                     let app = app.subcommand({
554                         let #app_var = ::structopt::clap::SubCommand::with_name(#name);
555                         #arg_block
556                     });
557                 })
558             },
559         }
560     });
561 
562     let app_methods = parent_attribute.top_level_methods();
563     let version = parent_attribute.version();
564     quote! {
565         fn augment_clap<'a, 'b>(
566             app: ::structopt::clap::App<'a, 'b>
567         ) -> ::structopt::clap::App<'a, 'b> {
568             let app = app #app_methods;
569             #( #subcommands )*;
570             app #version
571         }
572     }
573 }
574 
gen_from_clap_enum() -> TokenStream575 fn gen_from_clap_enum() -> TokenStream {
576     quote! {
577         fn from_clap(matches: &::structopt::clap::ArgMatches) -> Self {
578             <Self as ::structopt::StructOptInternal>::from_subcommand(matches.subcommand())
579                 .expect("structopt misuse: You likely tried to #[flatten] a struct \
580                          that contains #[subcommand]. This is forbidden.")
581         }
582     }
583 }
584 
gen_from_subcommand( name: &Ident, variants: &Punctuated<Variant, Comma>, parent_attribute: &Attrs, ) -> TokenStream585 fn gen_from_subcommand(
586     name: &Ident,
587     variants: &Punctuated<Variant, Comma>,
588     parent_attribute: &Attrs,
589 ) -> TokenStream {
590     use syn::Fields::*;
591 
592     let mut ext_subcmd = None;
593 
594     let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
595         .iter()
596         .filter_map(|variant| {
597             let attrs = Attrs::from_struct(
598                 variant.span(),
599                 &variant.attrs,
600                 Name::Derived(variant.ident.clone()),
601                 Some(parent_attribute),
602                 parent_attribute.casing(),
603                 parent_attribute.env_casing(),
604                 true,
605             );
606 
607             let variant_name = &variant.ident;
608 
609             match *attrs.kind() {
610                 Kind::ExternalSubcommand => {
611                     if ext_subcmd.is_some() {
612                         abort!(
613                             attrs.kind().span(),
614                             "Only one variant can be marked with `external_subcommand`, \
615                          this is the second"
616                         );
617                     }
618 
619                     let ty = match variant.fields {
620                         Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
621 
622                         _ => abort!(
623                             variant,
624                             "The enum variant marked with `external_attribute` must be \
625                          a single-typed tuple, and the type must be either `Vec<String>` \
626                          or `Vec<OsString>`."
627                         ),
628                     };
629 
630                     let (span, str_ty, values_of) = match subty_if_name(ty, "Vec") {
631                         Some(subty) => {
632                             if is_simple_ty(subty, "String") {
633                                 (
634                                     subty.span(),
635                                     quote!(::std::string::String),
636                                     quote!(values_of),
637                                 )
638                             } else {
639                                 (
640                                     subty.span(),
641                                     quote!(::std::ffi::OsString),
642                                     quote!(values_of_os),
643                                 )
644                             }
645                         }
646 
647                         None => abort!(
648                             ty,
649                             "The type must be either `Vec<String>` or `Vec<OsString>` \
650                          to be used with `external_subcommand`."
651                         ),
652                     };
653 
654                     ext_subcmd = Some((span, variant_name, str_ty, values_of));
655                     None
656                 }
657                 Kind::Skip(_) => None,
658                 _ => Some((variant, attrs)),
659             }
660         })
661         .partition(|(_, attrs)| match &*attrs.kind() {
662             Kind::Flatten => true,
663             _ => false,
664         });
665 
666     let other = format_ident!("other");
667     let matches = format_ident!("matches");
668 
669     let external = match ext_subcmd {
670         Some((span, var_name, str_ty, values_of)) => quote_spanned! { span=>
671             match #other {
672                 ("", ::std::option::Option::None) => None,
673 
674                 (external, Some(#matches)) => {
675                     ::std::option::Option::Some(#name::#var_name(
676                         ::std::iter::once(#str_ty::from(external))
677                         .chain(
678                             #matches.#values_of("").into_iter().flatten().map(#str_ty::from)
679                         )
680                         .collect::<::std::vec::Vec<_>>()
681                     ))
682                 }
683 
684                 (external, None) => {
685                     ::std::option::Option::Some(#name::#var_name(
686                         ::std::iter::once(#str_ty::from(external))
687                             .collect::<::std::vec::Vec<_>>()
688                     ))
689                 }
690             }
691         },
692 
693         None => quote!(None),
694     };
695 
696     let match_arms = variants.iter().map(|(variant, attrs)| {
697         let sub_name = attrs.cased_name();
698         let variant_name = &variant.ident;
699         let constructor_block = match variant.fields {
700             Named(ref fields) => gen_constructor(&fields.named, &attrs),
701             Unit => quote!(),
702             Unnamed(ref fields) if fields.unnamed.len() == 1 => {
703                 let ty = &fields.unnamed[0];
704                 quote!( ( <#ty as ::structopt::StructOpt>::from_clap(#matches) ) )
705             }
706             Unnamed(..) => abort!(
707                 variant.ident,
708                 "non single-typed tuple enums are not supported"
709             ),
710         };
711 
712         quote! {
713             (#sub_name, Some(#matches)) => {
714                 Some(#name :: #variant_name #constructor_block)
715             }
716         }
717     });
718 
719     let child_subcommands = flatten_variants.iter().map(|(variant, _attrs)| {
720         let variant_name = &variant.ident;
721         match variant.fields {
722             Unnamed(ref fields) if fields.unnamed.len() == 1 => {
723                 let ty = &fields.unnamed[0];
724                 quote! {
725                     if let Some(res) =
726                         <#ty as ::structopt::StructOptInternal>::from_subcommand(#other)
727                     {
728                         return Some(#name :: #variant_name (res));
729                     }
730                 }
731             }
732             _ => abort!(
733                 variant,
734                 "`flatten` is usable only with single-typed tuple variants"
735             ),
736         }
737     });
738 
739     quote! {
740         fn from_subcommand<'a, 'b>(
741             sub: (&'b str, Option<&'b ::structopt::clap::ArgMatches<'a>>)
742         ) -> Option<Self> {
743             match sub {
744                 #( #match_arms, )*
745                 #other => {
746                     #( #child_subcommands )else*;
747                     #external
748                 }
749             }
750         }
751     }
752 }
753 
754 #[cfg(feature = "paw")]
gen_paw_impl( impl_generics: &ImplGenerics, name: &Ident, ty_generics: &TypeGenerics, where_clause: &TokenStream, ) -> TokenStream755 fn gen_paw_impl(
756     impl_generics: &ImplGenerics,
757     name: &Ident,
758     ty_generics: &TypeGenerics,
759     where_clause: &TokenStream,
760 ) -> TokenStream {
761     quote! {
762         impl #impl_generics ::structopt::paw::ParseArgs for #name #ty_generics #where_clause {
763             type Error = std::io::Error;
764 
765             fn parse_args() -> std::result::Result<Self, Self::Error> {
766                 Ok(<#name as ::structopt::StructOpt>::from_args())
767             }
768         }
769     }
770 }
771 #[cfg(not(feature = "paw"))]
gen_paw_impl(_: &ImplGenerics, _: &Ident, _: &TypeGenerics, _: &TokenStream) -> TokenStream772 fn gen_paw_impl(_: &ImplGenerics, _: &Ident, _: &TypeGenerics, _: &TokenStream) -> TokenStream {
773     TokenStream::new()
774 }
775 
split_structopt_generics_for_impl( generics: &Generics, ) -> (ImplGenerics, TypeGenerics, TokenStream)776 fn split_structopt_generics_for_impl(
777     generics: &Generics,
778 ) -> (ImplGenerics, TypeGenerics, TokenStream) {
779     use syn::{token::Add, TypeParamBound::Trait};
780 
781     fn path_ends_with(path: &Path, ident: &str) -> bool {
782         path.segments.last().unwrap().ident == ident
783     }
784 
785     fn type_param_bounds_contains(bounds: &Punctuated<TypeParamBound, Add>, ident: &str) -> bool {
786         for bound in bounds {
787             if let Trait(bound) = bound {
788                 if path_ends_with(&bound.path, ident) {
789                     return true;
790                 }
791             }
792         }
793         return false;
794     }
795 
796     struct TraitBoundAmendments {
797         tokens: TokenStream,
798         need_where: bool,
799         need_comma: bool,
800     }
801 
802     impl TraitBoundAmendments {
803         fn new(where_clause: Option<&WhereClause>) -> Self {
804             let tokens = TokenStream::new();
805             let (need_where, need_comma) = if let Some(where_clause) = where_clause {
806                 if where_clause.predicates.trailing_punct() {
807                     (false, false)
808                 } else {
809                     (false, true)
810                 }
811             } else {
812                 (true, false)
813             };
814             Self {
815                 tokens,
816                 need_where,
817                 need_comma,
818             }
819         }
820 
821         fn add(&mut self, amendment: TokenStream) {
822             if self.need_where {
823                 self.tokens.extend(quote! { where });
824                 self.need_where = false;
825             }
826             if self.need_comma {
827                 self.tokens.extend(quote! { , });
828             }
829             self.tokens.extend(amendment);
830             self.need_comma = true;
831         }
832 
833         fn into_tokens(self) -> TokenStream {
834             self.tokens
835         }
836     }
837 
838     let mut trait_bound_amendments = TraitBoundAmendments::new(generics.where_clause.as_ref());
839 
840     for param in &generics.params {
841         if let GenericParam::Type(param) = param {
842             let param_ident = &param.ident;
843             if type_param_bounds_contains(&param.bounds, "StructOpt") {
844                 trait_bound_amendments
845                     .add(quote! { #param_ident : ::structopt::StructOptInternal });
846             }
847         }
848     }
849 
850     if let Some(where_clause) = &generics.where_clause {
851         for predicate in &where_clause.predicates {
852             if let WherePredicate::Type(predicate) = predicate {
853                 let predicate_bounded_ty = &predicate.bounded_ty;
854                 if type_param_bounds_contains(&predicate.bounds, "StructOpt") {
855                     trait_bound_amendments
856                         .add(quote! { #predicate_bounded_ty : ::structopt::StructOptInternal });
857                 }
858             }
859         }
860     }
861 
862     let trait_bound_amendments = trait_bound_amendments.into_tokens();
863 
864     let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
865 
866     let where_clause = quote! { #where_clause #trait_bound_amendments };
867 
868     (impl_generics, ty_generics, where_clause)
869 }
870 
impl_structopt_for_struct( name: &Ident, fields: &Punctuated<Field, Comma>, attrs: &[Attribute], generics: &Generics, ) -> TokenStream871 fn impl_structopt_for_struct(
872     name: &Ident,
873     fields: &Punctuated<Field, Comma>,
874     attrs: &[Attribute],
875     generics: &Generics,
876 ) -> TokenStream {
877     let (impl_generics, ty_generics, where_clause) = split_structopt_generics_for_impl(&generics);
878 
879     let basic_clap_app_gen = gen_clap_struct(attrs);
880     let augment_clap = gen_augment_clap(fields, &basic_clap_app_gen.attrs);
881     let from_clap = gen_from_clap(name, fields, &basic_clap_app_gen.attrs);
882     let paw_impl = gen_paw_impl(&impl_generics, name, &ty_generics, &where_clause);
883 
884     let clap_tokens = basic_clap_app_gen.tokens;
885     quote! {
886         #[allow(unused_variables)]
887         #[allow(unknown_lints)]
888         #[allow(
889             clippy::style,
890             clippy::complexity,
891             clippy::pedantic,
892             clippy::restriction,
893             clippy::perf,
894             clippy::deprecated,
895             clippy::nursery,
896             clippy::cargo
897         )]
898         #[deny(clippy::correctness)]
899         #[allow(dead_code, unreachable_code)]
900         impl #impl_generics ::structopt::StructOpt for #name #ty_generics #where_clause {
901             #clap_tokens
902             #from_clap
903         }
904 
905         #[allow(unused_variables)]
906         #[allow(unknown_lints)]
907         #[allow(
908             clippy::style,
909             clippy::complexity,
910             clippy::pedantic,
911             clippy::restriction,
912             clippy::perf,
913             clippy::deprecated,
914             clippy::nursery,
915             clippy::cargo
916         )]
917         #[deny(clippy::correctness)]
918         #[allow(dead_code, unreachable_code)]
919         impl #impl_generics ::structopt::StructOptInternal for #name #ty_generics #where_clause {
920             #augment_clap
921             fn is_subcommand() -> bool { false }
922         }
923 
924         #paw_impl
925     }
926 }
927 
impl_structopt_for_enum( name: &Ident, variants: &Punctuated<Variant, Comma>, attrs: &[Attribute], generics: &Generics, ) -> TokenStream928 fn impl_structopt_for_enum(
929     name: &Ident,
930     variants: &Punctuated<Variant, Comma>,
931     attrs: &[Attribute],
932     generics: &Generics,
933 ) -> TokenStream {
934     let (impl_generics, ty_generics, where_clause) = split_structopt_generics_for_impl(&generics);
935 
936     let basic_clap_app_gen = gen_clap_enum(attrs);
937     let clap_tokens = basic_clap_app_gen.tokens;
938     let attrs = basic_clap_app_gen.attrs;
939 
940     let augment_clap = gen_augment_clap_enum(variants, &attrs);
941     let from_clap = gen_from_clap_enum();
942     let from_subcommand = gen_from_subcommand(name, variants, &attrs);
943     let paw_impl = gen_paw_impl(&impl_generics, name, &ty_generics, &where_clause);
944 
945     quote! {
946         #[allow(unknown_lints)]
947         #[allow(unused_variables, dead_code, unreachable_code)]
948         #[allow(
949             clippy::style,
950             clippy::complexity,
951             clippy::pedantic,
952             clippy::restriction,
953             clippy::perf,
954             clippy::deprecated,
955             clippy::nursery,
956             clippy::cargo
957         )]
958         #[deny(clippy::correctness)]
959         impl #impl_generics ::structopt::StructOpt for #name #ty_generics #where_clause {
960             #clap_tokens
961             #from_clap
962         }
963 
964         #[allow(unused_variables)]
965         #[allow(unknown_lints)]
966         #[allow(
967             clippy::style,
968             clippy::complexity,
969             clippy::pedantic,
970             clippy::restriction,
971             clippy::perf,
972             clippy::deprecated,
973             clippy::nursery,
974             clippy::cargo
975         )]
976         #[deny(clippy::correctness)]
977         #[allow(dead_code, unreachable_code)]
978         impl #impl_generics ::structopt::StructOptInternal for #name #ty_generics #where_clause {
979             #augment_clap
980             #from_subcommand
981             fn is_subcommand() -> bool { true }
982         }
983 
984         #paw_impl
985     }
986 }
987 
impl_structopt(input: &DeriveInput) -> TokenStream988 fn impl_structopt(input: &DeriveInput) -> TokenStream {
989     use syn::Data::*;
990 
991     let struct_name = &input.ident;
992 
993     set_dummy(quote! {
994         impl ::structopt::StructOpt for #struct_name {
995             fn clap<'a, 'b>() -> ::structopt::clap::App<'a, 'b> {
996                 unimplemented!()
997             }
998             fn from_clap(_matches: &::structopt::clap::ArgMatches) -> Self {
999                 unimplemented!()
1000             }
1001         }
1002 
1003         impl ::structopt::StructOptInternal for #struct_name {}
1004     });
1005 
1006     match input.data {
1007         Struct(DataStruct {
1008             fields: syn::Fields::Named(ref fields),
1009             ..
1010         }) => impl_structopt_for_struct(struct_name, &fields.named, &input.attrs, &input.generics),
1011         Enum(ref e) => {
1012             impl_structopt_for_enum(struct_name, &e.variants, &input.attrs, &input.generics)
1013         }
1014         _ => abort_call_site!("structopt only supports non-tuple structs and enums"),
1015     }
1016 }
1017