indigo_structopt_derive/
lib.rs

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
18extern crate proc_macro;
19
20mod attrs;
21mod doc_comments;
22mod parse;
23mod spanned;
24mod ty;
25
26use crate::{
27  attrs::{Attrs, CasingStyle, Kind, Name, ParserKind},
28  spanned::Sp,
29  ty::{is_simple_ty, sub_type, subty_if_name, Ty},
30};
31
32use proc_macro2::{Span, TokenStream};
33use proc_macro_error::{abort, abort_call_site, proc_macro_error, set_dummy};
34use quote::{format_ident, quote, quote_spanned};
35use syn::{punctuated::Punctuated, spanned::Spanned, token::Comma, *};
36
37/// Default casing style for generated arguments.
38const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab;
39
40/// Default casing style for environment variables
41const 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.
48struct 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]
56pub 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.
64fn 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!(span, "multiple subcommand sets are not allowed, that's the second");
108  }
109
110  let args = fields.iter().filter_map(|field| {
111    let attrs = Attrs::from_field(
112      field,
113      Some(parent_attribute),
114      parent_attribute.casing(),
115      parent_attribute.env_casing(),
116    );
117    let kind = attrs.kind();
118    match &*kind {
119      Kind::ExternalSubcommand => {
120        abort!(kind.span(), "`external_subcommand` is only allowed on enum variants")
121      }
122      Kind::Subcommand(_) | Kind::Skip(_) => None,
123      Kind::Flatten => {
124        let ty = &field.ty;
125        Some(quote_spanned! { kind.span()=>
126            let #app_var = <#ty as structopt::StructOptInternal>::augment_clap(#app_var);
127            let #app_var = if <#ty as structopt::StructOptInternal>::is_subcommand() {
128                #app_var.setting(structopt::clap::AppSettings::SubcommandRequiredElseHelp)
129            } else {
130                #app_var
131            };
132        })
133      }
134      Kind::Arg(ty) => {
135        let convert_type = match **ty {
136          Ty::Vec | Ty::Option => sub_type(&field.ty).unwrap_or(&field.ty),
137          Ty::OptionOption | Ty::OptionVec => {
138            sub_type(&field.ty).and_then(sub_type).unwrap_or(&field.ty)
139          }
140          _ => &field.ty,
141        };
142
143        let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences;
144        let flag = *attrs.parser().kind == ParserKind::FromFlag;
145
146        let parser = attrs.parser();
147        let func = &parser.func;
148        let validator = match *parser.kind {
149          ParserKind::TryFromStr => quote_spanned! { func.span()=>
150              .validator(|s| {
151                  #func(s.as_str())
152                  .map(|_: #convert_type| ())
153                  .map_err(|e| e.to_string())
154              })
155          },
156          ParserKind::TryFromOsStr => quote_spanned! { func.span()=>
157              .validator_os(|s| #func(&s).map(|_: #convert_type| ()))
158          },
159          _ => quote!(),
160        };
161
162        let modifier = match **ty {
163          Ty::Bool => quote_spanned! { ty.span()=>
164              .takes_value(false)
165              .multiple(false)
166          },
167
168          Ty::Option => quote_spanned! { ty.span()=>
169              .takes_value(true)
170              .multiple(false)
171              #validator
172          },
173
174          Ty::OptionOption => quote_spanned! { ty.span()=>
175                  .takes_value(true)
176                  .multiple(false)
177                  .min_values(0)
178                  .max_values(1)
179                  #validator
180          },
181
182          Ty::OptionVec => quote_spanned! { ty.span()=>
183              .takes_value(true)
184              .multiple(true)
185              .min_values(0)
186              #validator
187          },
188
189          Ty::Vec => quote_spanned! { ty.span()=>
190              .takes_value(true)
191              .multiple(true)
192              #validator
193          },
194
195          Ty::Other if occurrences => quote_spanned! { ty.span()=>
196              .takes_value(false)
197              .multiple(true)
198          },
199
200          Ty::Other if flag => quote_spanned! { ty.span()=>
201              .takes_value(false)
202              .multiple(false)
203          },
204
205          Ty::Other => {
206            let required = !attrs.has_method("default_value");
207            quote_spanned! { ty.span()=>
208                .takes_value(true)
209                .multiple(false)
210                .required(#required)
211                #validator
212            }
213          }
214        };
215
216        let name = attrs.cased_name();
217        let methods = attrs.field_methods();
218
219        Some(quote_spanned! { field.span()=>
220            let #app_var = #app_var.arg(
221                structopt::clap::Arg::with_name(#name)
222                    #modifier
223                    #methods
224            );
225        })
226      }
227    }
228  });
229
230  let app_methods = parent_attribute.top_level_methods();
231  let version = parent_attribute.version();
232  quote! {{
233      let #app_var = #app_var#app_methods;
234      #( #args )*
235      #subcmd
236      #app_var#version
237  }}
238}
239
240fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream {
241  // This ident is used in several match branches below,
242  // and the `quote[_spanned]` invocations have different spans.
243  //
244  // Given that this ident is used in several places and
245  // that the branches are located inside of a loop, it is possible that
246  // this ident will be given _different_ spans in different places, and
247  // thus will not be the _same_ ident anymore. To make sure the `matches`
248  // is always the same, we factor it out.
249  let matches = format_ident!("matches");
250
251  let fields = fields.iter().map(|field| {
252    let attrs = Attrs::from_field(
253      field,
254      Some(parent_attribute),
255      parent_attribute.casing(),
256      parent_attribute.env_casing(),
257    );
258    let field_name = field.ident.as_ref().unwrap();
259    let kind = attrs.kind();
260    match &*kind {
261      Kind::ExternalSubcommand => {
262        abort!(kind.span(), "`external_subcommand` is allowed only on enum variants")
263      }
264
265      Kind::Subcommand(ty) => {
266        let subcmd_type = match (**ty, sub_type(&field.ty)) {
267          (Ty::Option, Some(sub_type)) => sub_type,
268          _ => &field.ty,
269        };
270        let unwrapper = match **ty {
271          Ty::Option => quote!(),
272          _ => quote_spanned!( ty.span()=> .unwrap() ),
273        };
274        quote_spanned! { kind.span()=>
275            #field_name: <#subcmd_type as structopt::StructOptInternal>::from_subcommand(
276                #matches.subcommand())
277                #unwrapper
278        }
279      }
280
281      Kind::Flatten => quote_spanned! { kind.span()=>
282          #field_name: structopt::StructOpt::from_clap(#matches)
283      },
284
285      Kind::Skip(val) => match val {
286        None => quote_spanned!(kind.span()=> #field_name: Default::default()),
287        Some(val) => quote_spanned!(kind.span()=> #field_name: (#val).into()),
288      },
289
290      Kind::Arg(ty) => {
291        use crate::attrs::ParserKind::*;
292
293        let parser = attrs.parser();
294        let func = &parser.func;
295        let span = parser.kind.span();
296        let (value_of, values_of, parse) = match *parser.kind {
297          FromStr => {
298            (quote_spanned!(span=> value_of), quote_spanned!(span=> values_of), func.clone())
299          }
300          TryFromStr => (
301            quote_spanned!(span=> value_of),
302            quote_spanned!(span=> values_of),
303            quote_spanned!(func.span()=> |s| #func(s).unwrap()),
304          ),
305          FromOsStr => {
306            (quote_spanned!(span=> value_of_os), quote_spanned!(span=> values_of_os), func.clone())
307          }
308          TryFromOsStr => (
309            quote_spanned!(span=> value_of_os),
310            quote_spanned!(span=> values_of_os),
311            quote_spanned!(func.span()=> |s| #func(s).unwrap()),
312          ),
313          FromOccurrences => (quote_spanned!(span=> occurrences_of), quote!(), func.clone()),
314          FromFlag => (quote!(), quote!(), func.clone()),
315        };
316
317        let flag = *attrs.parser().kind == ParserKind::FromFlag;
318        let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences;
319        let name = attrs.cased_name();
320        let field_value = match **ty {
321          Ty::Bool => quote_spanned!(ty.span()=> #matches.is_present(#name)),
322
323          Ty::Option => quote_spanned! { ty.span()=>
324              #matches.#value_of(#name)
325                  .map(#parse)
326          },
327
328          Ty::OptionOption => quote_spanned! { ty.span()=>
329              if #matches.is_present(#name) {
330                  Some(#matches.#value_of(#name).map(#parse))
331              } else {
332                  None
333              }
334          },
335
336          Ty::OptionVec => quote_spanned! { ty.span()=>
337              if #matches.is_present(#name) {
338                  Some(#matches.#values_of(#name)
339                       .map_or_else(Vec::new, |v| v.map(#parse).collect()))
340              } else {
341                  None
342              }
343          },
344
345          Ty::Vec => quote_spanned! { ty.span()=>
346              #matches.#values_of(#name)
347                  .map_or_else(Vec::new, |v| v.map(#parse).collect())
348          },
349
350          Ty::Other if occurrences => quote_spanned! { ty.span()=>
351              #parse(#matches.#value_of(#name))
352          },
353
354          Ty::Other if flag => quote_spanned! { ty.span()=>
355              #parse(#matches.is_present(#name))
356          },
357
358          Ty::Other => quote_spanned! { ty.span()=>
359              #matches.#value_of(#name)
360                  .map(#parse)
361                  .unwrap()
362          },
363        };
364
365        quote_spanned!(field.span()=> #field_name: #field_value )
366      }
367    }
368  });
369
370  quote! {{
371      #( #fields ),*
372  }}
373}
374
375fn gen_from_clap(
376  struct_name: &Ident,
377  fields: &Punctuated<Field, Comma>,
378  parent_attribute: &Attrs,
379) -> TokenStream {
380  let field_block = gen_constructor(fields, parent_attribute);
381
382  quote! {
383      fn from_clap(matches: &structopt::clap::ArgMatches) -> Self {
384          #struct_name #field_block
385      }
386  }
387}
388
389fn gen_clap(attrs: &[Attribute]) -> GenOutput {
390  let name = std::env::var("CARGO_PKG_NAME").ok().unwrap_or_default();
391
392  let attrs = Attrs::from_struct(
393    Span::call_site(),
394    attrs,
395    Name::Assigned(quote!(#name)),
396    None,
397    Sp::call_site(DEFAULT_CASING),
398    Sp::call_site(DEFAULT_ENV_CASING),
399  );
400  let tokens = {
401    let name = attrs.cased_name();
402    quote!(structopt::clap::App::new(#name))
403  };
404
405  GenOutput { tokens, attrs }
406}
407
408fn gen_clap_struct(struct_attrs: &[Attribute]) -> GenOutput {
409  let initial_clap_app_gen = gen_clap(struct_attrs);
410  let clap_tokens = initial_clap_app_gen.tokens;
411
412  let augmented_tokens = quote! {
413      fn clap<'a, 'b>() -> structopt::clap::App<'a, 'b> {
414          let app = #clap_tokens;
415          <Self as structopt::StructOptInternal>::augment_clap(app)
416      }
417  };
418
419  GenOutput { tokens: augmented_tokens, attrs: initial_clap_app_gen.attrs }
420}
421
422fn gen_augment_clap(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream {
423  let app_var = Ident::new("app", Span::call_site());
424  let augmentation = gen_augmentation(fields, &app_var, parent_attribute);
425  quote! {
426      fn augment_clap<'a, 'b>(
427          #app_var: structopt::clap::App<'a, 'b>
428      ) -> structopt::clap::App<'a, 'b> {
429          #augmentation
430      }
431  }
432}
433
434fn gen_clap_enum(enum_attrs: &[Attribute]) -> GenOutput {
435  let initial_clap_app_gen = gen_clap(enum_attrs);
436  let clap_tokens = initial_clap_app_gen.tokens;
437
438  let tokens = quote! {
439      fn clap<'a, 'b>() -> structopt::clap::App<'a, 'b> {
440          let app = #clap_tokens
441              .setting(structopt::clap::AppSettings::SubcommandRequiredElseHelp);
442          <Self as structopt::StructOptInternal>::augment_clap(app)
443      }
444  };
445
446  GenOutput { tokens, attrs: initial_clap_app_gen.attrs }
447}
448
449fn gen_augment_clap_enum(
450  variants: &Punctuated<Variant, Comma>,
451  parent_attribute: &Attrs,
452) -> TokenStream {
453  use syn::Fields::*;
454
455  let subcommands = variants.iter().map(|variant| {
456    let attrs = Attrs::from_struct(
457      variant.span(),
458      &variant.attrs,
459      Name::Derived(variant.ident.clone()),
460      Some(parent_attribute),
461      parent_attribute.casing(),
462      parent_attribute.env_casing(),
463    );
464
465    let kind = attrs.kind();
466    match &*kind {
467      Kind::ExternalSubcommand => {
468        let app_var = Ident::new("app", Span::call_site());
469        quote_spanned! { attrs.kind().span()=>
470            let #app_var = #app_var.setting(
471                structopt::clap::AppSettings::AllowExternalSubcommands
472            );
473        }
474      }
475
476      Kind::Flatten => match variant.fields {
477        Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
478          let ty = &unnamed[0];
479          quote! {
480              let app = <#ty as structopt::StructOptInternal>::augment_clap(app);
481          }
482        }
483        _ => abort!(variant, "`flatten` is usable only with single-typed tuple variants"),
484      },
485
486      _ => {
487        let app_var = Ident::new("subcommand", Span::call_site());
488        let arg_block = match variant.fields {
489          Named(ref fields) => gen_augmentation(&fields.named, &app_var, &attrs),
490          Unit => quote!( #app_var ),
491          Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
492            let ty = &unnamed[0];
493            quote_spanned! { ty.span()=>
494                {
495                    let #app_var = <#ty as structopt::StructOptInternal>::augment_clap(
496                        #app_var
497                    );
498                    if <#ty as structopt::StructOptInternal>::is_subcommand() {
499                        #app_var.setting(
500                            structopt::clap::AppSettings::SubcommandRequiredElseHelp
501                        )
502                    } else {
503                        #app_var
504                    }
505                }
506            }
507          }
508          Unnamed(..) => abort!(variant, "non single-typed tuple enums are not supported"),
509        };
510
511        let name = attrs.cased_name();
512        let from_attrs = attrs.top_level_methods();
513        let version = attrs.version();
514        quote! {
515            let app = app.subcommand({
516                let #app_var = structopt::clap::SubCommand::with_name(#name);
517                let #app_var = #arg_block;
518                #app_var#from_attrs#version
519            });
520        }
521      }
522    }
523  });
524
525  let app_methods = parent_attribute.top_level_methods();
526  let version = parent_attribute.version();
527  quote! {
528      fn augment_clap<'a, 'b>(
529          app: structopt::clap::App<'a, 'b>
530      ) -> structopt::clap::App<'a, 'b> {
531          let app = app #app_methods;
532          #( #subcommands )*;
533          app #version
534      }
535  }
536}
537
538fn gen_from_clap_enum(name: &Ident) -> TokenStream {
539  quote! {
540      fn from_clap(matches: &structopt::clap::ArgMatches) -> Self {
541          <#name as structopt::StructOptInternal>::from_subcommand(matches.subcommand())
542              .expect("structopt misuse: You likely tried to #[flatten] a struct \
543                       that contains #[subcommand]. This is forbidden.")
544      }
545  }
546}
547
548fn gen_from_subcommand(
549  name: &Ident,
550  variants: &Punctuated<Variant, Comma>,
551  parent_attribute: &Attrs,
552) -> TokenStream {
553  use syn::Fields::*;
554
555  let mut ext_subcmd = None;
556
557  let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
558    .iter()
559    .filter_map(|variant| {
560      let attrs = Attrs::from_struct(
561        variant.span(),
562        &variant.attrs,
563        Name::Derived(variant.ident.clone()),
564        Some(parent_attribute),
565        parent_attribute.casing(),
566        parent_attribute.env_casing(),
567      );
568
569      let variant_name = &variant.ident;
570
571      if let Kind::ExternalSubcommand = *attrs.kind() {
572        if ext_subcmd.is_some() {
573          abort!(
574            attrs.kind().span(),
575            "Only one variant can be marked with `external_subcommand`, \
576                         this is the second"
577          );
578        }
579
580        let ty = match variant.fields {
581          Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
582
583          _ => abort!(
584            variant,
585            "The enum variant marked with `external_attribute` must be \
586                         a single-typed tuple, and the type must be either `Vec<String>` \
587                         or `Vec<OsString>`."
588          ),
589        };
590
591        let (span, str_ty, values_of) = match subty_if_name(ty, "Vec") {
592          Some(subty) => {
593            if is_simple_ty(subty, "String") {
594              (subty.span(), quote!(::std::string::String), quote!(values_of))
595            } else {
596              (subty.span(), quote!(::std::ffi::OsString), quote!(values_of_os))
597            }
598          }
599
600          None => abort!(
601            ty,
602            "The type must be either `Vec<String>` or `Vec<OsString>` \
603                         to be used with `external_subcommand`."
604          ),
605        };
606
607        ext_subcmd = Some((span, variant_name, str_ty, values_of));
608        None
609      } else {
610        Some((variant, attrs))
611      }
612    })
613    .partition(|(_, attrs)| match &*attrs.kind() {
614      Kind::Flatten => true,
615      _ => false,
616    });
617
618  let other = format_ident!("other");
619  let matches = format_ident!("matches");
620
621  let external = match ext_subcmd {
622    Some((span, var_name, str_ty, values_of)) => quote_spanned! { span=>
623        match #other {
624            ("", ::std::option::Option::None) => None,
625
626            (external, Some(#matches)) => {
627                ::std::option::Option::Some(#name::#var_name(
628                    ::std::iter::once(#str_ty::from(external))
629                    .chain(
630                        #matches.#values_of("").into_iter().flatten().map(#str_ty::from)
631                    )
632                    .collect::<::std::vec::Vec<_>>()
633                ))
634            }
635
636            (external, None) => {
637                ::std::option::Option::Some(#name::#var_name(
638                    ::std::iter::once(#str_ty::from(external))
639                        .collect::<::std::vec::Vec<_>>()
640                ))
641            }
642        }
643    },
644
645    None => quote!(None),
646  };
647
648  let match_arms = variants.iter().map(|(variant, attrs)| {
649    let sub_name = attrs.cased_name();
650    let variant_name = &variant.ident;
651    let constructor_block = match variant.fields {
652      Named(ref fields) => gen_constructor(&fields.named, &attrs),
653      Unit => quote!(),
654      Unnamed(ref fields) if fields.unnamed.len() == 1 => {
655        let ty = &fields.unnamed[0];
656        quote!( ( <#ty as structopt::StructOpt>::from_clap(#matches) ) )
657      }
658      Unnamed(..) => abort!(variant.ident, "non single-typed tuple enums are not supported"),
659    };
660
661    quote! {
662        (#sub_name, Some(#matches)) => {
663            Some(#name :: #variant_name #constructor_block)
664        }
665    }
666  });
667
668  let child_subcommands = flatten_variants.iter().map(|(variant, _attrs)| {
669    let variant_name = &variant.ident;
670    match variant.fields {
671      Unnamed(ref fields) if fields.unnamed.len() == 1 => {
672        let ty = &fields.unnamed[0];
673        quote! {
674            if let Some(res) =
675                <#ty as structopt::StructOptInternal>::from_subcommand(#other)
676            {
677                return Some(#name :: #variant_name (res));
678            }
679        }
680      }
681      _ => abort!(variant, "`flatten` is usable only with single-typed tuple variants"),
682    }
683  });
684
685  quote! {
686      fn from_subcommand<'a, 'b>(
687          sub: (&'b str, Option<&'b structopt::clap::ArgMatches<'a>>)
688      ) -> Option<Self> {
689          match sub {
690              #( #match_arms, )*
691              #other => {
692                  #( #child_subcommands )else*;
693                  #external
694              }
695          }
696      }
697  }
698}
699
700#[cfg(feature = "paw")]
701fn gen_paw_impl(name: &Ident) -> TokenStream {
702  quote! {
703      impl structopt::paw::ParseArgs for #name {
704          type Error = std::io::Error;
705
706          fn parse_args() -> std::result::Result<Self, Self::Error> {
707              Ok(<#name as structopt::StructOpt>::from_args())
708          }
709      }
710  }
711}
712#[cfg(not(feature = "paw"))]
713fn gen_paw_impl(_: &Ident) -> TokenStream {
714  TokenStream::new()
715}
716
717fn impl_structopt_for_struct(
718  name: &Ident,
719  fields: &Punctuated<Field, Comma>,
720  attrs: &[Attribute],
721) -> TokenStream {
722  let basic_clap_app_gen = gen_clap_struct(attrs);
723  let augment_clap = gen_augment_clap(fields, &basic_clap_app_gen.attrs);
724  let from_clap = gen_from_clap(name, fields, &basic_clap_app_gen.attrs);
725  let paw_impl = gen_paw_impl(name);
726
727  let clap_tokens = basic_clap_app_gen.tokens;
728  quote! {
729      #[allow(unused_variables)]
730      #[allow(unknown_lints)]
731      #[allow(
732          clippy::style,
733          clippy::complexity,
734          clippy::pedantic,
735          clippy::restriction,
736          clippy::perf,
737          clippy::deprecated,
738          clippy::nursery,
739          clippy::cargo
740      )]
741      #[deny(clippy::correctness)]
742      #[allow(dead_code, unreachable_code)]
743      impl structopt::StructOpt for #name {
744          #clap_tokens
745          #from_clap
746      }
747
748      #[allow(unused_variables)]
749      #[allow(unknown_lints)]
750      #[allow(
751          clippy::style,
752          clippy::complexity,
753          clippy::pedantic,
754          clippy::restriction,
755          clippy::perf,
756          clippy::deprecated,
757          clippy::nursery,
758          clippy::cargo
759      )]
760      #[deny(clippy::correctness)]
761      #[allow(dead_code, unreachable_code)]
762      impl structopt::StructOptInternal for #name {
763          #augment_clap
764          fn is_subcommand() -> bool { false }
765      }
766
767      #paw_impl
768  }
769}
770
771fn impl_structopt_for_enum(
772  name: &Ident,
773  variants: &Punctuated<Variant, Comma>,
774  attrs: &[Attribute],
775) -> TokenStream {
776  let basic_clap_app_gen = gen_clap_enum(attrs);
777  let clap_tokens = basic_clap_app_gen.tokens;
778  let attrs = basic_clap_app_gen.attrs;
779
780  let augment_clap = gen_augment_clap_enum(variants, &attrs);
781  let from_clap = gen_from_clap_enum(name);
782  let from_subcommand = gen_from_subcommand(name, variants, &attrs);
783  let paw_impl = gen_paw_impl(name);
784
785  quote! {
786      #[allow(unknown_lints)]
787      #[allow(unused_variables, dead_code, unreachable_code)]
788      #[allow(
789          clippy::style,
790          clippy::complexity,
791          clippy::pedantic,
792          clippy::restriction,
793          clippy::perf,
794          clippy::deprecated,
795          clippy::nursery,
796          clippy::cargo
797      )]
798      #[deny(clippy::correctness)]
799      impl structopt::StructOpt for #name {
800          #clap_tokens
801          #from_clap
802      }
803
804      #[allow(unused_variables)]
805      #[allow(unknown_lints)]
806      #[allow(
807          clippy::style,
808          clippy::complexity,
809          clippy::pedantic,
810          clippy::restriction,
811          clippy::perf,
812          clippy::deprecated,
813          clippy::nursery,
814          clippy::cargo
815      )]
816      #[deny(clippy::correctness)]
817      #[allow(dead_code, unreachable_code)]
818      impl structopt::StructOptInternal for #name {
819          #augment_clap
820          #from_subcommand
821          fn is_subcommand() -> bool { true }
822      }
823
824      #paw_impl
825  }
826}
827
828fn impl_structopt(input: &DeriveInput) -> TokenStream {
829  use syn::Data::*;
830
831  let struct_name = &input.ident;
832
833  set_dummy(quote! {
834      impl structopt::StructOpt for #struct_name {
835          fn clap<'a, 'b>() -> structopt::clap::App<'a, 'b> {
836              unimplemented!()
837          }
838          fn from_clap(_matches: &structopt::clap::ArgMatches) -> Self {
839              unimplemented!()
840          }
841      }
842
843      impl structopt::StructOptInternal for #struct_name {}
844  });
845
846  match input.data {
847    Struct(DataStruct { fields: syn::Fields::Named(ref fields), .. }) => {
848      impl_structopt_for_struct(struct_name, &fields.named, &input.attrs)
849    }
850    Enum(ref e) => impl_structopt_for_enum(struct_name, &e.variants, &input.attrs),
851    _ => abort_call_site!("structopt only supports non-tuple structs and enums"),
852  }
853}