diff options
Diffstat (limited to 'vendor/clap_derive/src/attrs.rs')
-rw-r--r-- | vendor/clap_derive/src/attrs.rs | 1412 |
1 files changed, 0 insertions, 1412 deletions
diff --git a/vendor/clap_derive/src/attrs.rs b/vendor/clap_derive/src/attrs.rs deleted file mode 100644 index 2c5b47d95..000000000 --- a/vendor/clap_derive/src/attrs.rs +++ /dev/null @@ -1,1412 +0,0 @@ -// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>, -// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and -// Ana Hobden (@hoverbear) <operator@hoverbear.org> -// -// 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 work was derived from Structopt (https://github.com/TeXitoi/structopt) -// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the -// MIT/Apache 2.0 license. - -use crate::{ - parse::*, - utils::{inner_type, is_simple_ty, process_doc_comment, Sp, Ty}, -}; - -use std::env; - -use heck::{ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase}; -use proc_macro2::{self, Span, TokenStream}; -use proc_macro_error::abort; -use quote::{quote, quote_spanned, ToTokens}; -use syn::{ - self, ext::IdentExt, spanned::Spanned, Attribute, Expr, Field, Ident, LitStr, MetaNameValue, - Type, Variant, -}; - -/// Default casing style for generated arguments. -pub const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab; - -/// Default casing style for environment variables -pub const DEFAULT_ENV_CASING: CasingStyle = CasingStyle::ScreamingSnake; - -#[derive(Clone)] -pub struct Attrs { - name: Name, - casing: Sp<CasingStyle>, - env_casing: Sp<CasingStyle>, - ty: Option<Type>, - doc_comment: Vec<Method>, - methods: Vec<Method>, - value_parser: Option<ValueParser>, - action: Option<Action>, - parser: Option<Sp<Parser>>, - verbatim_doc_comment: Option<Ident>, - next_display_order: Option<Method>, - next_help_heading: Option<Method>, - help_heading: Option<Method>, - is_enum: bool, - is_positional: bool, - kind: Sp<Kind>, -} - -impl Attrs { - pub fn from_struct( - span: Span, - attrs: &[Attribute], - name: Name, - argument_casing: Sp<CasingStyle>, - env_casing: Sp<CasingStyle>, - ) -> Self { - let mut res = Self::new(span, name, None, argument_casing, env_casing); - res.push_attrs(attrs); - res.push_doc_comment(attrs, "about"); - - if let Some(value_parser) = res.value_parser.as_ref() { - abort!( - value_parser.span(), - "`value_parser` attribute is only allowed on fields" - ); - } - if let Some(action) = res.action.as_ref() { - abort!( - action.span(), - "`action` attribute is only allowed on fields" - ); - } - if let Some(parser) = res.parser.as_ref() { - abort!(parser.span(), "`parse` attribute is only allowed on fields"); - } - match &*res.kind { - Kind::Subcommand(_) => abort!(res.kind.span(), "subcommand is only allowed on fields"), - Kind::Skip(_) => abort!(res.kind.span(), "skip is only allowed on fields"), - Kind::Arg(_) => res, - Kind::FromGlobal(_) => abort!(res.kind.span(), "from_global is only allowed on fields"), - Kind::Flatten => abort!(res.kind.span(), "flatten is only allowed on fields"), - Kind::ExternalSubcommand => abort!( - res.kind.span(), - "external_subcommand is only allowed on fields" - ), - } - } - - pub fn from_variant( - variant: &Variant, - struct_casing: Sp<CasingStyle>, - env_casing: Sp<CasingStyle>, - ) -> Self { - let name = variant.ident.clone(); - let mut res = Self::new( - variant.span(), - Name::Derived(name), - None, - struct_casing, - env_casing, - ); - res.push_attrs(&variant.attrs); - res.push_doc_comment(&variant.attrs, "about"); - - match &*res.kind { - Kind::Flatten => { - if let Some(value_parser) = res.value_parser.as_ref() { - abort!( - value_parser.span(), - "`value_parser` attribute is not allowed for flattened entry" - ); - } - if let Some(action) = res.action.as_ref() { - abort!( - action.span(), - "`action` attribute is not allowed for flattened entry" - ); - } - if let Some(parser) = res.parser.as_ref() { - abort!( - parser.span(), - "parse attribute is not allowed for flattened entry" - ); - } - if res.has_explicit_methods() { - abort!( - res.kind.span(), - "methods are not allowed for flattened entry" - ); - } - - // ignore doc comments - res.doc_comment = vec![]; - } - - Kind::ExternalSubcommand => (), - - Kind::Subcommand(_) => { - if let Some(value_parser) = res.value_parser.as_ref() { - abort!( - value_parser.span(), - "`value_parser` attribute is not allowed for subcommand" - ); - } - if let Some(action) = res.action.as_ref() { - abort!( - action.span(), - "`action` attribute is not allowed for subcommand" - ); - } - if let Some(parser) = res.parser.as_ref() { - abort!( - parser.span(), - "parse attribute is not allowed for subcommand" - ); - } - - use syn::Fields::*; - use syn::FieldsUnnamed; - let field_ty = match variant.fields { - Named(_) => { - abort!(variant.span(), "structs are not allowed for subcommand"); - } - Unit => abort!(variant.span(), "unit-type is not allowed for subcommand"), - Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { - &unnamed[0].ty - } - Unnamed(..) => { - abort!( - variant, - "non single-typed tuple is not allowed for subcommand" - ) - } - }; - let ty = Ty::from_syn_ty(field_ty); - match *ty { - Ty::OptionOption => { - abort!( - field_ty, - "Option<Option<T>> type is not allowed for subcommand" - ); - } - Ty::OptionVec => { - abort!( - field_ty, - "Option<Vec<T>> type is not allowed for subcommand" - ); - } - _ => (), - } - - res.kind = Sp::new(Kind::Subcommand(ty), res.kind.span()); - } - Kind::Skip(_) => (), - Kind::FromGlobal(_) => { - abort!(res.kind.span(), "from_global is not supported on variants"); - } - Kind::Arg(_) => (), - } - - res - } - - pub fn from_value_enum_variant( - variant: &Variant, - argument_casing: Sp<CasingStyle>, - env_casing: Sp<CasingStyle>, - ) -> Self { - let mut res = Self::new( - variant.span(), - Name::Derived(variant.ident.clone()), - None, - argument_casing, - env_casing, - ); - res.push_attrs(&variant.attrs); - res.push_doc_comment(&variant.attrs, "help"); - - if let Some(value_parser) = res.value_parser.as_ref() { - abort!( - value_parser.span(), - "`value_parser` attribute is only allowed on fields" - ); - } - if let Some(action) = res.action.as_ref() { - abort!( - action.span(), - "`action` attribute is only allowed on fields" - ); - } - if let Some(parser) = res.parser.as_ref() { - abort!(parser.span(), "`parse` attribute is only allowed on fields"); - } - match &*res.kind { - Kind::Subcommand(_) => abort!(res.kind.span(), "subcommand is only allowed on fields"), - Kind::Skip(_) => res, - Kind::Arg(_) => res, - Kind::FromGlobal(_) => abort!(res.kind.span(), "from_global is only allowed on fields"), - Kind::Flatten => abort!(res.kind.span(), "flatten is only allowed on fields"), - Kind::ExternalSubcommand => abort!( - res.kind.span(), - "external_subcommand is only allowed on fields" - ), - } - } - - pub fn from_field( - field: &Field, - struct_casing: Sp<CasingStyle>, - env_casing: Sp<CasingStyle>, - ) -> Self { - let name = field.ident.clone().unwrap(); - let mut res = Self::new( - field.span(), - Name::Derived(name), - Some(field.ty.clone()), - struct_casing, - env_casing, - ); - res.push_attrs(&field.attrs); - res.push_doc_comment(&field.attrs, "help"); - - match &*res.kind { - Kind::Flatten => { - if let Some(value_parser) = res.value_parser.as_ref() { - abort!( - value_parser.span(), - "`value_parser` attribute is not allowed for flattened entry" - ); - } - if let Some(action) = res.action.as_ref() { - abort!( - action.span(), - "`action` attribute is not allowed for flattened entry" - ); - } - if let Some(parser) = res.parser.as_ref() { - abort!( - parser.span(), - "parse attribute is not allowed for flattened entry" - ); - } - if res.has_explicit_methods() { - abort!( - res.kind.span(), - "methods are not allowed for flattened entry" - ); - } - - // ignore doc comments - res.doc_comment = vec![]; - } - - Kind::ExternalSubcommand => { - abort! { res.kind.span(), - "`external_subcommand` can be used only on enum variants" - } - } - - Kind::Subcommand(_) => { - if let Some(value_parser) = res.value_parser.as_ref() { - abort!( - value_parser.span(), - "`value_parser` attribute is not allowed for subcommand" - ); - } - if let Some(action) = res.action.as_ref() { - abort!( - action.span(), - "`action` attribute is not allowed for subcommand" - ); - } - if let Some(parser) = res.parser.as_ref() { - abort!( - parser.span(), - "parse attribute is not allowed for subcommand" - ); - } - if res.has_explicit_methods() { - abort!( - res.kind.span(), - "methods in attributes are not allowed for subcommand" - ); - } - - let ty = Ty::from_syn_ty(&field.ty); - match *ty { - Ty::OptionOption => { - abort!( - field.ty, - "Option<Option<T>> type is not allowed for subcommand" - ); - } - Ty::OptionVec => { - abort!( - field.ty, - "Option<Vec<T>> type is not allowed for subcommand" - ); - } - _ => (), - } - - res.kind = Sp::new(Kind::Subcommand(ty), res.kind.span()); - } - Kind::Skip(_) => { - if res.has_explicit_methods() { - abort!( - res.kind.span(), - "methods are not allowed for skipped fields" - ); - } - } - Kind::FromGlobal(orig_ty) => { - let ty = Ty::from_syn_ty(&field.ty); - res.kind = Sp::new(Kind::FromGlobal(ty), orig_ty.span()); - } - Kind::Arg(_) => { - let mut ty = Ty::from_syn_ty(&field.ty); - if res.parser.is_some() { - if let Some(value_parser) = res.value_parser.as_ref() { - abort!( - value_parser.span(), - "`value_parser` attribute conflicts with `parse` attribute" - ); - } - if let Some(action) = res.action.as_ref() { - abort!( - action.span(), - "`action` attribute conflicts with `parse` attribute" - ); - } - match *ty { - Ty::Option | Ty::Vec | Ty::OptionVec => (), - _ => ty = Sp::new(Ty::Other, ty.span()), - } - } - - match *ty { - Ty::Option => { - if let Some(m) = res.find_default_method() { - abort!(m.name, "default_value is meaningless for Option") - } - } - Ty::OptionOption => { - if res.is_positional() { - abort!( - field.ty, - "Option<Option<T>> type is meaningless for positional argument" - ) - } - } - Ty::OptionVec => { - if res.is_positional() { - abort!( - field.ty, - "Option<Vec<T>> type is meaningless for positional argument" - ) - } - } - - _ => (), - } - res.kind = Sp::new( - Kind::Arg(ty), - field - .ident - .as_ref() - .map(|i| i.span()) - .unwrap_or_else(|| field.ty.span()), - ); - } - } - - res - } - - fn new( - default_span: Span, - name: Name, - ty: Option<Type>, - casing: Sp<CasingStyle>, - env_casing: Sp<CasingStyle>, - ) -> Self { - Self { - name, - ty, - casing, - env_casing, - doc_comment: vec![], - methods: vec![], - value_parser: None, - action: None, - parser: None, - verbatim_doc_comment: None, - next_display_order: None, - next_help_heading: None, - help_heading: None, - is_enum: false, - is_positional: true, - kind: Sp::new(Kind::Arg(Sp::new(Ty::Other, default_span)), default_span), - } - } - - fn push_method(&mut self, name: Ident, arg: impl ToTokens) { - if name == "name" || name == "id" { - self.name = Name::Assigned(quote!(#arg)); - } else if name == "value_parser" { - self.value_parser = Some(ValueParser::Explicit(Method::new(name, quote!(#arg)))); - } else if name == "action" { - self.action = Some(Action::Explicit(Method::new(name, quote!(#arg)))); - } else { - if name == "short" || name == "long" { - self.is_positional = false; - } - self.methods.push(Method::new(name, quote!(#arg))); - } - } - - fn push_attrs(&mut self, attrs: &[Attribute]) { - use ClapAttr::*; - - let parsed = parse_clap_attributes(attrs); - for attr in &parsed { - let attr = attr.clone(); - match attr { - Short(ident) => { - self.push_method(ident, self.name.clone().translate_char(*self.casing)); - } - - Long(ident) => { - self.push_method(ident, self.name.clone().translate(*self.casing)); - } - - ValueParser(ident) => { - use crate::attrs::ValueParser; - self.value_parser = Some(ValueParser::Implicit(ident)); - } - - Action(ident) => { - use crate::attrs::Action; - self.action = Some(Action::Implicit(ident)); - } - - Env(ident) => { - self.push_method(ident, self.name.clone().translate(*self.env_casing)); - } - - ValueEnum(_) => self.is_enum = true, - - FromGlobal(ident) => { - let ty = Sp::call_site(Ty::Other); - let kind = Sp::new(Kind::FromGlobal(ty), ident.span()); - self.set_kind(kind); - } - - Subcommand(ident) => { - let ty = Sp::call_site(Ty::Other); - let kind = Sp::new(Kind::Subcommand(ty), ident.span()); - self.set_kind(kind); - } - - ExternalSubcommand(ident) => { - let kind = Sp::new(Kind::ExternalSubcommand, ident.span()); - self.set_kind(kind); - } - - Flatten(ident) => { - let kind = Sp::new(Kind::Flatten, ident.span()); - self.set_kind(kind); - } - - Skip(ident, expr) => { - let kind = Sp::new(Kind::Skip(expr), ident.span()); - self.set_kind(kind); - } - - VerbatimDocComment(ident) => self.verbatim_doc_comment = Some(ident), - - DefaultValueT(ident, expr) => { - let ty = if let Some(ty) = self.ty.as_ref() { - ty - } else { - abort!( - ident, - "#[clap(default_value_t)] (without an argument) can be used \ - only on field level"; - - note = "see \ - https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes") - }; - - let val = if let Some(expr) = expr { - quote!(#expr) - } else { - quote!(<#ty as ::std::default::Default>::default()) - }; - - let val = if parsed.iter().any(|a| matches!(a, ValueEnum(_))) { - quote_spanned!(ident.span()=> { - { - let val: #ty = #val; - clap::ValueEnum::to_possible_value(&val).unwrap().get_name() - } - }) - } else { - quote_spanned!(ident.span()=> { - static DEFAULT_VALUE: clap::__macro_refs::once_cell::sync::Lazy<String> = clap::__macro_refs::once_cell::sync::Lazy::new(|| { - let val: #ty = #val; - ::std::string::ToString::to_string(&val) - }); - &*DEFAULT_VALUE - }) - }; - - let raw_ident = Ident::new("default_value", ident.span()); - self.methods.push(Method::new(raw_ident, val)); - } - - DefaultValuesT(ident, expr) => { - let ty = if let Some(ty) = self.ty.as_ref() { - ty - } else { - abort!( - ident, - "#[clap(default_values_t)] (without an argument) can be used \ - only on field level"; - - note = "see \ - https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes") - }; - - let container_type = Ty::from_syn_ty(ty); - if *container_type != Ty::Vec { - abort!( - ident, - "#[clap(default_values_t)] can be used only on Vec types"; - - note = "see \ - https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes") - } - let inner_type = inner_type(ty); - - // Use `Borrow<#inner_type>` so we accept `&Vec<#inner_type>` and - // `Vec<#inner_type>`. - let val = if parsed.iter().any(|a| matches!(a, ValueEnum(_))) { - quote_spanned!(ident.span()=> { - { - fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> Vec<&'static str> - where - T: ::std::borrow::Borrow<#inner_type> - { - iterable - .into_iter() - .map(|val| { - clap::ValueEnum::to_possible_value(val.borrow()).unwrap().get_name() - }) - .collect() - - } - - static DEFAULT_VALUES: clap::__macro_refs::once_cell::sync::Lazy<Vec<&str>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| { - iter_to_vals(#expr) - }); - &*DEFAULT_VALUES.as_slice() - } - }) - } else { - quote_spanned!(ident.span()=> { - { - fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> Vec<String> - where - T: ::std::borrow::Borrow<#inner_type> - { - iterable.into_iter().map(|val| val.borrow().to_string()).collect() - - } - - static DEFAULT_STRINGS: clap::__macro_refs::once_cell::sync::Lazy<Vec<::std::string::String>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| { - iter_to_vals(#expr) - }); - - static DEFAULT_VALUES: clap::__macro_refs::once_cell::sync::Lazy<Vec<&str>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| { - DEFAULT_STRINGS.iter().map(::std::string::String::as_str).collect() - }); - &*DEFAULT_VALUES.as_slice() - } - }) - }; - - self.methods - .push(Method::new(Ident::new("default_values", ident.span()), val)); - } - - DefaultValueOsT(ident, expr) => { - let ty = if let Some(ty) = self.ty.as_ref() { - ty - } else { - abort!( - ident, - "#[clap(default_value_os_t)] (without an argument) can be used \ - only on field level"; - - note = "see \ - https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes") - }; - - let val = if let Some(expr) = expr { - quote!(#expr) - } else { - quote!(<#ty as ::std::default::Default>::default()) - }; - - let val = if parsed.iter().any(|a| matches!(a, ValueEnum(_))) { - quote_spanned!(ident.span()=> { - { - let val: #ty = #val; - clap::ValueEnum::to_possible_value(&val).unwrap().get_name() - } - }) - } else { - quote_spanned!(ident.span()=> { - static DEFAULT_VALUE: clap::__macro_refs::once_cell::sync::Lazy<::std::ffi::OsString> = clap::__macro_refs::once_cell::sync::Lazy::new(|| { - let val: #ty = #val; - ::std::ffi::OsString::from(val) - }); - &*DEFAULT_VALUE - }) - }; - - let raw_ident = Ident::new("default_value_os", ident.span()); - self.methods.push(Method::new(raw_ident, val)); - } - - DefaultValuesOsT(ident, expr) => { - let ty = if let Some(ty) = self.ty.as_ref() { - ty - } else { - abort!( - ident, - "#[clap(default_values_os_t)] (without an argument) can be used \ - only on field level"; - - note = "see \ - https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes") - }; - - let container_type = Ty::from_syn_ty(ty); - if *container_type != Ty::Vec { - abort!( - ident, - "#[clap(default_values_os_t)] can be used only on Vec types"; - - note = "see \ - https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes") - } - let inner_type = inner_type(ty); - - // Use `Borrow<#inner_type>` so we accept `&Vec<#inner_type>` and - // `Vec<#inner_type>`. - let val = if parsed.iter().any(|a| matches!(a, ValueEnum(_))) { - quote_spanned!(ident.span()=> { - { - fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> Vec<&'static ::std::ffi::OsStr> - where - T: ::std::borrow::Borrow<#inner_type> - { - iterable - .into_iter() - .map(|val| { - clap::ValueEnum::to_possible_value(val.borrow()).unwrap().get_name() - }) - .map(::std::ffi::OsStr::new) - .collect() - - } - - static DEFAULT_VALUES: clap::__macro_refs::once_cell::sync::Lazy<Vec<&::std::ffi::OsStr>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| { - iter_to_vals(#expr) - }); - &*DEFAULT_VALUES.as_slice() - } - }) - } else { - quote_spanned!(ident.span()=> { - { - fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> Vec<::std::ffi::OsString> - where - T: ::std::borrow::Borrow<#inner_type> - { - iterable.into_iter().map(|val| val.borrow().into()).collect() - - } - - static DEFAULT_OS_STRINGS: clap::__macro_refs::once_cell::sync::Lazy<Vec<::std::ffi::OsString>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| { - iter_to_vals(#expr) - }); - - static DEFAULT_VALUES: clap::__macro_refs::once_cell::sync::Lazy<Vec<&::std::ffi::OsStr>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| { - DEFAULT_OS_STRINGS.iter().map(::std::ffi::OsString::as_os_str).collect() - }); - &*DEFAULT_VALUES.as_slice() - } - }) - }; - - self.methods.push(Method::new( - Ident::new("default_values_os", ident.span()), - val, - )); - } - - NextDisplayOrder(ident, expr) => { - self.next_display_order = Some(Method::new(ident, quote!(#expr))); - } - - HelpHeading(ident, expr) => { - self.help_heading = Some(Method::new(ident, quote!(#expr))); - } - NextHelpHeading(ident, expr) => { - self.next_help_heading = Some(Method::new(ident, quote!(#expr))); - } - - About(ident) => { - if let Some(method) = Method::from_env(ident, "CARGO_PKG_DESCRIPTION") { - self.methods.push(method); - } - } - - Author(ident) => { - if let Some(method) = Method::from_env(ident, "CARGO_PKG_AUTHORS") { - self.methods.push(method); - } - } - - Version(ident) => { - if let Some(method) = Method::from_env(ident, "CARGO_PKG_VERSION") { - self.methods.push(method); - } - } - - NameLitStr(name, lit) => { - self.push_method(name, lit); - } - - NameExpr(name, expr) => { - self.push_method(name, expr); - } - - MethodCall(name, args) => self.push_method(name, quote!(#(#args),*)), - - RenameAll(_, casing_lit) => { - self.casing = CasingStyle::from_lit(casing_lit); - } - - RenameAllEnv(_, casing_lit) => { - self.env_casing = CasingStyle::from_lit(casing_lit); - } - - Parse(ident, spec) => { - self.parser = Some(Parser::from_spec(ident, spec)); - } - } - } - } - - fn push_doc_comment(&mut self, attrs: &[Attribute], name: &str) { - use syn::Lit::*; - use syn::Meta::*; - - let comment_parts: Vec<_> = attrs - .iter() - .filter(|attr| attr.path.is_ident("doc")) - .filter_map(|attr| { - if let Ok(NameValue(MetaNameValue { lit: Str(s), .. })) = attr.parse_meta() { - Some(s.value()) - } else { - // non #[doc = "..."] attributes are not our concern - // we leave them for rustc to handle - None - } - }) - .collect(); - - self.doc_comment = - process_doc_comment(comment_parts, name, self.verbatim_doc_comment.is_none()); - } - - fn set_kind(&mut self, kind: Sp<Kind>) { - if let Kind::Arg(_) = *self.kind { - self.kind = kind; - } else { - abort!( - kind.span(), - "`subcommand`, `flatten`, `external_subcommand` and `skip` cannot be used together" - ); - } - } - - pub fn find_method(&self, name: &str) -> Option<&Method> { - self.methods.iter().find(|m| m.name == name) - } - - pub fn find_default_method(&self) -> Option<&Method> { - self.methods - .iter() - .find(|m| m.name == "default_value" || m.name == "default_value_os") - } - - /// generate methods from attributes on top of struct or enum - pub fn initial_top_level_methods(&self) -> TokenStream { - let next_display_order = self.next_display_order.as_ref().into_iter(); - let next_help_heading = self.next_help_heading.as_ref().into_iter(); - let help_heading = self.help_heading.as_ref().into_iter(); - quote!( - #(#next_display_order)* - #(#next_help_heading)* - #(#help_heading)* - ) - } - - pub fn final_top_level_methods(&self) -> TokenStream { - let methods = &self.methods; - let doc_comment = &self.doc_comment; - - quote!( #(#doc_comment)* #(#methods)*) - } - - /// generate methods on top of a field - pub fn field_methods(&self, supports_long_help: bool) -> proc_macro2::TokenStream { - let methods = &self.methods; - let help_heading = self.help_heading.as_ref().into_iter(); - match supports_long_help { - true => { - let doc_comment = &self.doc_comment; - quote!( #(#doc_comment)* #(#help_heading)* #(#methods)* ) - } - false => { - let doc_comment = self - .doc_comment - .iter() - .filter(|mth| mth.name != "long_help"); - quote!( #(#doc_comment)* #(#help_heading)* #(#methods)* ) - } - } - } - - pub fn next_display_order(&self) -> TokenStream { - let next_display_order = self.next_display_order.as_ref().into_iter(); - quote!( #(#next_display_order)* ) - } - - pub fn next_help_heading(&self) -> TokenStream { - let next_help_heading = self.next_help_heading.as_ref().into_iter(); - let help_heading = self.help_heading.as_ref().into_iter(); - quote!( #(#next_help_heading)* #(#help_heading)* ) - } - - #[cfg(feature = "unstable-v4")] - pub fn id(&self) -> TokenStream { - self.name.clone().raw() - } - - #[cfg(not(feature = "unstable-v4"))] - pub fn id(&self) -> TokenStream { - self.cased_name() - } - - pub fn cased_name(&self) -> TokenStream { - self.name.clone().translate(*self.casing) - } - - pub fn value_name(&self) -> TokenStream { - self.name.clone().translate(CasingStyle::ScreamingSnake) - } - - pub fn value_parser(&self, field_type: &Type) -> Method { - self.value_parser - .clone() - .map(|p| { - let inner_type = inner_type(field_type); - p.resolve(inner_type) - }) - .unwrap_or_else(|| { - if let Some(action) = self.action.as_ref() { - let inner_type = inner_type(field_type); - let span = action.span(); - default_value_parser(inner_type, span) - } else if !self.ignore_parser() || cfg!(not(feature = "unstable-v4")) { - self.parser(field_type).value_parser() - } else { - let inner_type = inner_type(field_type); - let span = self - .action - .as_ref() - .map(|a| a.span()) - .unwrap_or_else(|| self.kind.span()); - default_value_parser(inner_type, span) - } - }) - } - - pub fn action(&self, field_type: &Type) -> Method { - self.action - .clone() - .map(|p| p.resolve(field_type)) - .unwrap_or_else(|| { - if let Some(value_parser) = self.value_parser.as_ref() { - let span = value_parser.span(); - default_action(field_type, span) - } else if !self.ignore_parser() || cfg!(not(feature = "unstable-v4")) { - self.parser(field_type).action() - } else { - let span = self - .value_parser - .as_ref() - .map(|a| a.span()) - .unwrap_or_else(|| self.kind.span()); - default_action(field_type, span) - } - }) - } - - #[cfg(feature = "unstable-v4")] - pub fn ignore_parser(&self) -> bool { - self.parser.is_none() - } - - #[cfg(not(feature = "unstable-v4"))] - pub fn ignore_parser(&self) -> bool { - self.value_parser.is_some() || self.action.is_some() - } - - pub fn explicit_parser(&self) -> bool { - self.parser.is_some() - } - - pub fn parser(&self, field_type: &Type) -> Sp<Parser> { - self.parser - .clone() - .unwrap_or_else(|| Parser::from_type(field_type, self.kind.span())) - } - - pub fn kind(&self) -> Sp<Kind> { - self.kind.clone() - } - - pub fn is_enum(&self) -> bool { - self.is_enum - } - - pub fn is_positional(&self) -> bool { - self.is_positional - } - - pub fn ignore_case(&self) -> TokenStream { - let method = self.find_method("ignore_case"); - - if let Some(method) = method { - method.args.clone() - } else { - quote! { false } - } - } - - pub fn casing(&self) -> Sp<CasingStyle> { - self.casing.clone() - } - - pub fn env_casing(&self) -> Sp<CasingStyle> { - self.env_casing.clone() - } - - pub fn has_explicit_methods(&self) -> bool { - self.methods - .iter() - .any(|m| m.name != "help" && m.name != "long_help") - } -} - -#[derive(Clone)] -enum ValueParser { - Explicit(Method), - Implicit(Ident), -} - -impl ValueParser { - fn resolve(self, inner_type: &Type) -> Method { - match self { - Self::Explicit(method) => method, - Self::Implicit(ident) => default_value_parser(inner_type, ident.span()), - } - } - - fn span(&self) -> Span { - match self { - Self::Explicit(method) => method.name.span(), - Self::Implicit(ident) => ident.span(), - } - } -} - -fn default_value_parser(inner_type: &Type, span: Span) -> Method { - let func = Ident::new("value_parser", span); - Method::new( - func, - quote_spanned! { span=> - clap::value_parser!(#inner_type) - }, - ) -} - -#[derive(Clone)] -pub enum Action { - Explicit(Method), - Implicit(Ident), -} - -impl Action { - pub fn resolve(self, field_type: &Type) -> Method { - match self { - Self::Explicit(method) => method, - Self::Implicit(ident) => default_action(field_type, ident.span()), - } - } - - pub fn span(&self) -> Span { - match self { - Self::Explicit(method) => method.name.span(), - Self::Implicit(ident) => ident.span(), - } - } -} - -fn default_action(field_type: &Type, span: Span) -> Method { - let ty = Ty::from_syn_ty(field_type); - let args = match *ty { - Ty::Vec | Ty::OptionVec => { - quote_spanned! { span=> - clap::ArgAction::Append - } - } - Ty::Option | Ty::OptionOption => { - quote_spanned! { span=> - clap::ArgAction::Set - } - } - _ => { - if is_simple_ty(field_type, "bool") { - quote_spanned! { span=> - clap::ArgAction::SetTrue - } - } else { - quote_spanned! { span=> - clap::ArgAction::Set - } - } - } - }; - - let func = Ident::new("action", span); - Method::new(func, args) -} - -#[allow(clippy::large_enum_variant)] -#[derive(Clone)] -pub enum Kind { - Arg(Sp<Ty>), - FromGlobal(Sp<Ty>), - Subcommand(Sp<Ty>), - Flatten, - Skip(Option<Expr>), - ExternalSubcommand, -} - -#[derive(Clone)] -pub struct Method { - name: Ident, - args: TokenStream, -} - -impl Method { - pub fn new(name: Ident, args: TokenStream) -> Self { - Method { name, args } - } - - fn from_env(ident: Ident, env_var: &str) -> Option<Self> { - let mut lit = match env::var(env_var) { - Ok(val) => { - if val.is_empty() { - return None; - } - LitStr::new(&val, ident.span()) - } - Err(_) => { - abort!(ident, - "cannot derive `{}` from Cargo.toml", ident; - note = "`{}` environment variable is not set", env_var; - help = "use `{} = \"...\"` to set {} manually", ident, ident; - ); - } - }; - - if ident == "author" { - let edited = process_author_str(&lit.value()); - lit = LitStr::new(&edited, lit.span()); - } - - Some(Method::new(ident, quote!(#lit))) - } - - pub(crate) fn args(&self) -> &TokenStream { - &self.args - } -} - -impl ToTokens for Method { - fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) { - let Method { ref name, ref args } = self; - - let tokens = quote!( .#name(#args) ); - - tokens.to_tokens(ts); - } -} - -/// replace all `:` with `, ` when not inside the `<>` -/// -/// `"author1:author2:author3" => "author1, author2, author3"` -/// `"author1 <http://website1.com>:author2" => "author1 <http://website1.com>, author2" -fn process_author_str(author: &str) -> String { - let mut res = String::with_capacity(author.len()); - let mut inside_angle_braces = 0usize; - - for ch in author.chars() { - if inside_angle_braces > 0 && ch == '>' { - inside_angle_braces -= 1; - res.push(ch); - } else if ch == '<' { - inside_angle_braces += 1; - res.push(ch); - } else if inside_angle_braces == 0 && ch == ':' { - res.push_str(", "); - } else { - res.push(ch); - } - } - - res -} - -#[derive(Clone)] -pub struct Parser { - pub kind: Sp<ParserKind>, - pub func: TokenStream, -} - -impl Parser { - fn from_type(field_type: &Type, span: Span) -> Sp<Self> { - if is_simple_ty(field_type, "bool") { - let kind = Sp::new(ParserKind::FromFlag, span); - let func = quote_spanned!(span=> ::std::convert::From::from); - Sp::new(Parser { kind, func }, span) - } else { - let kind = Sp::new(ParserKind::TryFromStr, span); - let func = quote_spanned!(span=> ::std::str::FromStr::from_str); - Sp::new(Parser { kind, func }, span) - } - } - - fn from_spec(parse_ident: Ident, spec: ParserSpec) -> Sp<Self> { - use self::ParserKind::*; - - let kind = match &*spec.kind.to_string() { - "from_str" => FromStr, - "try_from_str" => TryFromStr, - "from_os_str" => FromOsStr, - "try_from_os_str" => TryFromOsStr, - "from_occurrences" => FromOccurrences, - "from_flag" => FromFlag, - s => abort!(spec.kind.span(), "unsupported parser `{}`", s), - }; - - let func = match spec.parse_func { - None => match kind { - FromStr | FromOsStr => { - quote_spanned!(spec.kind.span()=> ::std::convert::From::from) - } - TryFromStr => quote_spanned!(spec.kind.span()=> ::std::str::FromStr::from_str), - TryFromOsStr => abort!( - spec.kind.span(), - "you must set parser for `try_from_os_str` explicitly" - ), - FromOccurrences => quote_spanned!(spec.kind.span()=> { |v| v as _ }), - FromFlag => quote_spanned!(spec.kind.span()=> ::std::convert::From::from), - }, - - Some(func) => match func { - Expr::Path(_) => quote!(#func), - _ => abort!(func, "`parse` argument must be a function path"), - }, - }; - - let kind = Sp::new(kind, spec.kind.span()); - let parser = Parser { kind, func }; - Sp::new(parser, parse_ident.span()) - } - - fn value_parser(&self) -> Method { - let func = Ident::new("value_parser", self.kind.span()); - match *self.kind { - ParserKind::FromStr | ParserKind::TryFromStr => Method::new( - func, - quote_spanned! { self.kind.span()=> - clap::builder::ValueParser::string()}, - ), - ParserKind::FromOsStr | ParserKind::TryFromOsStr => Method::new( - func, - quote_spanned! { self.kind.span()=> clap::builder::ValueParser::os_string()}, - ), - ParserKind::FromOccurrences => Method::new( - func, - quote_spanned! { self.kind.span()=> clap::value_parser!(u64)}, - ), - ParserKind::FromFlag => Method::new( - func, - quote_spanned! { self.kind.span()=> clap::builder::ValueParser::bool()}, - ), - } - } - - fn action(&self) -> Method { - let func = Ident::new("action", self.kind.span()); - match *self.kind { - ParserKind::FromStr - | ParserKind::TryFromStr - | ParserKind::FromOsStr - | ParserKind::TryFromOsStr => Method::new( - func, - quote_spanned! { self.kind.span()=> clap::ArgAction::StoreValue}, - ), - ParserKind::FromOccurrences | ParserKind::FromFlag => Method::new( - func, - quote_spanned! { self.kind.span()=> clap::ArgAction::IncOccurrence}, - ), - } - } -} - -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum ParserKind { - FromStr, - TryFromStr, - FromOsStr, - TryFromOsStr, - FromOccurrences, - FromFlag, -} - -/// Defines the casing for the attributes long representation. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum CasingStyle { - /// Indicate word boundaries with uppercase letter, excluding the first word. - Camel, - /// Keep all letters lowercase and indicate word boundaries with hyphens. - Kebab, - /// Indicate word boundaries with uppercase letter, including the first word. - Pascal, - /// Keep all letters uppercase and indicate word boundaries with underscores. - ScreamingSnake, - /// Keep all letters lowercase and indicate word boundaries with underscores. - Snake, - /// Keep all letters lowercase and remove word boundaries. - Lower, - /// Keep all letters uppercase and remove word boundaries. - Upper, - /// Use the original attribute name defined in the code. - Verbatim, -} - -impl CasingStyle { - fn from_lit(name: LitStr) -> Sp<Self> { - use self::CasingStyle::*; - - let normalized = name.value().to_upper_camel_case().to_lowercase(); - let cs = |kind| Sp::new(kind, name.span()); - - match normalized.as_ref() { - "camel" | "camelcase" => cs(Camel), - "kebab" | "kebabcase" => cs(Kebab), - "pascal" | "pascalcase" => cs(Pascal), - "screamingsnake" | "screamingsnakecase" => cs(ScreamingSnake), - "snake" | "snakecase" => cs(Snake), - "lower" | "lowercase" => cs(Lower), - "upper" | "uppercase" => cs(Upper), - "verbatim" | "verbatimcase" => cs(Verbatim), - s => abort!(name, "unsupported casing: `{}`", s), - } - } -} - -#[derive(Clone)] -pub enum Name { - Derived(Ident), - Assigned(TokenStream), -} - -impl Name { - #[cfg(feature = "unstable-v4")] - pub fn raw(self) -> TokenStream { - match self { - Name::Assigned(tokens) => tokens, - Name::Derived(ident) => { - let s = ident.unraw().to_string(); - quote_spanned!(ident.span()=> #s) - } - } - } - - pub fn translate(self, style: CasingStyle) -> TokenStream { - use CasingStyle::*; - - match self { - Name::Assigned(tokens) => tokens, - Name::Derived(ident) => { - let s = ident.unraw().to_string(); - let s = match style { - Pascal => s.to_upper_camel_case(), - Kebab => s.to_kebab_case(), - Camel => s.to_lower_camel_case(), - ScreamingSnake => s.to_shouty_snake_case(), - Snake => s.to_snake_case(), - Lower => s.to_snake_case().replace('_', ""), - Upper => s.to_shouty_snake_case().replace('_', ""), - Verbatim => s, - }; - quote_spanned!(ident.span()=> #s) - } - } - } - - pub fn translate_char(self, style: CasingStyle) -> TokenStream { - use CasingStyle::*; - - match self { - Name::Assigned(tokens) => quote!( (#tokens).chars().next().unwrap() ), - Name::Derived(ident) => { - let s = ident.unraw().to_string(); - let s = match style { - Pascal => s.to_upper_camel_case(), - Kebab => s.to_kebab_case(), - Camel => s.to_lower_camel_case(), - ScreamingSnake => s.to_shouty_snake_case(), - Snake => s.to_snake_case(), - Lower => s.to_snake_case(), - Upper => s.to_shouty_snake_case(), - Verbatim => s, - }; - - let s = s.chars().next().unwrap(); - quote_spanned!(ident.span()=> #s) - } - } - } -} |