diff options
Diffstat (limited to 'structopt/structopt-derive/src/lib.rs')
-rw-r--r-- | structopt/structopt-derive/src/lib.rs | 667 |
1 files changed, 0 insertions, 667 deletions
diff --git a/structopt/structopt-derive/src/lib.rs b/structopt/structopt-derive/src/lib.rs deleted file mode 100644 index 87eaf1f..0000000 --- a/structopt/structopt-derive/src/lib.rs +++ /dev/null @@ -1,667 +0,0 @@ -// 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)] - -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::{sub_type, Ty}, -}; - -use proc_macro2::{Span, TokenStream}; -use proc_macro_error::{abort, abort_call_site, proc_macro_error, set_dummy}; -use quote::{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, - 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, - parent_attribute.casing(), - parent_attribute.env_casing(), - ); - let kind = attrs.kind(); - match &*kind { - Kind::Subcommand(_) | Kind::Skip(_) => None, - Kind::FlattenStruct => { - 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(); - quote! {{ - let #app_var = #app_var#app_methods; - #( #args )* - #subcmd - #app_var - }} -} - -fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream { - let fields = fields.iter().map(|field| { - let attrs = Attrs::from_field( - field, - parent_attribute.casing(), - parent_attribute.env_casing(), - ); - let field_name = field.ident.as_ref().unwrap(); - let kind = attrs.kind(); - match &*kind { - 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::FlattenStruct => 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(LitStr::new(&name, Span::call_site())), - 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()), - parent_attribute.casing(), - parent_attribute.env_casing(), - ); - 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_call_site!("{}: tuple enums are not supported", variant.ident), - }; - - let name = attrs.cased_name(); - let from_attrs = attrs.top_level_methods(); - - quote! { - .subcommand({ - let #app_var = ::structopt::clap::SubCommand::with_name(#name); - let #app_var = #arg_block; - #app_var#from_attrs - }) - } - }); - - let app_methods = parent_attribute.top_level_methods(); - - quote! { - fn augment_clap<'a, 'b>( - app: ::structopt::clap::App<'a, 'b> - ) -> ::structopt::clap::App<'a, 'b> { - app #app_methods #( #subcommands )* - } - } -} - -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()) - .unwrap() - } - } -} - -fn gen_from_subcommand( - name: &Ident, - variants: &Punctuated<Variant, Comma>, - parent_attribute: &Attrs, -) -> TokenStream { - use syn::Fields::*; - - let match_arms = variants.iter().map(|variant| { - let attrs = Attrs::from_struct( - variant.span(), - &variant.attrs, - Name::Derived(variant.ident.clone()), - parent_attribute.casing(), - parent_attribute.env_casing(), - ); - 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_call_site!("{}: tuple enums are not supported", variant.ident), - }; - - quote! { - (#sub_name, Some(matches)) => - Some(#name :: #variant_name #constructor_block) - } - }); - - quote! { - fn from_subcommand<'a, 'b>( - sub: (&'b str, Option<&'b ::structopt::clap::ArgMatches<'a>>) - ) -> Option<Self> { - match sub { - #( #match_arms ),*, - _ => None - } - } - } -} - -#[cfg(feature = "paw")] -fn gen_paw_impl(name: &Ident) -> TokenStream { - quote! { - impl 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::all)] - #[allow(dead_code, unreachable_code)] - impl ::structopt::StructOpt for #name { - #clap_tokens - #from_clap - } - - #[allow(unused_variables)] - #[allow(unknown_lints)] - #[allow(clippy::all)] - #[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 augment_clap = gen_augment_clap_enum(variants, &basic_clap_app_gen.attrs); - let from_clap = gen_from_clap_enum(name); - let from_subcommand = gen_from_subcommand(name, variants, &basic_clap_app_gen.attrs); - let paw_impl = gen_paw_impl(name); - - let clap_tokens = basic_clap_app_gen.tokens; - quote! { - #[allow(unknown_lints)] - #[allow(unused_variables, dead_code, unreachable_code)] - #[allow(clippy)] - impl ::structopt::StructOpt for #name { - #clap_tokens - #from_clap - } - - #[allow(unused_variables)] - #[allow(unknown_lints)] - #[allow(clippy::all)] - #[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!() - } - } - }); - - 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"), - } -} |