indigo-structopt-derive 0.4.14

Parse command line argument by defining a struct, derive crate.
Documentation
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This crate is custom derive for `StructOpt`. It should not be used
//! directly. See [structopt documentation](https://docs.rs/structopt)
//! for the usage of `#[derive(StructOpt)]`.

#![allow(clippy::large_enum_variant)]
// FIXME: remove when and if our MSRV hits 1.42
#![allow(clippy::match_like_matches_macro)]
#![forbid(unsafe_code)]

extern crate proc_macro;

mod attrs;
mod doc_comments;
mod parse;
mod spanned;
mod ty;

use crate::{
  attrs::{Attrs, CasingStyle, Kind, Name, ParserKind},
  spanned::Sp,
  ty::{is_simple_ty, sub_type, subty_if_name, Ty},
};

use proc_macro2::{Span, TokenStream};
use proc_macro_error::{abort, abort_call_site, proc_macro_error, set_dummy};
use quote::{format_ident, quote, quote_spanned};
use syn::{punctuated::Punctuated, spanned::Spanned, token::Comma, *};

/// Default casing style for generated arguments.
const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab;

/// Default casing style for environment variables
const DEFAULT_ENV_CASING: CasingStyle = CasingStyle::ScreamingSnake;

/// Output for the `gen_xxx()` methods were we need more than a simple stream of tokens.
///
/// The output of a generation method is not only the stream of new tokens but also the attribute
/// information of the current element. These attribute information may contain valuable information
/// for any kind of child arguments.
struct GenOutput {
  tokens: TokenStream,
  attrs: Attrs,
}

/// Generates the `StructOpt` impl.
#[proc_macro_derive(StructOpt, attributes(structopt))]
#[proc_macro_error]
pub fn structopt(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
  let input: DeriveInput = syn::parse(input).unwrap();
  let gen = impl_structopt(&input);
  gen.into()
}

/// Generate a block of code to add arguments/subcommands corresponding to
/// the `fields` to an app.
fn gen_augmentation(
  fields: &Punctuated<Field, Comma>,
  app_var: &Ident,
  parent_attribute: &Attrs,
) -> TokenStream {
  let mut subcmds = fields.iter().filter_map(|field| {
    let attrs = Attrs::from_field(
      field,
      Some(parent_attribute),
      parent_attribute.casing(),
      parent_attribute.env_casing(),
    );
    let kind = attrs.kind();
    if let Kind::Subcommand(ty) = &*kind {
      let subcmd_type = match (**ty, sub_type(&field.ty)) {
        (Ty::Option, Some(sub_type)) => sub_type,
        _ => &field.ty,
      };
      let required = if **ty == Ty::Option {
        quote!()
      } else {
        quote_spanned! { kind.span()=>
            let #app_var = #app_var.setting(
                structopt::clap::AppSettings::SubcommandRequiredElseHelp
            );
        }
      };

      let span = field.span();
      let ts = quote! {
          let #app_var = <#subcmd_type as structopt::StructOptInternal>::augment_clap(
              #app_var
          );
          #required
      };
      Some((span, ts))
    } else {
      None
    }
  });

  let subcmd = subcmds.next().map(|(_, ts)| ts);
  if let Some((span, _)) = subcmds.next() {
    abort!(span, "multiple subcommand sets are not allowed, that's the second");
  }

  let args = fields.iter().filter_map(|field| {
    let attrs = Attrs::from_field(
      field,
      Some(parent_attribute),
      parent_attribute.casing(),
      parent_attribute.env_casing(),
    );
    let kind = attrs.kind();
    match &*kind {
      Kind::ExternalSubcommand => {
        abort!(kind.span(), "`external_subcommand` is only allowed on enum variants")
      }
      Kind::Subcommand(_) | Kind::Skip(_) => None,
      Kind::Flatten => {
        let ty = &field.ty;
        Some(quote_spanned! { kind.span()=>
            let #app_var = <#ty as structopt::StructOptInternal>::augment_clap(#app_var);
            let #app_var = if <#ty as structopt::StructOptInternal>::is_subcommand() {
                #app_var.setting(structopt::clap::AppSettings::SubcommandRequiredElseHelp)
            } else {
                #app_var
            };
        })
      }
      Kind::Arg(ty) => {
        let convert_type = match **ty {
          Ty::Vec | Ty::Option => sub_type(&field.ty).unwrap_or(&field.ty),
          Ty::OptionOption | Ty::OptionVec => {
            sub_type(&field.ty).and_then(sub_type).unwrap_or(&field.ty)
          }
          _ => &field.ty,
        };

        let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences;
        let flag = *attrs.parser().kind == ParserKind::FromFlag;

        let parser = attrs.parser();
        let func = &parser.func;
        let validator = match *parser.kind {
          ParserKind::TryFromStr => quote_spanned! { func.span()=>
              .validator(|s| {
                  #func(s.as_str())
                  .map(|_: #convert_type| ())
                  .map_err(|e| e.to_string())
              })
          },
          ParserKind::TryFromOsStr => quote_spanned! { func.span()=>
              .validator_os(|s| #func(&s).map(|_: #convert_type| ()))
          },
          _ => quote!(),
        };

        let modifier = match **ty {
          Ty::Bool => quote_spanned! { ty.span()=>
              .takes_value(false)
              .multiple(false)
          },

          Ty::Option => quote_spanned! { ty.span()=>
              .takes_value(true)
              .multiple(false)
              #validator
          },

          Ty::OptionOption => quote_spanned! { ty.span()=>
                  .takes_value(true)
                  .multiple(false)
                  .min_values(0)
                  .max_values(1)
                  #validator
          },

          Ty::OptionVec => quote_spanned! { ty.span()=>
              .takes_value(true)
              .multiple(true)
              .min_values(0)
              #validator
          },

          Ty::Vec => quote_spanned! { ty.span()=>
              .takes_value(true)
              .multiple(true)
              #validator
          },

          Ty::Other if occurrences => quote_spanned! { ty.span()=>
              .takes_value(false)
              .multiple(true)
          },

          Ty::Other if flag => quote_spanned! { ty.span()=>
              .takes_value(false)
              .multiple(false)
          },

          Ty::Other => {
            let required = !attrs.has_method("default_value");
            quote_spanned! { ty.span()=>
                .takes_value(true)
                .multiple(false)
                .required(#required)
                #validator
            }
          }
        };

        let name = attrs.cased_name();
        let methods = attrs.field_methods();

        Some(quote_spanned! { field.span()=>
            let #app_var = #app_var.arg(
                structopt::clap::Arg::with_name(#name)
                    #modifier
                    #methods
            );
        })
      }
    }
  });

  let app_methods = parent_attribute.top_level_methods();
  let version = parent_attribute.version();
  quote! {{
      let #app_var = #app_var#app_methods;
      #( #args )*
      #subcmd
      #app_var#version
  }}
}

fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream {
  // This ident is used in several match branches below,
  // and the `quote[_spanned]` invocations have different spans.
  //
  // Given that this ident is used in several places and
  // that the branches are located inside of a loop, it is possible that
  // this ident will be given _different_ spans in different places, and
  // thus will not be the _same_ ident anymore. To make sure the `matches`
  // is always the same, we factor it out.
  let matches = format_ident!("matches");

  let fields = fields.iter().map(|field| {
    let attrs = Attrs::from_field(
      field,
      Some(parent_attribute),
      parent_attribute.casing(),
      parent_attribute.env_casing(),
    );
    let field_name = field.ident.as_ref().unwrap();
    let kind = attrs.kind();
    match &*kind {
      Kind::ExternalSubcommand => {
        abort!(kind.span(), "`external_subcommand` is allowed only on enum variants")
      }

      Kind::Subcommand(ty) => {
        let subcmd_type = match (**ty, sub_type(&field.ty)) {
          (Ty::Option, Some(sub_type)) => sub_type,
          _ => &field.ty,
        };
        let unwrapper = match **ty {
          Ty::Option => quote!(),
          _ => quote_spanned!( ty.span()=> .unwrap() ),
        };
        quote_spanned! { kind.span()=>
            #field_name: <#subcmd_type as structopt::StructOptInternal>::from_subcommand(
                #matches.subcommand())
                #unwrapper
        }
      }

      Kind::Flatten => quote_spanned! { kind.span()=>
          #field_name: structopt::StructOpt::from_clap(#matches)
      },

      Kind::Skip(val) => match val {
        None => quote_spanned!(kind.span()=> #field_name: Default::default()),
        Some(val) => quote_spanned!(kind.span()=> #field_name: (#val).into()),
      },

      Kind::Arg(ty) => {
        use crate::attrs::ParserKind::*;

        let parser = attrs.parser();
        let func = &parser.func;
        let span = parser.kind.span();
        let (value_of, values_of, parse) = match *parser.kind {
          FromStr => {
            (quote_spanned!(span=> value_of), quote_spanned!(span=> values_of), func.clone())
          }
          TryFromStr => (
            quote_spanned!(span=> value_of),
            quote_spanned!(span=> values_of),
            quote_spanned!(func.span()=> |s| #func(s).unwrap()),
          ),
          FromOsStr => {
            (quote_spanned!(span=> value_of_os), quote_spanned!(span=> values_of_os), func.clone())
          }
          TryFromOsStr => (
            quote_spanned!(span=> value_of_os),
            quote_spanned!(span=> values_of_os),
            quote_spanned!(func.span()=> |s| #func(s).unwrap()),
          ),
          FromOccurrences => (quote_spanned!(span=> occurrences_of), quote!(), func.clone()),
          FromFlag => (quote!(), quote!(), func.clone()),
        };

        let flag = *attrs.parser().kind == ParserKind::FromFlag;
        let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences;
        let name = attrs.cased_name();
        let field_value = match **ty {
          Ty::Bool => quote_spanned!(ty.span()=> #matches.is_present(#name)),

          Ty::Option => quote_spanned! { ty.span()=>
              #matches.#value_of(#name)
                  .map(#parse)
          },

          Ty::OptionOption => quote_spanned! { ty.span()=>
              if #matches.is_present(#name) {
                  Some(#matches.#value_of(#name).map(#parse))
              } else {
                  None
              }
          },

          Ty::OptionVec => quote_spanned! { ty.span()=>
              if #matches.is_present(#name) {
                  Some(#matches.#values_of(#name)
                       .map_or_else(Vec::new, |v| v.map(#parse).collect()))
              } else {
                  None
              }
          },

          Ty::Vec => quote_spanned! { ty.span()=>
              #matches.#values_of(#name)
                  .map_or_else(Vec::new, |v| v.map(#parse).collect())
          },

          Ty::Other if occurrences => quote_spanned! { ty.span()=>
              #parse(#matches.#value_of(#name))
          },

          Ty::Other if flag => quote_spanned! { ty.span()=>
              #parse(#matches.is_present(#name))
          },

          Ty::Other => quote_spanned! { ty.span()=>
              #matches.#value_of(#name)
                  .map(#parse)
                  .unwrap()
          },
        };

        quote_spanned!(field.span()=> #field_name: #field_value )
      }
    }
  });

  quote! {{
      #( #fields ),*
  }}
}

fn gen_from_clap(
  struct_name: &Ident,
  fields: &Punctuated<Field, Comma>,
  parent_attribute: &Attrs,
) -> TokenStream {
  let field_block = gen_constructor(fields, parent_attribute);

  quote! {
      fn from_clap(matches: &structopt::clap::ArgMatches) -> Self {
          #struct_name #field_block
      }
  }
}

fn gen_clap(attrs: &[Attribute]) -> GenOutput {
  let name = std::env::var("CARGO_PKG_NAME").ok().unwrap_or_default();

  let attrs = Attrs::from_struct(
    Span::call_site(),
    attrs,
    Name::Assigned(quote!(#name)),
    None,
    Sp::call_site(DEFAULT_CASING),
    Sp::call_site(DEFAULT_ENV_CASING),
  );
  let tokens = {
    let name = attrs.cased_name();
    quote!(structopt::clap::App::new(#name))
  };

  GenOutput { tokens, attrs }
}

fn gen_clap_struct(struct_attrs: &[Attribute]) -> GenOutput {
  let initial_clap_app_gen = gen_clap(struct_attrs);
  let clap_tokens = initial_clap_app_gen.tokens;

  let augmented_tokens = quote! {
      fn clap<'a, 'b>() -> structopt::clap::App<'a, 'b> {
          let app = #clap_tokens;
          <Self as structopt::StructOptInternal>::augment_clap(app)
      }
  };

  GenOutput { tokens: augmented_tokens, attrs: initial_clap_app_gen.attrs }
}

fn gen_augment_clap(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream {
  let app_var = Ident::new("app", Span::call_site());
  let augmentation = gen_augmentation(fields, &app_var, parent_attribute);
  quote! {
      fn augment_clap<'a, 'b>(
          #app_var: structopt::clap::App<'a, 'b>
      ) -> structopt::clap::App<'a, 'b> {
          #augmentation
      }
  }
}

fn gen_clap_enum(enum_attrs: &[Attribute]) -> GenOutput {
  let initial_clap_app_gen = gen_clap(enum_attrs);
  let clap_tokens = initial_clap_app_gen.tokens;

  let tokens = quote! {
      fn clap<'a, 'b>() -> structopt::clap::App<'a, 'b> {
          let app = #clap_tokens
              .setting(structopt::clap::AppSettings::SubcommandRequiredElseHelp);
          <Self as structopt::StructOptInternal>::augment_clap(app)
      }
  };

  GenOutput { tokens, attrs: initial_clap_app_gen.attrs }
}

fn gen_augment_clap_enum(
  variants: &Punctuated<Variant, Comma>,
  parent_attribute: &Attrs,
) -> TokenStream {
  use syn::Fields::*;

  let subcommands = variants.iter().map(|variant| {
    let attrs = Attrs::from_struct(
      variant.span(),
      &variant.attrs,
      Name::Derived(variant.ident.clone()),
      Some(parent_attribute),
      parent_attribute.casing(),
      parent_attribute.env_casing(),
    );

    let kind = attrs.kind();
    match &*kind {
      Kind::ExternalSubcommand => {
        let app_var = Ident::new("app", Span::call_site());
        quote_spanned! { attrs.kind().span()=>
            let #app_var = #app_var.setting(
                structopt::clap::AppSettings::AllowExternalSubcommands
            );
        }
      }

      Kind::Flatten => match variant.fields {
        Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
          let ty = &unnamed[0];
          quote! {
              let app = <#ty as structopt::StructOptInternal>::augment_clap(app);
          }
        }
        _ => abort!(variant, "`flatten` is usable only with single-typed tuple variants"),
      },

      _ => {
        let app_var = Ident::new("subcommand", Span::call_site());
        let arg_block = match variant.fields {
          Named(ref fields) => gen_augmentation(&fields.named, &app_var, &attrs),
          Unit => quote!( #app_var ),
          Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
            let ty = &unnamed[0];
            quote_spanned! { ty.span()=>
                {
                    let #app_var = <#ty as structopt::StructOptInternal>::augment_clap(
                        #app_var
                    );
                    if <#ty as structopt::StructOptInternal>::is_subcommand() {
                        #app_var.setting(
                            structopt::clap::AppSettings::SubcommandRequiredElseHelp
                        )
                    } else {
                        #app_var
                    }
                }
            }
          }
          Unnamed(..) => abort!(variant, "non single-typed tuple enums are not supported"),
        };

        let name = attrs.cased_name();
        let from_attrs = attrs.top_level_methods();
        let version = attrs.version();
        quote! {
            let app = app.subcommand({
                let #app_var = structopt::clap::SubCommand::with_name(#name);
                let #app_var = #arg_block;
                #app_var#from_attrs#version
            });
        }
      }
    }
  });

  let app_methods = parent_attribute.top_level_methods();
  let version = parent_attribute.version();
  quote! {
      fn augment_clap<'a, 'b>(
          app: structopt::clap::App<'a, 'b>
      ) -> structopt::clap::App<'a, 'b> {
          let app = app #app_methods;
          #( #subcommands )*;
          app #version
      }
  }
}

fn gen_from_clap_enum(name: &Ident) -> TokenStream {
  quote! {
      fn from_clap(matches: &structopt::clap::ArgMatches) -> Self {
          <#name as structopt::StructOptInternal>::from_subcommand(matches.subcommand())
              .expect("structopt misuse: You likely tried to #[flatten] a struct \
                       that contains #[subcommand]. This is forbidden.")
      }
  }
}

fn gen_from_subcommand(
  name: &Ident,
  variants: &Punctuated<Variant, Comma>,
  parent_attribute: &Attrs,
) -> TokenStream {
  use syn::Fields::*;

  let mut ext_subcmd = None;

  let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
    .iter()
    .filter_map(|variant| {
      let attrs = Attrs::from_struct(
        variant.span(),
        &variant.attrs,
        Name::Derived(variant.ident.clone()),
        Some(parent_attribute),
        parent_attribute.casing(),
        parent_attribute.env_casing(),
      );

      let variant_name = &variant.ident;

      if let Kind::ExternalSubcommand = *attrs.kind() {
        if ext_subcmd.is_some() {
          abort!(
            attrs.kind().span(),
            "Only one variant can be marked with `external_subcommand`, \
                         this is the second"
          );
        }

        let ty = match variant.fields {
          Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,

          _ => abort!(
            variant,
            "The enum variant marked with `external_attribute` must be \
                         a single-typed tuple, and the type must be either `Vec<String>` \
                         or `Vec<OsString>`."
          ),
        };

        let (span, str_ty, values_of) = match subty_if_name(ty, "Vec") {
          Some(subty) => {
            if is_simple_ty(subty, "String") {
              (subty.span(), quote!(::std::string::String), quote!(values_of))
            } else {
              (subty.span(), quote!(::std::ffi::OsString), quote!(values_of_os))
            }
          }

          None => abort!(
            ty,
            "The type must be either `Vec<String>` or `Vec<OsString>` \
                         to be used with `external_subcommand`."
          ),
        };

        ext_subcmd = Some((span, variant_name, str_ty, values_of));
        None
      } else {
        Some((variant, attrs))
      }
    })
    .partition(|(_, attrs)| match &*attrs.kind() {
      Kind::Flatten => true,
      _ => false,
    });

  let other = format_ident!("other");
  let matches = format_ident!("matches");

  let external = match ext_subcmd {
    Some((span, var_name, str_ty, values_of)) => quote_spanned! { span=>
        match #other {
            ("", ::std::option::Option::None) => None,

            (external, Some(#matches)) => {
                ::std::option::Option::Some(#name::#var_name(
                    ::std::iter::once(#str_ty::from(external))
                    .chain(
                        #matches.#values_of("").into_iter().flatten().map(#str_ty::from)
                    )
                    .collect::<::std::vec::Vec<_>>()
                ))
            }

            (external, None) => {
                ::std::option::Option::Some(#name::#var_name(
                    ::std::iter::once(#str_ty::from(external))
                        .collect::<::std::vec::Vec<_>>()
                ))
            }
        }
    },

    None => quote!(None),
  };

  let match_arms = variants.iter().map(|(variant, attrs)| {
    let sub_name = attrs.cased_name();
    let variant_name = &variant.ident;
    let constructor_block = match variant.fields {
      Named(ref fields) => gen_constructor(&fields.named, &attrs),
      Unit => quote!(),
      Unnamed(ref fields) if fields.unnamed.len() == 1 => {
        let ty = &fields.unnamed[0];
        quote!( ( <#ty as structopt::StructOpt>::from_clap(#matches) ) )
      }
      Unnamed(..) => abort!(variant.ident, "non single-typed tuple enums are not supported"),
    };

    quote! {
        (#sub_name, Some(#matches)) => {
            Some(#name :: #variant_name #constructor_block)
        }
    }
  });

  let child_subcommands = flatten_variants.iter().map(|(variant, _attrs)| {
    let variant_name = &variant.ident;
    match variant.fields {
      Unnamed(ref fields) if fields.unnamed.len() == 1 => {
        let ty = &fields.unnamed[0];
        quote! {
            if let Some(res) =
                <#ty as structopt::StructOptInternal>::from_subcommand(#other)
            {
                return Some(#name :: #variant_name (res));
            }
        }
      }
      _ => abort!(variant, "`flatten` is usable only with single-typed tuple variants"),
    }
  });

  quote! {
      fn from_subcommand<'a, 'b>(
          sub: (&'b str, Option<&'b structopt::clap::ArgMatches<'a>>)
      ) -> Option<Self> {
          match sub {
              #( #match_arms, )*
              #other => {
                  #( #child_subcommands )else*;
                  #external
              }
          }
      }
  }
}

#[cfg(feature = "paw")]
fn gen_paw_impl(name: &Ident) -> TokenStream {
  quote! {
      impl structopt::paw::ParseArgs for #name {
          type Error = std::io::Error;

          fn parse_args() -> std::result::Result<Self, Self::Error> {
              Ok(<#name as structopt::StructOpt>::from_args())
          }
      }
  }
}
#[cfg(not(feature = "paw"))]
fn gen_paw_impl(_: &Ident) -> TokenStream {
  TokenStream::new()
}

fn impl_structopt_for_struct(
  name: &Ident,
  fields: &Punctuated<Field, Comma>,
  attrs: &[Attribute],
) -> TokenStream {
  let basic_clap_app_gen = gen_clap_struct(attrs);
  let augment_clap = gen_augment_clap(fields, &basic_clap_app_gen.attrs);
  let from_clap = gen_from_clap(name, fields, &basic_clap_app_gen.attrs);
  let paw_impl = gen_paw_impl(name);

  let clap_tokens = basic_clap_app_gen.tokens;
  quote! {
      #[allow(unused_variables)]
      #[allow(unknown_lints)]
      #[allow(
          clippy::style,
          clippy::complexity,
          clippy::pedantic,
          clippy::restriction,
          clippy::perf,
          clippy::deprecated,
          clippy::nursery,
          clippy::cargo
      )]
      #[deny(clippy::correctness)]
      #[allow(dead_code, unreachable_code)]
      impl structopt::StructOpt for #name {
          #clap_tokens
          #from_clap
      }

      #[allow(unused_variables)]
      #[allow(unknown_lints)]
      #[allow(
          clippy::style,
          clippy::complexity,
          clippy::pedantic,
          clippy::restriction,
          clippy::perf,
          clippy::deprecated,
          clippy::nursery,
          clippy::cargo
      )]
      #[deny(clippy::correctness)]
      #[allow(dead_code, unreachable_code)]
      impl structopt::StructOptInternal for #name {
          #augment_clap
          fn is_subcommand() -> bool { false }
      }

      #paw_impl
  }
}

fn impl_structopt_for_enum(
  name: &Ident,
  variants: &Punctuated<Variant, Comma>,
  attrs: &[Attribute],
) -> TokenStream {
  let basic_clap_app_gen = gen_clap_enum(attrs);
  let clap_tokens = basic_clap_app_gen.tokens;
  let attrs = basic_clap_app_gen.attrs;

  let augment_clap = gen_augment_clap_enum(variants, &attrs);
  let from_clap = gen_from_clap_enum(name);
  let from_subcommand = gen_from_subcommand(name, variants, &attrs);
  let paw_impl = gen_paw_impl(name);

  quote! {
      #[allow(unknown_lints)]
      #[allow(unused_variables, dead_code, unreachable_code)]
      #[allow(
          clippy::style,
          clippy::complexity,
          clippy::pedantic,
          clippy::restriction,
          clippy::perf,
          clippy::deprecated,
          clippy::nursery,
          clippy::cargo
      )]
      #[deny(clippy::correctness)]
      impl structopt::StructOpt for #name {
          #clap_tokens
          #from_clap
      }

      #[allow(unused_variables)]
      #[allow(unknown_lints)]
      #[allow(
          clippy::style,
          clippy::complexity,
          clippy::pedantic,
          clippy::restriction,
          clippy::perf,
          clippy::deprecated,
          clippy::nursery,
          clippy::cargo
      )]
      #[deny(clippy::correctness)]
      #[allow(dead_code, unreachable_code)]
      impl structopt::StructOptInternal for #name {
          #augment_clap
          #from_subcommand
          fn is_subcommand() -> bool { true }
      }

      #paw_impl
  }
}

fn impl_structopt(input: &DeriveInput) -> TokenStream {
  use syn::Data::*;

  let struct_name = &input.ident;

  set_dummy(quote! {
      impl structopt::StructOpt for #struct_name {
          fn clap<'a, 'b>() -> structopt::clap::App<'a, 'b> {
              unimplemented!()
          }
          fn from_clap(_matches: &structopt::clap::ArgMatches) -> Self {
              unimplemented!()
          }
      }

      impl structopt::StructOptInternal for #struct_name {}
  });

  match input.data {
    Struct(DataStruct { fields: syn::Fields::Named(ref fields), .. }) => {
      impl_structopt_for_struct(struct_name, &fields.named, &input.attrs)
    }
    Enum(ref e) => impl_structopt_for_enum(struct_name, &e.variants, &input.attrs),
    _ => abort_call_site!("structopt only supports non-tuple structs and enums"),
  }
}