diff options
Diffstat (limited to 'vendor/clap_derive')
-rw-r--r-- | vendor/clap_derive/.cargo-checksum.json | 2 | ||||
-rw-r--r-- | vendor/clap_derive/Cargo.toml | 16 | ||||
-rw-r--r-- | vendor/clap_derive/README.md | 4 | ||||
-rw-r--r-- | vendor/clap_derive/src/attr.rs | 222 | ||||
-rw-r--r-- | vendor/clap_derive/src/attrs.rs | 1412 | ||||
-rw-r--r-- | vendor/clap_derive/src/derives/args.rs | 801 | ||||
-rw-r--r-- | vendor/clap_derive/src/derives/into_app.rs | 62 | ||||
-rw-r--r-- | vendor/clap_derive/src/derives/parser.rs | 90 | ||||
-rw-r--r-- | vendor/clap_derive/src/derives/subcommand.rs | 495 | ||||
-rw-r--r-- | vendor/clap_derive/src/derives/value_enum.rs | 85 | ||||
-rw-r--r-- | vendor/clap_derive/src/dummies.rs | 15 | ||||
-rw-r--r-- | vendor/clap_derive/src/item.rs | 1449 | ||||
-rw-r--r-- | vendor/clap_derive/src/lib.rs | 20 | ||||
-rw-r--r-- | vendor/clap_derive/src/parse.rs | 288 | ||||
-rw-r--r-- | vendor/clap_derive/src/utils/doc_comments.rs | 87 | ||||
-rw-r--r-- | vendor/clap_derive/src/utils/mod.rs | 4 | ||||
-rw-r--r-- | vendor/clap_derive/src/utils/spanned.rs | 9 | ||||
-rw-r--r-- | vendor/clap_derive/src/utils/ty.rs | 58 |
18 files changed, 2544 insertions, 2575 deletions
diff --git a/vendor/clap_derive/.cargo-checksum.json b/vendor/clap_derive/.cargo-checksum.json index 1dfa35a62..3171538b8 100644 --- a/vendor/clap_derive/.cargo-checksum.json +++ b/vendor/clap_derive/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"eaf10a4c685b527f604ea9f84fbe7095e15fe4424225c57481d0555a89dfb054","LICENSE-APACHE":"c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4","LICENSE-MIT":"0d687e1f07b58fe68bda74668ff6326125e5e5efa184cce755cd84ac535b7058","README.md":"4ec07b9c59a2ddcedcdc9d21d51c147b18157794b03ff2ea5ae57f2b5236943d","src/attrs.rs":"1c95a75fd377d19587f429d3f51544ac0ca96f868cce4ffe12d1163f7465fa73","src/derives/args.rs":"27a13d33f24f7f7668c82fa653978e909514bd39e808c7996fddb0d3a3c0e570","src/derives/into_app.rs":"02c36d403ad35e3d49de6042cacecf2a724561b5e3948b5aec4b45646e031877","src/derives/mod.rs":"ff7947fddee4b3d8365e29459a02458382e13df022aee822e09bce1ac255d3bb","src/derives/parser.rs":"9ca609e6e5bfb303e22b4ecb8aca9035571179920b0df91031e9f987559252e0","src/derives/subcommand.rs":"0513c48013c2bd9f450ffc7bfcbf84b3a3290e37ff571dd070798356c2b291ac","src/derives/value_enum.rs":"4d0958d3a767c82f2366d8fda20367c5aa033af616d09ef581e8abdb2e029a70","src/dummies.rs":"02f111bbd84b8f0525faeb9189c59cb2fd2df05ae6c7b014735c82feac5ff23f","src/lib.rs":"e4be47e8db06a6555a2a4cd1802d17c16a3b51ae0ec291b3ec75ef2b6517eb0e","src/parse.rs":"ceaac282cabccfac5bcf56ce02095c9acbf00dbfdd3c3009fa58e87bdde46218","src/utils/doc_comments.rs":"386859b274fd1e298aeaeafd2b20764db0957cbb4e6c9e8bab52a506a0b8ecda","src/utils/mod.rs":"ee28526587d2d80d5dba162f28191f44f6a0c296d0e52225eb74b90534d49b25","src/utils/spanned.rs":"e93600124c54985533f74fe23f096f4ada94b310eff97a16f0ce40fb0f690a3b","src/utils/ty.rs":"0a9b8c5dabfa28388b60423f4b6075a275b347258ac35301997cb0d7f763607f"},"package":"ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"}
\ No newline at end of file +{"files":{"Cargo.toml":"a5011ce50757048c6df89811613aa16a7c730d85a4ad7d2f99c78ab1b3d7c07b","LICENSE-APACHE":"c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4","LICENSE-MIT":"0d687e1f07b58fe68bda74668ff6326125e5e5efa184cce755cd84ac535b7058","README.md":"425bac05c6433fcda018d78beae9543e4421b2d715a459f5bdef67a927574991","src/attr.rs":"3827adb91905c0159d686e5d6fdac3cb7739e2cfd4d6e8df2f76542e787a4c31","src/derives/args.rs":"1478efc53beb005797573cc6d33747ac6ed7d20ad7cfb29fcd03c1610e477ed8","src/derives/into_app.rs":"955e04ebf2f53d8ba66c51b3e79f7c12443ef172666285d35b7c8b8295b77dce","src/derives/mod.rs":"ff7947fddee4b3d8365e29459a02458382e13df022aee822e09bce1ac255d3bb","src/derives/parser.rs":"b999328f722b5bdaa5fa8ff38b71445b36b146d529c753a4571d168063b44927","src/derives/subcommand.rs":"b2e416618734d6a2a1e032447b33e276bed88ca91145ffb5b9f40a29896b6ed6","src/derives/value_enum.rs":"2884db96889b8cb7db6518a309293f5517394c5b894f13e644d19449d48dfb2a","src/dummies.rs":"3a7e2c44b2d2c6bd100a0c01ea69403d02cf1742241506cbd9a45575050273cb","src/item.rs":"c60a7f1d62d2c90b631920bb6c4f65651bfe164c3698b27c04d695bed86d7940","src/lib.rs":"b996ae67cab42e30b0c7dfd0e65ef806bac1d8cbb30d04ad9c8e1b70580b298f","src/utils/doc_comments.rs":"1f3278eacc6add1e63e8f49118a30fb31f75a3642b49dba60585e7b3dc72a7ed","src/utils/mod.rs":"a441e099609b553af6e174d722bb56b660b37eb53393f6e75af658336986b5e3","src/utils/spanned.rs":"39f7fb9bda1a2c56c358ab60a23d459fffd913b966b819160fae8ae2f7f20075","src/utils/ty.rs":"c8e9cb8b46e514e3a189dadf1d2c2741ca1469049b2d40b5fa72b83689d37c6f"},"package":"684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"}
\ No newline at end of file diff --git a/vendor/clap_derive/Cargo.toml b/vendor/clap_derive/Cargo.toml index f74476e68..d87b57e3c 100644 --- a/vendor/clap_derive/Cargo.toml +++ b/vendor/clap_derive/Cargo.toml @@ -11,9 +11,9 @@ [package] edition = "2021" -rust-version = "1.56.1" +rust-version = "1.64.0" name = "clap_derive" -version = "3.2.18" +version = "4.1.0" include = [ "build.rs", "src/**/*", @@ -38,7 +38,6 @@ categories = [ ] license = "MIT OR Apache-2.0" repository = "https://github.com/clap-rs/clap/tree/master/clap_derive" -resolver = "2" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -48,13 +47,6 @@ shared-version = true dependent-version = "upgrade" tag-name = "v{{version}}" -[[package.metadata.release.pre-release-replacements]] -file = "README.md" -search = "github.com/clap-rs/clap/blob/[^/]+/" -replace = "github.com/clap-rs/clap/blob/{{tag_name}}/" -exactly = 2 -prerelease = true - [lib] bench = false proc-macro = true @@ -66,7 +58,7 @@ version = "0.4.0" version = "1" [dependencies.proc-macro2] -version = "1.0.28" +version = "1.0.42" [dependencies.quote] version = "1.0.9" @@ -80,4 +72,4 @@ debug = [] default = [] deprecated = [] raw-deprecated = ["deprecated"] -unstable-v4 = ["deprecated"] +unstable-v5 = ["deprecated"] diff --git a/vendor/clap_derive/README.md b/vendor/clap_derive/README.md index 17f649eda..1ca9ce6c9 100644 --- a/vendor/clap_derive/README.md +++ b/vendor/clap_derive/README.md @@ -3,8 +3,8 @@ Macro implementation for clap's derives. [docs.rs](https://docs.rs/clap) -- [Derive Tutorial](https://github.com/clap-rs/clap/blob/v3.2.18/examples/tutorial_derive/README.md) -- [Derive Reference](https://github.com/clap-rs/clap/blob/v3.2.18/examples/derive_ref/README.md) +- [Derive Tutorial](https://docs.rs/clap/latest/clap/_derive/_tutorial/index.html) +- [Derive Reference](https://docs.rs/clap/latest/clap/_derive/index.html) ## License diff --git a/vendor/clap_derive/src/attr.rs b/vendor/clap_derive/src/attr.rs new file mode 100644 index 000000000..e282a8f94 --- /dev/null +++ b/vendor/clap_derive/src/attr.rs @@ -0,0 +1,222 @@ +use std::iter::FromIterator; + +use proc_macro2::TokenStream; +use proc_macro_error::abort; +use proc_macro_error::ResultExt; +use quote::quote; +use quote::ToTokens; +use syn::spanned::Spanned; +use syn::{ + parenthesized, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + Attribute, Expr, Ident, LitStr, Token, +}; + +use crate::utils::Sp; + +#[derive(Clone)] +pub struct ClapAttr { + pub kind: Sp<AttrKind>, + pub name: Ident, + pub magic: Option<MagicAttrName>, + pub value: Option<AttrValue>, +} + +impl ClapAttr { + pub fn parse_all(all_attrs: &[Attribute]) -> Vec<Self> { + all_attrs + .iter() + .filter_map(|attr| { + let kind = if attr.path.is_ident("clap") { + Some(Sp::new(AttrKind::Clap, attr.path.span())) + } else if attr.path.is_ident("structopt") { + Some(Sp::new(AttrKind::StructOpt, attr.path.span())) + } else if attr.path.is_ident("command") { + Some(Sp::new(AttrKind::Command, attr.path.span())) + } else if attr.path.is_ident("group") { + Some(Sp::new(AttrKind::Group, attr.path.span())) + } else if attr.path.is_ident("arg") { + Some(Sp::new(AttrKind::Arg, attr.path.span())) + } else if attr.path.is_ident("value") { + Some(Sp::new(AttrKind::Value, attr.path.span())) + } else { + None + }; + kind.map(|k| (k, attr)) + }) + .flat_map(|(k, attr)| { + attr.parse_args_with(Punctuated::<ClapAttr, Token![,]>::parse_terminated) + .unwrap_or_abort() + .into_iter() + .map(move |mut a| { + a.kind = k; + a + }) + }) + .collect() + } + + pub fn value_or_abort(&self) -> &AttrValue { + self.value + .as_ref() + .unwrap_or_else(|| abort!(self.name, "attribute `{}` requires a value", self.name)) + } + + pub fn lit_str_or_abort(&self) -> &LitStr { + let value = self.value_or_abort(); + match value { + AttrValue::LitStr(tokens) => tokens, + AttrValue::Expr(_) | AttrValue::Call(_) => { + abort!( + self.name, + "attribute `{}` can only accept string literals", + self.name + ) + } + } + } +} + +impl Parse for ClapAttr { + fn parse(input: ParseStream) -> syn::Result<Self> { + let name: Ident = input.parse()?; + let name_str = name.to_string(); + + let magic = match name_str.as_str() { + "rename_all" => Some(MagicAttrName::RenameAll), + "rename_all_env" => Some(MagicAttrName::RenameAllEnv), + "skip" => Some(MagicAttrName::Skip), + "next_display_order" => Some(MagicAttrName::NextDisplayOrder), + "next_help_heading" => Some(MagicAttrName::NextHelpHeading), + "default_value_t" => Some(MagicAttrName::DefaultValueT), + "default_values_t" => Some(MagicAttrName::DefaultValuesT), + "default_value_os_t" => Some(MagicAttrName::DefaultValueOsT), + "default_values_os_t" => Some(MagicAttrName::DefaultValuesOsT), + "long" => Some(MagicAttrName::Long), + "short" => Some(MagicAttrName::Short), + "value_parser" => Some(MagicAttrName::ValueParser), + "action" => Some(MagicAttrName::Action), + "env" => Some(MagicAttrName::Env), + "flatten" => Some(MagicAttrName::Flatten), + "value_enum" => Some(MagicAttrName::ValueEnum), + "from_global" => Some(MagicAttrName::FromGlobal), + "subcommand" => Some(MagicAttrName::Subcommand), + "external_subcommand" => Some(MagicAttrName::ExternalSubcommand), + "verbatim_doc_comment" => Some(MagicAttrName::VerbatimDocComment), + "about" => Some(MagicAttrName::About), + "long_about" => Some(MagicAttrName::LongAbout), + "long_help" => Some(MagicAttrName::LongHelp), + "author" => Some(MagicAttrName::Author), + "version" => Some(MagicAttrName::Version), + _ => None, + }; + + let value = if input.peek(Token![=]) { + // `name = value` attributes. + let assign_token = input.parse::<Token![=]>()?; // skip '=' + if input.peek(LitStr) { + let lit: LitStr = input.parse()?; + Some(AttrValue::LitStr(lit)) + } else { + match input.parse::<Expr>() { + Ok(expr) => Some(AttrValue::Expr(expr)), + + Err(_) => abort! { + assign_token, + "expected `string literal` or `expression` after `=`" + }, + } + } + } else if input.peek(syn::token::Paren) { + // `name(...)` attributes. + let nested; + parenthesized!(nested in input); + + let method_args: Punctuated<_, Token![,]> = nested.parse_terminated(Expr::parse)?; + Some(AttrValue::Call(Vec::from_iter(method_args))) + } else { + None + }; + + Ok(Self { + kind: Sp::new(AttrKind::Clap, name.span()), + name, + magic, + value, + }) + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum MagicAttrName { + Short, + Long, + ValueParser, + Action, + Env, + Flatten, + ValueEnum, + FromGlobal, + Subcommand, + VerbatimDocComment, + ExternalSubcommand, + About, + LongAbout, + LongHelp, + Author, + Version, + RenameAllEnv, + RenameAll, + Skip, + DefaultValueT, + DefaultValuesT, + DefaultValueOsT, + DefaultValuesOsT, + NextDisplayOrder, + NextHelpHeading, +} + +#[derive(Clone)] +#[allow(clippy::large_enum_variant)] +pub enum AttrValue { + LitStr(LitStr), + Expr(Expr), + Call(Vec<Expr>), +} + +impl ToTokens for AttrValue { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::LitStr(t) => t.to_tokens(tokens), + Self::Expr(t) => t.to_tokens(tokens), + Self::Call(t) => { + let t = quote!(#(#t),*); + t.to_tokens(tokens) + } + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum AttrKind { + Clap, + StructOpt, + Command, + Group, + Arg, + Value, +} + +impl AttrKind { + pub fn as_str(&self) -> &'static str { + match self { + Self::Clap => "clap", + Self::StructOpt => "structopt", + Self::Command => "command", + Self::Group => "group", + Self::Arg => "arg", + Self::Value => "value", + } + } +} 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) - } - } - } -} diff --git a/vendor/clap_derive/src/derives/args.rs b/vendor/clap_derive/src/derives/args.rs index 4195a0810..b5d51bfca 100644 --- a/vendor/clap_derive/src/derives/args.rs +++ b/vendor/clap_derive/src/derives/args.rs @@ -12,20 +12,19 @@ // commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the // MIT/Apache 2.0 license. -use crate::{ - attrs::{Attrs, Kind, Name, ParserKind, DEFAULT_CASING, DEFAULT_ENV_CASING}, - dummies, - utils::{inner_type, is_simple_ty, sub_type, Sp, Ty}, -}; - use proc_macro2::{Ident, Span, TokenStream}; use proc_macro_error::{abort, abort_call_site}; use quote::{format_ident, quote, quote_spanned}; +use syn::ext::IdentExt; use syn::{ - punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, Data, DataStruct, - DeriveInput, Field, Fields, Generics, Type, + punctuated::Punctuated, spanned::Spanned, token::Comma, Data, DataStruct, DeriveInput, Field, + Fields, Generics, }; +use crate::dummies; +use crate::item::{Item, Kind, Name}; +use crate::utils::{inner_type, sub_type, Sp, Ty}; + pub fn derive_args(input: &DeriveInput) -> TokenStream { let ident = &input.ident; @@ -35,87 +34,68 @@ pub fn derive_args(input: &DeriveInput) -> TokenStream { Data::Struct(DataStruct { fields: Fields::Named(ref fields), .. - }) => gen_for_struct(ident, &input.generics, &fields.named, &input.attrs), + }) => { + let name = Name::Derived(ident.clone()); + let item = Item::from_args_struct(input, name); + let fields = fields + .named + .iter() + .map(|field| { + let item = Item::from_args_field(field, item.casing(), item.env_casing()); + (field, item) + }) + .collect::<Vec<_>>(); + gen_for_struct(&item, ident, &input.generics, &fields) + } Data::Struct(DataStruct { fields: Fields::Unit, .. - }) => gen_for_struct( - ident, - &input.generics, - &Punctuated::<Field, Comma>::new(), - &input.attrs, - ), + }) => { + let name = Name::Derived(ident.clone()); + let item = Item::from_args_struct(input, name); + let fields = Punctuated::<Field, Comma>::new(); + let fields = fields + .iter() + .map(|field| { + let item = Item::from_args_field(field, item.casing(), item.env_casing()); + (field, item) + }) + .collect::<Vec<_>>(); + gen_for_struct(&item, ident, &input.generics, &fields) + } _ => abort_call_site!("`#[derive(Args)]` only supports non-tuple structs"), } } pub fn gen_for_struct( - struct_name: &Ident, + item: &Item, + item_name: &Ident, generics: &Generics, - fields: &Punctuated<Field, Comma>, - attrs: &[Attribute], + fields: &[(&Field, Item)], ) -> TokenStream { - let from_arg_matches = gen_from_arg_matches_for_struct(struct_name, generics, fields, attrs); - - let attrs = Attrs::from_struct( - Span::call_site(), - attrs, - Name::Derived(struct_name.clone()), - Sp::call_site(DEFAULT_CASING), - Sp::call_site(DEFAULT_ENV_CASING), - ); - let app_var = Ident::new("__clap_app", Span::call_site()); - let augmentation = gen_augment(fields, &app_var, &attrs, false); - let augmentation_update = gen_augment(fields, &app_var, &attrs, true); - - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - - quote! { - #from_arg_matches - - #[allow(dead_code, unreachable_code, unused_variables, unused_braces)] - #[allow( - clippy::style, - clippy::complexity, - clippy::pedantic, - clippy::restriction, - clippy::perf, - clippy::deprecated, - clippy::nursery, - clippy::cargo, - clippy::suspicious_else_formatting, - )] - #[deny(clippy::correctness)] - impl #impl_generics clap::Args for #struct_name #ty_generics #where_clause { - fn augment_args<'b>(#app_var: clap::Command<'b>) -> clap::Command<'b> { - #augmentation - } - fn augment_args_for_update<'b>(#app_var: clap::Command<'b>) -> clap::Command<'b> { - #augmentation_update - } + if !matches!(&*item.kind(), Kind::Command(_)) { + abort! { item.kind().span(), + "`{}` cannot be used with `command`", + item.kind().name(), } } -} -pub fn gen_from_arg_matches_for_struct( - struct_name: &Ident, - generics: &Generics, - fields: &Punctuated<Field, Comma>, - attrs: &[Attribute], -) -> TokenStream { - let attrs = Attrs::from_struct( - Span::call_site(), - attrs, - Name::Derived(struct_name.clone()), - Sp::call_site(DEFAULT_CASING), - Sp::call_site(DEFAULT_ENV_CASING), - ); - - let constructor = gen_constructor(fields, &attrs); - let updater = gen_updater(fields, &attrs, true); + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let constructor = gen_constructor(fields); + let updater = gen_updater(fields, true); let raw_deprecated = raw_deprecated(); - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let app_var = Ident::new("__clap_app", Span::call_site()); + let augmentation = gen_augment(fields, &app_var, item, false); + let augmentation_update = gen_augment(fields, &app_var, item, true); + + let group_id = if item.skip_group() { + quote!(None) + } else { + let group_id = item.ident().unraw().to_string(); + quote!(Some(clap::Id::from(#group_id))) + }; quote! { #[allow(dead_code, unreachable_code, unused_variables, unused_braces)] @@ -131,14 +111,14 @@ pub fn gen_from_arg_matches_for_struct( clippy::suspicious_else_formatting, )] #[deny(clippy::correctness)] - impl #impl_generics clap::FromArgMatches for #struct_name #ty_generics #where_clause { + impl #impl_generics clap::FromArgMatches for #item_name #ty_generics #where_clause { fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> { Self::from_arg_matches_mut(&mut __clap_arg_matches.clone()) } fn from_arg_matches_mut(__clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> { #raw_deprecated - let v = #struct_name #constructor; + let v = #item_name #constructor; ::std::result::Result::Ok(v) } @@ -152,229 +132,149 @@ pub fn gen_from_arg_matches_for_struct( ::std::result::Result::Ok(()) } } + + #[allow(dead_code, unreachable_code, unused_variables, unused_braces)] + #[allow( + clippy::style, + clippy::complexity, + clippy::pedantic, + clippy::restriction, + clippy::perf, + clippy::deprecated, + clippy::nursery, + clippy::cargo, + clippy::suspicious_else_formatting, + )] + #[deny(clippy::correctness)] + impl #impl_generics clap::Args for #item_name #ty_generics #where_clause { + fn group_id() -> Option<clap::Id> { + #group_id + } + fn augment_args<'b>(#app_var: clap::Command) -> clap::Command { + #augmentation + } + fn augment_args_for_update<'b>(#app_var: clap::Command) -> clap::Command { + #augmentation_update + } + } } } /// Generate a block of code to add arguments/subcommands corresponding to /// the `fields` to an cmd. pub fn gen_augment( - fields: &Punctuated<Field, Comma>, + fields: &[(&Field, Item)], app_var: &Ident, - parent_attribute: &Attrs, + parent_item: &Item, override_required: bool, ) -> 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()=> - #[allow(deprecated)] - let #app_var = #app_var.setting( - clap::AppSettings::SubcommandRequiredElseHelp - ); + let mut subcommand_specified = false; + let args = fields.iter().filter_map(|(field, item)| { + let kind = item.kind(); + match &*kind { + Kind::Command(_) + | Kind::Value + | Kind::Skip(_, _) + | Kind::FromGlobal(_) + | Kind::ExternalSubcommand => None, + Kind::Subcommand(ty) => { + if subcommand_specified { + abort!(field.span(), "`#[command(subcommand)]` can only be used once per container"); } - }; + subcommand_specified = true; - let span = field.span(); - let ts = if override_required { - quote! { - let #app_var = <#subcmd_type as clap::Subcommand>::augment_subcommands_for_update( #app_var ); - } - } else{ - quote! { + let subcmd_type = match (**ty, sub_type(&field.ty)) { + (Ty::Option, Some(sub_type)) => sub_type, + _ => &field.ty, + }; + let implicit_methods = if **ty == Ty::Option { + quote!() + } else { + quote_spanned! { kind.span()=> + .subcommand_required(true) + .arg_required_else_help(true) + } + }; + + let override_methods = if override_required { + quote_spanned! { kind.span()=> + .subcommand_required(false) + .arg_required_else_help(false) + } + } else { + quote!() + }; + + Some(quote! { let #app_var = <#subcmd_type as clap::Subcommand>::augment_subcommands( #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 #app_var = #app_var + #implicit_methods + #override_methods; + }) + } + Kind::Flatten(ty) => { + let inner_type = match (**ty, sub_type(&field.ty)) { + (Ty::Option, Some(sub_type)) => sub_type, + _ => &field.ty, + }; - 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(_) - | Kind::FromGlobal(_) - | Kind::ExternalSubcommand => None, - Kind::Flatten => { - let ty = &field.ty; - let old_heading_var = format_ident!("__clap_old_heading"); - let next_help_heading = attrs.next_help_heading(); - let next_display_order = attrs.next_display_order(); + let next_help_heading = item.next_help_heading(); + let next_display_order = item.next_display_order(); if override_required { Some(quote_spanned! { kind.span()=> - let #old_heading_var = #app_var.get_next_help_heading(); - let #app_var = #app_var #next_help_heading #next_display_order; - let #app_var = <#ty as clap::Args>::augment_args_for_update(#app_var); - let #app_var = #app_var.next_help_heading(#old_heading_var); + let #app_var = #app_var + #next_help_heading + #next_display_order; + let #app_var = <#inner_type as clap::Args>::augment_args_for_update(#app_var); }) } else { Some(quote_spanned! { kind.span()=> - let #old_heading_var = #app_var.get_next_help_heading(); - let #app_var = #app_var #next_help_heading #next_display_order; - let #app_var = <#ty as clap::Args>::augment_args(#app_var); - let #app_var = #app_var.next_help_heading(#old_heading_var); + let #app_var = #app_var + #next_help_heading + #next_display_order; + let #app_var = <#inner_type as clap::Args>::augment_args(#app_var); }) } } Kind::Arg(ty) => { - let convert_type = inner_type(&field.ty); - - let parser = attrs.parser(&field.ty); - - let value_parser = attrs.value_parser(&field.ty); - let action = attrs.action(&field.ty); - let func = &parser.func; - - let mut occurrences = false; - let mut flag = false; - let validator = match *parser.kind { - _ if attrs.ignore_parser() || attrs.is_enum() => quote!(), - ParserKind::TryFromStr => quote_spanned! { func.span()=> - .validator(|s| { - #func(s) - .map(|_: #convert_type| ()) - }) - }, - ParserKind::TryFromOsStr => quote_spanned! { func.span()=> - .validator_os(|s| #func(s).map(|_: #convert_type| ())) - }, - ParserKind::FromStr | ParserKind::FromOsStr => quote!(), - ParserKind::FromFlag => { - flag = true; - quote!() - } - ParserKind::FromOccurrences => { - occurrences = true; - quote!() - } - }; - let parse_deprecation = match *parser.kind { - _ if !attrs.explicit_parser() || cfg!(not(feature = "deprecated")) => quote!(), - ParserKind::FromStr => quote_spanned! { func.span()=> - #[deprecated(since = "3.2.0", note = "Replaced with `#[clap(value_parser = ...)]`")] - fn parse_from_str() { - } - parse_from_str(); - }, - ParserKind::TryFromStr => quote_spanned! { func.span()=> - #[deprecated(since = "3.2.0", note = "Replaced with `#[clap(value_parser = ...)]`")] - fn parse_try_from_str() { - } - parse_try_from_str(); - }, - ParserKind::FromOsStr => quote_spanned! { func.span()=> - #[deprecated(since = "3.2.0", note = "Replaced with `#[clap(value_parser)]` for `PathBuf` or `#[clap(value_parser = ...)]` with a custom `TypedValueParser`")] - fn parse_from_os_str() { - } - parse_from_os_str(); - }, - ParserKind::TryFromOsStr => quote_spanned! { func.span()=> - #[deprecated(since = "3.2.0", note = "Replaced with `#[clap(value_parser = ...)]` with a custom `TypedValueParser`")] - fn parse_try_from_os_str() { - } - parse_try_from_os_str(); - }, - ParserKind::FromFlag => quote_spanned! { func.span()=> - #[deprecated(since = "3.2.0", note = "Replaced with `#[clap(action = ArgAction::SetTrue)]`")] - fn parse_from_flag() { - } - parse_from_flag(); - }, - ParserKind::FromOccurrences => quote_spanned! { func.span()=> - #[deprecated(since = "3.2.0", note = "Replaced with `#[clap(action = ArgAction::Count)]` with a field type of `u8`")] - fn parse_from_occurrences() { - } - parse_from_occurrences(); - }, - }; - - let value_name = attrs.value_name(); - let possible_values = if attrs.is_enum() && !attrs.ignore_parser() { - gen_value_enum_possible_values(convert_type) - } else { - quote!() - }; + let value_parser = item.value_parser(&field.ty); + let action = item.action(&field.ty); + let value_name = item.value_name(); let implicit_methods = match **ty { + Ty::Unit => { + // Leaving out `value_parser` as it will always fail + quote_spanned! { ty.span()=> + .value_name(#value_name) + #action + } + } Ty::Option => { quote_spanned! { ty.span()=> - .takes_value(true) .value_name(#value_name) - #possible_values - #validator #value_parser #action } } Ty::OptionOption => quote_spanned! { ty.span()=> - .takes_value(true) .value_name(#value_name) - .min_values(0) - .max_values(1) - .multiple_values(false) - #possible_values - #validator + .num_args(0..=1) #value_parser #action }, Ty::OptionVec => { - if attrs.ignore_parser() { - if attrs.is_positional() { - quote_spanned! { ty.span()=> - .takes_value(true) - .value_name(#value_name) - .multiple_values(true) // action won't be sufficient for getting multiple - #possible_values - #validator - #value_parser - #action - } - } else { - quote_spanned! { ty.span()=> - .takes_value(true) - .value_name(#value_name) - #possible_values - #validator - #value_parser - #action - } + if item.is_positional() { + quote_spanned! { ty.span()=> + .value_name(#value_name) + .num_args(1..) // action won't be sufficient for getting multiple + #value_parser + #action } } else { quote_spanned! { ty.span()=> - .takes_value(true) .value_name(#value_name) - .multiple_occurrences(true) - #possible_values - #validator #value_parser #action } @@ -382,72 +282,63 @@ pub fn gen_augment( } Ty::Vec => { - if attrs.ignore_parser() { - if attrs.is_positional() { - quote_spanned! { ty.span()=> - .takes_value(true) - .value_name(#value_name) - .multiple_values(true) // action won't be sufficient for getting multiple - #possible_values - #validator - #value_parser - #action - } - } else { - quote_spanned! { ty.span()=> - .takes_value(true) - .value_name(#value_name) - #possible_values - #validator - #value_parser - #action - } + if item.is_positional() { + quote_spanned! { ty.span()=> + .value_name(#value_name) + .num_args(1..) // action won't be sufficient for getting multiple + #value_parser + #action } } else { quote_spanned! { ty.span()=> - .takes_value(true) .value_name(#value_name) - .multiple_occurrences(true) - #possible_values - #validator #value_parser #action } } } - Ty::Other if occurrences => quote_spanned! { ty.span()=> - .multiple_occurrences(true) - }, - - Ty::Other if flag => quote_spanned! { ty.span()=> - .takes_value(false) - }, + Ty::VecVec | Ty::OptionVecVec => { + quote_spanned! { ty.span() => + .value_name(#value_name) + #value_parser + #action + } + } Ty::Other => { - let required = attrs.find_default_method().is_none() && !override_required; + let required = item.find_default_method().is_none(); // `ArgAction::takes_values` is assuming `ArgAction::default_value` will be // set though that won't always be true but this should be good enough, // otherwise we'll report an "arg required" error when unwrapping. let action_value = action.args(); quote_spanned! { ty.span()=> - .takes_value(true) .value_name(#value_name) .required(#required && #action_value.takes_values()) - #possible_values - #validator #value_parser #action } } }; - let id = attrs.id(); - let explicit_methods = attrs.field_methods(true); + let id = item.id(); + let explicit_methods = item.field_methods(); + let deprecations = if !override_required { + item.deprecations() + } else { + quote!() + }; + let override_methods = if override_required { + quote_spanned! { kind.span()=> + .required(false) + } + } else { + quote!() + }; Some(quote_spanned! { field.span()=> let #app_var = #app_var.arg({ - #parse_deprecation + #deprecations #[allow(deprecated)] let arg = clap::Arg::new(#id) @@ -455,6 +346,10 @@ pub fn gen_augment( let arg = arg #explicit_methods; + + let arg = arg + #override_methods; + arg }); }) @@ -462,36 +357,80 @@ pub fn gen_augment( } }); - let initial_app_methods = parent_attribute.initial_top_level_methods(); - let final_app_methods = parent_attribute.final_top_level_methods(); + let deprecations = if !override_required { + parent_item.deprecations() + } else { + quote!() + }; + let initial_app_methods = parent_item.initial_top_level_methods(); + let final_app_methods = parent_item.final_top_level_methods(); + let group_app_methods = if parent_item.skip_group() { + quote!() + } else { + let group_id = parent_item.ident().unraw().to_string(); + let literal_group_members = fields + .iter() + .filter_map(|(_field, item)| { + let kind = item.kind(); + if matches!(*kind, Kind::Arg(_)) { + Some(item.id()) + } else { + None + } + }) + .collect::<Vec<_>>(); + let literal_group_members_len = literal_group_members.len(); + let mut literal_group_members = quote! {{ + let members: [clap::Id; #literal_group_members_len] = [#( clap::Id::from(#literal_group_members) ),* ]; + members + }}; + // HACK: Validation isn't ready yet for nested arg groups, so just don't populate the group in + // that situation + let possible_group_members_len = fields + .iter() + .filter(|(_field, item)| { + let kind = item.kind(); + matches!(*kind, Kind::Flatten(_)) + }) + .count(); + if 0 < possible_group_members_len { + literal_group_members = quote! {{ + let members: [clap::Id; 0] = []; + members + }}; + } + + quote!( + .group( + clap::ArgGroup::new(#group_id) + .multiple(true) + .args(#literal_group_members) + ) + ) + }; quote! {{ - let #app_var = #app_var #initial_app_methods; + #deprecations + let #app_var = #app_var + #initial_app_methods + #group_app_methods + ; #( #args )* - #subcmd #app_var #final_app_methods }} } -fn gen_value_enum_possible_values(ty: &Type) -> TokenStream { - quote_spanned! { ty.span()=> - .possible_values(<#ty as clap::ValueEnum>::value_variants().iter().filter_map(clap::ValueEnum::to_possible_value)) - } -} - -pub 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(), - ); +pub fn gen_constructor(fields: &[(&Field, Item)]) -> TokenStream { + let fields = fields.iter().map(|(field, item)| { let field_name = field.ident.as_ref().unwrap(); - let kind = attrs.kind(); + let kind = item.kind(); let arg_matches = format_ident!("__clap_arg_matches"); match &*kind { - Kind::ExternalSubcommand => { + Kind::Command(_) + | Kind::Value + | Kind::ExternalSubcommand => { abort! { kind.span(), - "`external_subcommand` can be used only on enum variants" + "`{}` cannot be used with `arg`", + kind.name(), } } Kind::Subcommand(ty) => { @@ -511,27 +450,76 @@ pub fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Att } } }, - _ => { + Ty::Other => { quote_spanned! { kind.span()=> #field_name: { <#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)? } } }, + Ty::Unit | + Ty::Vec | + Ty::OptionOption | + Ty::OptionVec | + Ty::VecVec | + Ty::OptionVecVec => { + abort!( + ty.span(), + "{} types are not supported for subcommand", + ty.as_str() + ); + } } } - Kind::Flatten => quote_spanned! { kind.span()=> - #field_name: clap::FromArgMatches::from_arg_matches_mut(#arg_matches)? + Kind::Flatten(ty) => { + let inner_type = match (**ty, sub_type(&field.ty)) { + (Ty::Option, Some(sub_type)) => sub_type, + _ => &field.ty, + }; + match **ty { + Ty::Other => { + quote_spanned! { kind.span()=> + #field_name: <#inner_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)? + } + }, + Ty::Option => { + quote_spanned! { kind.span()=> + #field_name: { + let group_id = <#inner_type as clap::Args>::group_id() + .expect("`#[arg(flatten)]`ed field type implements `Args::group_id`"); + if #arg_matches.contains_id(group_id.as_str()) { + Some( + <#inner_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)? + ) + } else { + None + } + } + } + }, + Ty::Unit | + Ty::Vec | + Ty::OptionOption | + Ty::OptionVec | + Ty::VecVec | + Ty::OptionVecVec => { + abort!( + ty.span(), + "{} types are not supported for flatten", + ty.as_str() + ); + } + } }, - Kind::Skip(val) => match val { + 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) | Kind::FromGlobal(ty) => { - gen_parsers(&attrs, ty, field_name, field, None) + gen_parsers(item, ty, field_name, field, None) } } }); @@ -541,19 +529,10 @@ pub fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Att }} } -pub fn gen_updater( - fields: &Punctuated<Field, Comma>, - parent_attribute: &Attrs, - use_self: bool, -) -> TokenStream { - let fields = fields.iter().map(|field| { - let attrs = Attrs::from_field( - field, - parent_attribute.casing(), - parent_attribute.env_casing(), - ); +pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> TokenStream { + let fields = fields.iter().map(|(field, item)| { let field_name = field.ident.as_ref().unwrap(); - let kind = attrs.kind(); + let kind = item.kind(); let access = if use_self { quote! { @@ -566,9 +545,12 @@ pub fn gen_updater( let arg_matches = format_ident!("__clap_arg_matches"); match &*kind { - Kind::ExternalSubcommand => { + Kind::Command(_) + | Kind::Value + | Kind::ExternalSubcommand => { abort! { kind.span(), - "`external_subcommand` can be used only on enum variants" + "`{}` cannot be used with `arg`", + kind.name(), } } Kind::Subcommand(ty) => { @@ -604,15 +586,42 @@ pub fn gen_updater( } } - Kind::Flatten => quote_spanned! { kind.span()=> { - #access - clap::FromArgMatches::update_from_arg_matches_mut(#field_name, #arg_matches)?; + Kind::Flatten(ty) => { + let inner_type = match (**ty, sub_type(&field.ty)) { + (Ty::Option, Some(sub_type)) => sub_type, + _ => &field.ty, + }; + + let updater = quote_spanned! { ty.span()=> + <#inner_type as clap::FromArgMatches>::update_from_arg_matches_mut(#field_name, #arg_matches)?; + }; + + let updater = match **ty { + Ty::Option => quote_spanned! { kind.span()=> + if let Some(#field_name) = #field_name.as_mut() { + #updater + } else { + *#field_name = Some(<#inner_type as clap::FromArgMatches>::from_arg_matches_mut( + #arg_matches + )?); + } + }, + _ => quote_spanned! { kind.span()=> + #updater + }, + }; + + quote_spanned! { kind.span()=> + { + #access + #updater + } } }, - Kind::Skip(_) => quote!(), + Kind::Skip(_, _) => quote!(), - Kind::Arg(ty) | Kind::FromGlobal(ty) => gen_parsers(&attrs, ty, field_name, field, Some(&access)), + Kind::Arg(ty) | Kind::FromGlobal(ty) => gen_parsers(item, ty, field_name, field, Some(&access)), } }); @@ -622,78 +631,18 @@ pub fn gen_updater( } fn gen_parsers( - attrs: &Attrs, + item: &Item, ty: &Sp<Ty>, field_name: &Ident, field: &Field, update: Option<&TokenStream>, ) -> TokenStream { - use self::ParserKind::*; - - let parser = attrs.parser(&field.ty); - let func = &parser.func; - let span = parser.kind.span(); + let span = ty.span(); let convert_type = inner_type(&field.ty); - let id = attrs.id(); - let mut flag = false; - let mut occurrences = false; - let (get_one, get_many, deref, mut parse) = match *parser.kind { - _ if attrs.ignore_parser() => ( - quote_spanned!(span=> remove_one::<#convert_type>), - quote_spanned!(span=> remove_many::<#convert_type>), - quote!(|s| s), - quote_spanned!(func.span()=> |s| ::std::result::Result::Ok::<_, clap::Error>(s)), - ), - FromOccurrences => { - occurrences = true; - ( - quote_spanned!(span=> occurrences_of), - quote!(), - quote!(|s| ::std::ops::Deref::deref(s)), - func.clone(), - ) - } - FromFlag => { - flag = true; - ( - quote!(), - quote!(), - quote!(|s| ::std::ops::Deref::deref(s)), - func.clone(), - ) - } - FromStr => ( - quote_spanned!(span=> get_one::<String>), - quote_spanned!(span=> get_many::<String>), - quote!(|s| ::std::ops::Deref::deref(s)), - quote_spanned!(func.span()=> |s| ::std::result::Result::Ok::<_, clap::Error>(#func(s))), - ), - TryFromStr => ( - quote_spanned!(span=> get_one::<String>), - quote_spanned!(span=> get_many::<String>), - quote!(|s| ::std::ops::Deref::deref(s)), - quote_spanned!(func.span()=> |s| #func(s).map_err(|err| clap::Error::raw(clap::ErrorKind::ValueValidation, format!("Invalid value for {}: {}", #id, err)))), - ), - FromOsStr => ( - quote_spanned!(span=> get_one::<::std::ffi::OsString>), - quote_spanned!(span=> get_many::<::std::ffi::OsString>), - quote!(|s| ::std::ops::Deref::deref(s)), - quote_spanned!(func.span()=> |s| ::std::result::Result::Ok::<_, clap::Error>(#func(s))), - ), - TryFromOsStr => ( - quote_spanned!(span=> get_one::<::std::ffi::OsString>), - quote_spanned!(span=> get_many::<::std::ffi::OsString>), - quote!(|s| ::std::ops::Deref::deref(s)), - quote_spanned!(func.span()=> |s| #func(s).map_err(|err| clap::Error::raw(clap::ErrorKind::ValueValidation, format!("Invalid value for {}: {}", #id, err)))), - ), - }; - if attrs.is_enum() && !attrs.ignore_parser() { - let ci = attrs.ignore_case(); - - parse = quote_spanned! { convert_type.span()=> - |s| <#convert_type as clap::ValueEnum>::from_str(s, #ci).map_err(|err| clap::Error::raw(clap::ErrorKind::ValueValidation, format!("Invalid value for {}: {}", #id, err))) - } - } + let id = item.id(); + let get_one = quote_spanned!(span=> remove_one::<#convert_type>); + let get_many = quote_spanned!(span=> remove_many::<#convert_type>); + let get_occurrences = quote_spanned!(span=> remove_occurrences::<#convert_type>); // Give this identifier the same hygiene // as the `arg_matches` parameter definition. This @@ -701,12 +650,15 @@ fn gen_parsers( let arg_matches = format_ident!("__clap_arg_matches"); let field_value = match **ty { + Ty::Unit => { + quote_spanned! { ty.span()=> + () + } + } + Ty::Option => { quote_spanned! { ty.span()=> #arg_matches.#get_one(#id) - .map(#deref) - .map(#parse) - .transpose()? } } @@ -714,8 +666,6 @@ fn gen_parsers( if #arg_matches.contains_id(#id) { Some( #arg_matches.#get_one(#id) - .map(#deref) - .map(#parse).transpose()? ) } else { None @@ -725,8 +675,7 @@ fn gen_parsers( Ty::OptionVec => quote_spanned! { ty.span()=> if #arg_matches.contains_id(#id) { Some(#arg_matches.#get_many(#id) - .map(|v| v.map(#deref).map::<::std::result::Result<#convert_type, clap::Error>, _>(#parse).collect::<::std::result::Result<Vec<_>, clap::Error>>()) - .transpose()? + .map(|v| v.collect::<Vec<_>>()) .unwrap_or_else(Vec::new)) } else { None @@ -736,36 +685,26 @@ fn gen_parsers( Ty::Vec => { quote_spanned! { ty.span()=> #arg_matches.#get_many(#id) - .map(|v| v.map(#deref).map::<::std::result::Result<#convert_type, clap::Error>, _>(#parse).collect::<::std::result::Result<Vec<_>, clap::Error>>()) - .transpose()? + .map(|v| v.collect::<Vec<_>>()) .unwrap_or_else(Vec::new) } } - Ty::Other if occurrences => quote_spanned! { ty.span()=> - #parse( - #arg_matches.#get_one(#id) - ) + Ty::VecVec => quote_spanned! { ty.span()=> + #arg_matches.#get_occurrences(#id) + .map(|g| g.map(::std::iter::Iterator::collect).collect::<Vec<Vec<_>>>()) + .unwrap_or_else(Vec::new) }, - Ty::Other if flag => { - if update.is_some() && is_simple_ty(&field.ty, "bool") { - quote_spanned! { ty.span()=> - *#field_name || #arg_matches.is_present(#id) - } - } else { - quote_spanned! { ty.span()=> - #parse(#arg_matches.is_present(#id)) - } - } - } + Ty::OptionVecVec => quote_spanned! { ty.span()=> + #arg_matches.#get_occurrences(#id) + .map(|g| g.map(::std::iter::Iterator::collect).collect::<Vec<Vec<_>>>()) + }, Ty::Other => { quote_spanned! { ty.span()=> #arg_matches.#get_one(#id) - .map(#deref) - .ok_or_else(|| clap::Error::raw(clap::ErrorKind::MissingRequiredArgument, format!("The following required argument was not provided: {}", #id))) - .and_then(#parse)? + .ok_or_else(|| clap::Error::raw(clap::error::ErrorKind::MissingRequiredArgument, format!("The following required argument was not provided: {}", #id)))? } } }; diff --git a/vendor/clap_derive/src/derives/into_app.rs b/vendor/clap_derive/src/derives/into_app.rs index 319735753..a45667ab2 100644 --- a/vendor/clap_derive/src/derives/into_app.rs +++ b/vendor/clap_derive/src/derives/into_app.rs @@ -12,36 +12,18 @@ // commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the // MIT/Apache 2.0 license. -use std::env; - use proc_macro2::{Span, TokenStream}; use quote::quote; -use syn::{Attribute, Generics, Ident}; +use syn::{Generics, Ident}; -use crate::{ - attrs::{Attrs, Name, DEFAULT_CASING, DEFAULT_ENV_CASING}, - utils::Sp, -}; +use crate::item::Item; -pub fn gen_for_struct( - struct_name: &Ident, - generics: &Generics, - attrs: &[Attribute], -) -> TokenStream { - let app_name = env::var("CARGO_PKG_NAME").ok().unwrap_or_default(); +pub fn gen_for_struct(item: &Item, item_name: &Ident, generics: &Generics) -> TokenStream { + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - let attrs = Attrs::from_struct( - Span::call_site(), - attrs, - Name::Assigned(quote!(#app_name)), - Sp::call_site(DEFAULT_CASING), - Sp::call_site(DEFAULT_ENV_CASING), - ); - let name = attrs.cased_name(); + let name = item.cased_name(); let app_var = Ident::new("__clap_app", Span::call_site()); - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - let tokens = quote! { #[allow(dead_code, unreachable_code, unused_variables, unused_braces)] #[allow( @@ -56,14 +38,13 @@ pub fn gen_for_struct( clippy::suspicious_else_formatting, )] #[deny(clippy::correctness)] - #[allow(deprecated)] - impl #impl_generics clap::CommandFactory for #struct_name #ty_generics #where_clause { - fn into_app<'b>() -> clap::Command<'b> { + impl #impl_generics clap::CommandFactory for #item_name #ty_generics #where_clause { + fn command<'b>() -> clap::Command { let #app_var = clap::Command::new(#name); <Self as clap::Args>::augment_args(#app_var) } - fn into_app_for_update<'b>() -> clap::Command<'b> { + fn command_for_update<'b>() -> clap::Command { let #app_var = clap::Command::new(#name); <Self as clap::Args>::augment_args_for_update(#app_var) } @@ -73,21 +54,12 @@ pub fn gen_for_struct( tokens } -pub fn gen_for_enum(enum_name: &Ident, generics: &Generics, attrs: &[Attribute]) -> TokenStream { - let app_name = env::var("CARGO_PKG_NAME").ok().unwrap_or_default(); +pub fn gen_for_enum(item: &Item, item_name: &Ident, generics: &Generics) -> TokenStream { + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - let attrs = Attrs::from_struct( - Span::call_site(), - attrs, - Name::Assigned(quote!(#app_name)), - Sp::call_site(DEFAULT_CASING), - Sp::call_site(DEFAULT_ENV_CASING), - ); - let name = attrs.cased_name(); + let name = item.cased_name(); let app_var = Ident::new("__clap_app", Span::call_site()); - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - quote! { #[allow(dead_code, unreachable_code, unused_variables, unused_braces)] #[allow( @@ -102,17 +74,19 @@ pub fn gen_for_enum(enum_name: &Ident, generics: &Generics, attrs: &[Attribute]) clippy::suspicious_else_formatting, )] #[deny(clippy::correctness)] - impl #impl_generics clap::CommandFactory for #enum_name #ty_generics #where_clause { - fn into_app<'b>() -> clap::Command<'b> { - #[allow(deprecated)] + impl #impl_generics clap::CommandFactory for #item_name #ty_generics #where_clause { + fn command<'b>() -> clap::Command { let #app_var = clap::Command::new(#name) - .setting(clap::AppSettings::SubcommandRequiredElseHelp); + .subcommand_required(true) + .arg_required_else_help(true); <Self as clap::Subcommand>::augment_subcommands(#app_var) } - fn into_app_for_update<'b>() -> clap::Command<'b> { + fn command_for_update<'b>() -> clap::Command { let #app_var = clap::Command::new(#name); <Self as clap::Subcommand>::augment_subcommands_for_update(#app_var) + .subcommand_required(false) + .arg_required_else_help(false) } } } diff --git a/vendor/clap_derive/src/derives/parser.rs b/vendor/clap_derive/src/derives/parser.rs index 621430f31..617857cac 100644 --- a/vendor/clap_derive/src/derives/parser.rs +++ b/vendor/clap_derive/src/derives/parser.rs @@ -12,21 +12,24 @@ // commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the // MIT/Apache 2.0 license. -use crate::{ - derives::{args, into_app, subcommand}, - dummies, -}; - use proc_macro2::TokenStream; use proc_macro_error::abort_call_site; use quote::quote; +use syn::Ident; +use syn::Variant; use syn::{ - self, punctuated::Punctuated, token::Comma, Attribute, Data, DataEnum, DataStruct, DeriveInput, - Field, Fields, Generics, Ident, + self, punctuated::Punctuated, token::Comma, Data, DataStruct, DeriveInput, Field, Fields, + Generics, }; +use crate::derives::{args, into_app, subcommand}; +use crate::dummies; +use crate::item::Item; +use crate::item::Name; + pub fn derive_parser(input: &DeriveInput) -> TokenStream { let ident = &input.ident; + let pkg_name = std::env::var("CARGO_PKG_NAME").ok().unwrap_or_default(); match input.data { Data::Struct(DataStruct { @@ -34,41 +37,70 @@ pub fn derive_parser(input: &DeriveInput) -> TokenStream { .. }) => { dummies::parser_struct(ident); - gen_for_struct(ident, &input.generics, &fields.named, &input.attrs) + + let name = Name::Assigned(quote!(#pkg_name)); + let item = Item::from_args_struct(input, name); + let fields = fields + .named + .iter() + .map(|field| { + let item = Item::from_args_field(field, item.casing(), item.env_casing()); + (field, item) + }) + .collect::<Vec<_>>(); + gen_for_struct(&item, ident, &input.generics, &fields) } Data::Struct(DataStruct { fields: Fields::Unit, .. }) => { dummies::parser_struct(ident); - gen_for_struct( - ident, - &input.generics, - &Punctuated::<Field, Comma>::new(), - &input.attrs, - ) + + let name = Name::Assigned(quote!(#pkg_name)); + let item = Item::from_args_struct(input, name); + let fields = Punctuated::<Field, Comma>::new(); + let fields = fields + .iter() + .map(|field| { + let item = Item::from_args_field(field, item.casing(), item.env_casing()); + (field, item) + }) + .collect::<Vec<_>>(); + gen_for_struct(&item, ident, &input.generics, &fields) } Data::Enum(ref e) => { dummies::parser_enum(ident); - gen_for_enum(ident, &input.generics, &input.attrs, e) + + let name = Name::Assigned(quote!(#pkg_name)); + let item = Item::from_subcommand_enum(input, name); + let variants = e + .variants + .iter() + .map(|variant| { + let item = + Item::from_subcommand_variant(variant, item.casing(), item.env_casing()); + (variant, item) + }) + .collect::<Vec<_>>(); + gen_for_enum(&item, ident, &input.generics, &variants) } _ => abort_call_site!("`#[derive(Parser)]` only supports non-tuple structs and enums"), } } fn gen_for_struct( - name: &Ident, + item: &Item, + item_name: &Ident, generics: &Generics, - fields: &Punctuated<Field, Comma>, - attrs: &[Attribute], + fields: &[(&Field, Item)], ) -> TokenStream { - let into_app = into_app::gen_for_struct(name, generics, attrs); - let args = args::gen_for_struct(name, generics, fields, attrs); - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let into_app = into_app::gen_for_struct(item, item_name, generics); + let args = args::gen_for_struct(item, item_name, generics, fields); + quote! { - impl #impl_generics clap::Parser for #name #ty_generics #where_clause {} + impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {} #into_app #args @@ -76,18 +108,18 @@ fn gen_for_struct( } fn gen_for_enum( - name: &Ident, + item: &Item, + item_name: &Ident, generics: &Generics, - attrs: &[Attribute], - e: &DataEnum, + variants: &[(&Variant, Item)], ) -> TokenStream { - let into_app = into_app::gen_for_enum(name, generics, attrs); - let subcommand = subcommand::gen_for_enum(name, generics, attrs, e); - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let into_app = into_app::gen_for_enum(item, item_name, generics); + let subcommand = subcommand::gen_for_enum(item, item_name, generics, variants); + quote! { - impl #impl_generics clap::Parser for #name #ty_generics #where_clause {} + impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {} #into_app #subcommand diff --git a/vendor/clap_derive/src/derives/subcommand.rs b/vendor/clap_derive/src/derives/subcommand.rs index 07bdce5ec..8a44e0c42 100644 --- a/vendor/clap_derive/src/derives/subcommand.rs +++ b/vendor/clap_derive/src/derives/subcommand.rs @@ -11,20 +11,16 @@ // 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::{ - attrs::{Attrs, Kind, Name, DEFAULT_CASING, DEFAULT_ENV_CASING}, - derives::args, - dummies, - utils::{is_simple_ty, subty_if_name, Sp}, -}; use proc_macro2::{Ident, Span, TokenStream}; use proc_macro_error::{abort, abort_call_site}; use quote::{format_ident, quote, quote_spanned}; -use syn::{ - punctuated::Punctuated, spanned::Spanned, Attribute, Data, DataEnum, DeriveInput, - FieldsUnnamed, Generics, Token, Variant, -}; +use syn::{spanned::Spanned, Data, DeriveInput, FieldsUnnamed, Generics, Variant}; + +use crate::derives::args; +use crate::dummies; +use crate::item::{Item, Kind, Name}; +use crate::utils::{is_simple_ty, subty_if_name}; pub fn derive_subcommand(input: &DeriveInput) -> TokenStream { let ident = &input.ident; @@ -32,35 +28,47 @@ pub fn derive_subcommand(input: &DeriveInput) -> TokenStream { dummies::subcommand(ident); match input.data { - Data::Enum(ref e) => gen_for_enum(ident, &input.generics, &input.attrs, e), + Data::Enum(ref e) => { + let name = Name::Derived(ident.clone()); + let item = Item::from_subcommand_enum(input, name); + let variants = e + .variants + .iter() + .map(|variant| { + let item = + Item::from_subcommand_variant(variant, item.casing(), item.env_casing()); + (variant, item) + }) + .collect::<Vec<_>>(); + gen_for_enum(&item, ident, &input.generics, &variants) + } _ => abort_call_site!("`#[derive(Subcommand)]` only supports enums"), } } pub fn gen_for_enum( - enum_name: &Ident, + item: &Item, + item_name: &Ident, generics: &Generics, - attrs: &[Attribute], - e: &DataEnum, + variants: &[(&Variant, Item)], ) -> TokenStream { - let from_arg_matches = gen_from_arg_matches_for_enum(enum_name, generics, attrs, e); - - let attrs = Attrs::from_struct( - Span::call_site(), - attrs, - Name::Derived(enum_name.clone()), - Sp::call_site(DEFAULT_CASING), - Sp::call_site(DEFAULT_ENV_CASING), - ); - let augmentation = gen_augment(&e.variants, &attrs, false); - let augmentation_update = gen_augment(&e.variants, &attrs, true); - let has_subcommand = gen_has_subcommand(&e.variants, &attrs); + if !matches!(&*item.kind(), Kind::Command(_)) { + abort! { item.kind().span(), + "`{}` cannot be used with `command`", + item.kind().name(), + } + } let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - quote! { - #from_arg_matches + let from_arg_matches = gen_from_arg_matches(variants); + let update_from_arg_matches = gen_update_from_arg_matches(variants); + + let augmentation = gen_augment(variants, item, false); + let augmentation_update = gen_augment(variants, item, true); + let has_subcommand = gen_has_subcommand(variants); + quote! { #[allow(dead_code, unreachable_code, unused_variables, unused_braces)] #[allow( clippy::style, @@ -74,40 +82,19 @@ pub fn gen_for_enum( clippy::suspicious_else_formatting, )] #[deny(clippy::correctness)] - impl #impl_generics clap::Subcommand for #enum_name #ty_generics #where_clause { - fn augment_subcommands <'b>(__clap_app: clap::Command<'b>) -> clap::Command<'b> { - #augmentation - } - fn augment_subcommands_for_update <'b>(__clap_app: clap::Command<'b>) -> clap::Command<'b> { - #augmentation_update - } - fn has_subcommand(__clap_name: &str) -> bool { - #has_subcommand + impl #impl_generics clap::FromArgMatches for #item_name #ty_generics #where_clause { + fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> { + Self::from_arg_matches_mut(&mut __clap_arg_matches.clone()) } - } - } -} -fn gen_from_arg_matches_for_enum( - name: &Ident, - generics: &Generics, - attrs: &[Attribute], - e: &DataEnum, -) -> TokenStream { - let attrs = Attrs::from_struct( - Span::call_site(), - attrs, - Name::Derived(name.clone()), - Sp::call_site(DEFAULT_CASING), - Sp::call_site(DEFAULT_ENV_CASING), - ); - - let from_arg_matches = gen_from_arg_matches(name, &e.variants, &attrs); - let update_from_arg_matches = gen_update_from_arg_matches(name, &e.variants, &attrs); + #from_arg_matches - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + fn update_from_arg_matches(&mut self, __clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<(), clap::Error> { + self.update_from_arg_matches_mut(&mut __clap_arg_matches.clone()) + } + #update_from_arg_matches + } - quote! { #[allow(dead_code, unreachable_code, unused_variables, unused_braces)] #[allow( clippy::style, @@ -121,24 +108,23 @@ fn gen_from_arg_matches_for_enum( clippy::suspicious_else_formatting, )] #[deny(clippy::correctness)] - impl #impl_generics clap::FromArgMatches for #name #ty_generics #where_clause { - fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> { - Self::from_arg_matches_mut(&mut __clap_arg_matches.clone()) + impl #impl_generics clap::Subcommand for #item_name #ty_generics #where_clause { + fn augment_subcommands <'b>(__clap_app: clap::Command) -> clap::Command { + #augmentation } - - #from_arg_matches - - fn update_from_arg_matches(&mut self, __clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<(), clap::Error> { - self.update_from_arg_matches_mut(&mut __clap_arg_matches.clone()) + fn augment_subcommands_for_update <'b>(__clap_app: clap::Command) -> clap::Command { + #augmentation_update + } + fn has_subcommand(__clap_name: &str) -> bool { + #has_subcommand } - #update_from_arg_matches } } } fn gen_augment( - variants: &Punctuated<Variant, Token![,]>, - parent_attribute: &Attrs, + variants: &[(&Variant, Item)], + parent_item: &Item, override_required: bool, ) -> TokenStream { use syn::Fields::*; @@ -147,16 +133,14 @@ fn gen_augment( let subcommands: Vec<_> = variants .iter() - .filter_map(|variant| { - let attrs = Attrs::from_variant( - variant, - parent_attribute.casing(), - parent_attribute.env_casing(), - ); - let kind = attrs.kind(); + .filter_map(|(variant, item)| { + let kind = item.kind(); match &*kind { - Kind::Skip(_) => None, + Kind::Skip(_, _) | + Kind::Arg(_) | + Kind::FromGlobal(_) | + Kind::Value => None, Kind::ExternalSubcommand => { let ty = match variant.fields { @@ -169,47 +153,51 @@ fn gen_augment( or `Vec<OsString>`." ), }; - let subcommand = match subty_if_name(ty, "Vec") { - Some(subty) => { - if is_simple_ty(subty, "OsString") { - quote_spanned! { kind.span()=> - let #app_var = #app_var.allow_external_subcommands(true).allow_invalid_utf8_for_external_subcommands(true); - } - } else { - quote_spanned! { kind.span()=> - let #app_var = #app_var.allow_external_subcommands(true); - } - } - } - - None => abort!( + let deprecations = if !override_required { + item.deprecations() + } else { + quote!() + }; + let subty = subty_if_name(ty, "Vec").unwrap_or_else(|| { + abort!( ty.span(), "The type must be `Vec<_>` \ to be used with `external_subcommand`." - ), + ) + }); + let subcommand = quote_spanned! { kind.span()=> + #deprecations + let #app_var = #app_var + .external_subcommand_value_parser(clap::value_parser!(#subty)); }; Some(subcommand) } - Kind::Flatten => match variant.fields { + Kind::Flatten(_) => match variant.fields { Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { let ty = &unnamed[0]; - let old_heading_var = format_ident!("__clap_old_heading"); - let next_help_heading = attrs.next_help_heading(); - let next_display_order = attrs.next_display_order(); + let deprecations = if !override_required { + item.deprecations() + } else { + quote!() + }; + let next_help_heading = item.next_help_heading(); + let next_display_order = item.next_display_order(); let subcommand = if override_required { quote! { - let #old_heading_var = #app_var.get_next_help_heading(); - let #app_var = #app_var #next_help_heading #next_display_order; + #deprecations + let #app_var = #app_var + #next_help_heading + #next_display_order; let #app_var = <#ty as clap::Subcommand>::augment_subcommands_for_update(#app_var); - let #app_var = #app_var.next_help_heading(#old_heading_var); } } else { quote! { - let #old_heading_var = #app_var.get_next_help_heading(); - let #app_var = #app_var #next_help_heading #next_display_order; + #deprecations + let #app_var = #app_var + #next_help_heading + #next_display_order; let #app_var = <#ty as clap::Subcommand>::augment_subcommands(#app_var); - let #app_var = #app_var.next_help_heading(#old_heading_var); } }; Some(subcommand) @@ -248,33 +236,56 @@ fn gen_augment( } }; - let name = attrs.cased_name(); - let initial_app_methods = attrs.initial_top_level_methods(); - let final_from_attrs = attrs.final_top_level_methods(); + let name = item.cased_name(); + let deprecations = if !override_required { + item.deprecations() + } else { + quote!() + }; + let initial_app_methods = item.initial_top_level_methods(); + let final_from_attrs = item.final_top_level_methods(); + let override_methods = if override_required { + quote_spanned! { kind.span()=> + .subcommand_required(false) + .arg_required_else_help(false) + } + } else { + quote!() + }; let subcommand = quote! { let #app_var = #app_var.subcommand({ + #deprecations; let #subcommand_var = clap::Command::new(#name); + let #subcommand_var = #subcommand_var + .subcommand_required(true) + .arg_required_else_help(true); let #subcommand_var = #subcommand_var #initial_app_methods; let #subcommand_var = #arg_block; - #[allow(deprecated)] - let #subcommand_var = #subcommand_var.setting(clap::AppSettings::SubcommandRequiredElseHelp); - #subcommand_var #final_from_attrs + #subcommand_var #final_from_attrs #override_methods }); }; Some(subcommand) } - _ => { + Kind::Command(_) => { let subcommand_var = Ident::new("__clap_subcommand", Span::call_site()); let sub_augment = match variant.fields { Named(ref fields) => { // Defer to `gen_augment` for adding cmd methods - args::gen_augment(&fields.named, &subcommand_var, &attrs, override_required) + let fields = fields + .named + .iter() + .map(|field| { + let item = Item::from_args_field(field, item.casing(), item.env_casing()); + (field, item) + }) + .collect::<Vec<_>>(); + args::gen_augment(&fields, &subcommand_var, item, override_required) } Unit => { let arg_block = quote!( #subcommand_var ); - let initial_app_methods = attrs.initial_top_level_methods(); - let final_from_attrs = attrs.final_top_level_methods(); + let initial_app_methods = item.initial_top_level_methods(); + let final_from_attrs = item.final_top_level_methods(); quote! { let #subcommand_var = #subcommand_var #initial_app_methods; let #subcommand_var = #arg_block; @@ -296,8 +307,8 @@ fn gen_augment( } } }; - let initial_app_methods = attrs.initial_top_level_methods(); - let final_from_attrs = attrs.final_top_level_methods(); + let initial_app_methods = item.initial_top_level_methods(); + let final_from_attrs = item.final_top_level_methods(); quote! { let #subcommand_var = #subcommand_var #initial_app_methods; let #subcommand_var = #arg_block; @@ -309,9 +320,15 @@ fn gen_augment( } }; - let name = attrs.cased_name(); + let deprecations = if !override_required { + item.deprecations() + } else { + quote!() + }; + let name = item.cased_name(); let subcommand = quote! { let #app_var = #app_var.subcommand({ + #deprecations let #subcommand_var = clap::Command::new(#name); #sub_augment }); @@ -322,46 +339,47 @@ fn gen_augment( }) .collect(); - let initial_app_methods = parent_attribute.initial_top_level_methods(); - let final_app_methods = parent_attribute.final_top_level_methods(); + let deprecations = if !override_required { + parent_item.deprecations() + } else { + quote!() + }; + let initial_app_methods = parent_item.initial_top_level_methods(); + let final_app_methods = parent_item.final_top_level_methods(); quote! { - let #app_var = #app_var #initial_app_methods; - #( #subcommands )*; - #app_var #final_app_methods + #deprecations; + let #app_var = #app_var #initial_app_methods; + #( #subcommands )*; + #app_var #final_app_methods } } -fn gen_has_subcommand( - variants: &Punctuated<Variant, Token![,]>, - parent_attribute: &Attrs, -) -> TokenStream { +fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> TokenStream { use syn::Fields::*; let mut ext_subcmd = false; let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants .iter() - .filter_map(|variant| { - let attrs = Attrs::from_variant( - variant, - parent_attribute.casing(), - parent_attribute.env_casing(), - ); + .filter_map(|(variant, item)| { + let kind = item.kind(); + match &*kind { + Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => None, - if let Kind::ExternalSubcommand = &*attrs.kind() { - ext_subcmd = true; - None - } else { - Some((variant, attrs)) + Kind::ExternalSubcommand => { + ext_subcmd = true; + None + } + Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => Some((variant, item)), } }) - .partition(|(_, attrs)| { - let kind = attrs.kind(); - matches!(&*kind, Kind::Flatten) + .partition(|(_, item)| { + let kind = item.kind(); + matches!(&*kind, Kind::Flatten(_)) }); - let subcommands = variants.iter().map(|(_variant, attrs)| { - let sub_name = attrs.cased_name(); + let subcommands = variants.iter().map(|(_variant, item)| { + let sub_name = item.cased_name(); quote! { if #sub_name == __clap_name { return true @@ -398,11 +416,7 @@ fn gen_has_subcommand( } } -fn gen_from_arg_matches( - name: &Ident, - variants: &Punctuated<Variant, Token![,]>, - parent_attribute: &Attrs, -) -> TokenStream { +fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream { use syn::Fields::*; let mut ext_subcmd = None; @@ -411,71 +425,79 @@ fn gen_from_arg_matches( let sub_arg_matches_var = format_ident!("__clap_arg_matches"); let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants .iter() - .filter_map(|variant| { - let attrs = Attrs::from_variant( - variant, - parent_attribute.casing(), - parent_attribute.env_casing(), - ); - - if let Kind::ExternalSubcommand = &*attrs.kind() { - if ext_subcmd.is_some() { - abort!( - attrs.kind().span(), - "Only one variant can be marked with `external_subcommand`, \ + .filter_map(|(variant, item)| { + let kind = item.kind(); + match &*kind { + Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => None, + + Kind::ExternalSubcommand => { + if ext_subcmd.is_some() { + abort!( + item.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, + 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_subcommand` must be \ + _ => abort!( + variant, + "The enum variant marked with `external_subcommand` must be \ a single-typed tuple, and the type must be either `Vec<String>` \ or `Vec<OsString>`." - ), - }; - - let (span, str_ty) = match subty_if_name(ty, "Vec") { - Some(subty) => { - if is_simple_ty(subty, "String") { - (subty.span(), quote!(::std::string::String)) - } else if is_simple_ty(subty, "OsString") { - (subty.span(), quote!(::std::ffi::OsString)) - } else { - abort!( - ty.span(), - "The type must be either `Vec<String>` or `Vec<OsString>` \ + ), + }; + + let (span, str_ty) = match subty_if_name(ty, "Vec") { + Some(subty) => { + if is_simple_ty(subty, "String") { + (subty.span(), quote!(::std::string::String)) + } else if is_simple_ty(subty, "OsString") { + (subty.span(), quote!(::std::ffi::OsString)) + } else { + abort!( + ty.span(), + "The type must be either `Vec<String>` or `Vec<OsString>` \ to be used with `external_subcommand`." - ); + ); + } } - } - None => abort!( - ty.span(), - "The type must be either `Vec<String>` or `Vec<OsString>` \ + None => abort!( + ty.span(), + "The type must be either `Vec<String>` or `Vec<OsString>` \ to be used with `external_subcommand`." - ), - }; + ), + }; - ext_subcmd = Some((span, &variant.ident, str_ty)); - None - } else { - Some((variant, attrs)) + ext_subcmd = Some((span, &variant.ident, str_ty)); + None + } + Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => Some((variant, item)), } }) - .partition(|(_, attrs)| { - let kind = attrs.kind(); - matches!(&*kind, Kind::Flatten) + .partition(|(_, item)| { + let kind = item.kind(); + matches!(&*kind, Kind::Flatten(_)) }); - let subcommands = variants.iter().map(|(variant, attrs)| { - let sub_name = attrs.cased_name(); + let subcommands = variants.iter().map(|(variant, item)| { + let sub_name = item.cased_name(); let variant_name = &variant.ident; let constructor_block = match variant.fields { - Named(ref fields) => args::gen_constructor(&fields.named, attrs), + Named(ref fields) => { + let fields = fields + .named + .iter() + .map(|field| { + let item = Item::from_args_field(field, item.casing(), item.env_casing()); + (field, item) + }) + .collect::<Vec<_>>(); + args::gen_constructor(&fields) + }, Unit => quote!(), Unnamed(ref fields) if fields.unnamed.len() == 1 => { let ty = &fields.unnamed[0]; @@ -484,17 +506,9 @@ fn gen_from_arg_matches( Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident), }; - if cfg!(feature = "unstable-v4") { - quote! { - if #sub_name == #subcommand_name_var && !#sub_arg_matches_var.contains_id("") { - return ::std::result::Result::Ok(#name :: #variant_name #constructor_block) - } - } - } else { - quote! { - if #sub_name == #subcommand_name_var { - return ::std::result::Result::Ok(#name :: #variant_name #constructor_block) - } + quote! { + if #subcommand_name_var == #sub_name && !#sub_arg_matches_var.contains_id("") { + return ::std::result::Result::Ok(Self :: #variant_name #constructor_block) } } }); @@ -510,7 +524,7 @@ fn gen_from_arg_matches( .unwrap_or_default() { let __clap_res = <#ty as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)?; - return ::std::result::Result::Ok(#name :: #variant_name (__clap_res)); + return ::std::result::Result::Ok(Self :: #variant_name (__clap_res)); } } } @@ -523,12 +537,12 @@ fn gen_from_arg_matches( let wildcard = match ext_subcmd { Some((span, var_name, str_ty)) => quote_spanned! { span=> - ::std::result::Result::Ok(#name::#var_name( + ::std::result::Result::Ok(Self::#var_name( ::std::iter::once(#str_ty::from(#subcommand_name_var)) .chain( #sub_arg_matches_var .remove_many::<#str_ty>("") - .into_iter().flatten() // `""` isn't present, bug in `unstable-v4` + .unwrap() .map(#str_ty::from) ) .collect::<::std::vec::Vec<_>>() @@ -536,7 +550,7 @@ fn gen_from_arg_matches( }, None => quote! { - ::std::result::Result::Err(clap::Error::raw(clap::ErrorKind::UnrecognizedSubcommand, format!("The subcommand '{}' wasn't recognized", #subcommand_name_var))) + ::std::result::Result::Err(clap::Error::raw(clap::error::ErrorKind::InvalidSubcommand, format!("The subcommand '{}' wasn't recognized", #subcommand_name_var))) }, }; @@ -553,61 +567,52 @@ fn gen_from_arg_matches( #wildcard } else { - ::std::result::Result::Err(clap::Error::raw(clap::ErrorKind::MissingSubcommand, "A subcommand is required but one was not provided.")) + ::std::result::Result::Err(clap::Error::raw(clap::error::ErrorKind::MissingSubcommand, "A subcommand is required but one was not provided.")) } } } } -fn gen_update_from_arg_matches( - name: &Ident, - variants: &Punctuated<Variant, Token![,]>, - parent_attribute: &Attrs, -) -> TokenStream { +fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream { use syn::Fields::*; let (flatten, variants): (Vec<_>, Vec<_>) = variants .iter() - .filter_map(|variant| { - let attrs = Attrs::from_variant( - variant, - parent_attribute.casing(), - parent_attribute.env_casing(), - ); - - match &*attrs.kind() { + .filter_map(|(variant, item)| { + let kind = item.kind(); + match &*kind { // Fallback to `from_arg_matches_mut` - Kind::ExternalSubcommand => None, - _ => Some((variant, attrs)), + Kind::Skip(_, _) + | Kind::Arg(_) + | Kind::FromGlobal(_) + | Kind::Value + | Kind::ExternalSubcommand => None, + Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => Some((variant, item)), } }) - .partition(|(_, attrs)| { - let kind = attrs.kind(); - matches!(&*kind, Kind::Flatten) + .partition(|(_, item)| { + let kind = item.kind(); + matches!(&*kind, Kind::Flatten(_)) }); - let subcommands = variants.iter().map(|(variant, attrs)| { - let sub_name = attrs.cased_name(); + let subcommands = variants.iter().map(|(variant, item)| { + let sub_name = item.cased_name(); let variant_name = &variant.ident; let (pattern, updater) = match variant.fields { Named(ref fields) => { - let (fields, update): (Vec<_>, Vec<_>) = fields + let field_names = fields.named.iter().map(|field| { + field.ident.as_ref().unwrap() + }).collect::<Vec<_>>(); + let fields = fields .named .iter() .map(|field| { - let attrs = Attrs::from_field( - field, - parent_attribute.casing(), - parent_attribute.env_casing(), - ); - let field_name = field.ident.as_ref().unwrap(); - ( - quote!( ref mut #field_name ), - args::gen_updater(&fields.named, &attrs, false), - ) + let item = Item::from_args_field(field, item.casing(), item.env_casing()); + (field, item) }) - .unzip(); - (quote!( { #( #fields, )* }), quote!( { #( #update )* } )) + .collect::<Vec<_>>(); + let update = args::gen_updater(&fields, false); + (quote!( { #( #field_names, )* }), quote!( { #update } )) } Unit => (quote!(), quote!({})), Unnamed(ref fields) => { @@ -626,7 +631,7 @@ fn gen_update_from_arg_matches( }; quote! { - #name :: #variant_name #pattern if #sub_name == __clap_name => { + Self :: #variant_name #pattern if #sub_name == __clap_name => { let (_, mut __clap_arg_sub_matches) = __clap_arg_matches.remove_subcommand().unwrap(); let __clap_arg_matches = &mut __clap_arg_sub_matches; #updater @@ -641,7 +646,7 @@ fn gen_update_from_arg_matches( let ty = &fields.unnamed[0]; quote! { if <#ty as clap::Subcommand>::has_subcommand(__clap_name) { - if let #name :: #variant_name (child) = s { + if let Self :: #variant_name (child) = s { <#ty as clap::FromArgMatches>::update_from_arg_matches_mut(child, __clap_arg_matches)?; return ::std::result::Result::Ok(()); } diff --git a/vendor/clap_derive/src/derives/value_enum.rs b/vendor/clap_derive/src/derives/value_enum.rs index 06d514f0e..a1411d02a 100644 --- a/vendor/clap_derive/src/derives/value_enum.rs +++ b/vendor/clap_derive/src/derives/value_enum.rs @@ -8,20 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::{ - attrs::{Attrs, Kind, Name, DEFAULT_CASING, DEFAULT_ENV_CASING}, - dummies, - utils::Sp, -}; - -use proc_macro2::{Span, TokenStream}; +use proc_macro2::TokenStream; use proc_macro_error::{abort, abort_call_site}; use quote::quote; use quote::quote_spanned; -use syn::{ - punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, Data, DataEnum, DeriveInput, - Fields, Ident, Variant, -}; +use syn::{spanned::Spanned, Data, DeriveInput, Fields, Ident, Variant}; + +use crate::dummies; +use crate::item::{Item, Kind, Name}; pub fn derive_value_enum(input: &DeriveInput) -> TokenStream { let ident = &input.ident; @@ -29,23 +23,35 @@ pub fn derive_value_enum(input: &DeriveInput) -> TokenStream { dummies::value_enum(ident); match input.data { - Data::Enum(ref e) => gen_for_enum(ident, &input.attrs, e), + Data::Enum(ref e) => { + let name = Name::Derived(ident.clone()); + let item = Item::from_value_enum(input, name); + let variants = e + .variants + .iter() + .map(|variant| { + let item = + Item::from_value_enum_variant(variant, item.casing(), item.env_casing()); + (variant, item) + }) + .collect::<Vec<_>>(); + gen_for_enum(&item, ident, &variants) + } _ => abort_call_site!("`#[derive(ValueEnum)]` only supports enums"), } } -pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStream { - let attrs = Attrs::from_struct( - Span::call_site(), - attrs, - Name::Derived(name.clone()), - Sp::call_site(DEFAULT_CASING), - Sp::call_site(DEFAULT_ENV_CASING), - ); +pub fn gen_for_enum(item: &Item, item_name: &Ident, variants: &[(&Variant, Item)]) -> TokenStream { + if !matches!(&*item.kind(), Kind::Value) { + abort! { item.kind().span(), + "`{}` cannot be used with `value`", + item.kind().name(), + } + } - let lits = lits(&e.variants, &attrs); + let lits = lits(variants); let value_variants = gen_value_variants(&lits); - let to_possible_value = gen_to_possible_value(&lits); + let to_possible_value = gen_to_possible_value(item, &lits); quote! { #[allow(dead_code, unreachable_code, unused_variables, unused_braces)] @@ -61,38 +67,32 @@ pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStr clippy::suspicious_else_formatting, )] #[deny(clippy::correctness)] - impl clap::ValueEnum for #name { + impl clap::ValueEnum for #item_name { #value_variants #to_possible_value } } } -fn lits( - variants: &Punctuated<Variant, Comma>, - parent_attribute: &Attrs, -) -> Vec<(TokenStream, Ident)> { +fn lits(variants: &[(&Variant, Item)]) -> Vec<(TokenStream, Ident)> { variants .iter() - .filter_map(|variant| { - let attrs = Attrs::from_value_enum_variant( - variant, - parent_attribute.casing(), - parent_attribute.env_casing(), - ); - if let Kind::Skip(_) = &*attrs.kind() { + .filter_map(|(variant, item)| { + if let Kind::Skip(_, _) = &*item.kind() { None } else { if !matches!(variant.fields, Fields::Unit) { abort!(variant.span(), "`#[derive(ValueEnum)]` only supports unit variants. Non-unit variants must be skipped"); } - let fields = attrs.field_methods(false); - let name = attrs.cased_name(); + let fields = item.field_methods(); + let deprecations = item.deprecations(); + let name = item.cased_name(); Some(( - quote_spanned! { variant.span()=> - clap::PossibleValue::new(#name) + quote_spanned! { variant.span()=> { + #deprecations + clap::builder::PossibleValue::new(#name) #fields - }, + }}, variant.ident.clone(), )) } @@ -110,11 +110,14 @@ fn gen_value_variants(lits: &[(TokenStream, Ident)]) -> TokenStream { } } -fn gen_to_possible_value(lits: &[(TokenStream, Ident)]) -> TokenStream { +fn gen_to_possible_value(item: &Item, lits: &[(TokenStream, Ident)]) -> TokenStream { let (lit, variant): (Vec<TokenStream>, Vec<Ident>) = lits.iter().cloned().unzip(); + let deprecations = item.deprecations(); + quote! { - fn to_possible_value<'a>(&self) -> ::std::option::Option<clap::PossibleValue<'a>> { + fn to_possible_value<'a>(&self) -> ::std::option::Option<clap::builder::PossibleValue> { + #deprecations match self { #(Self::#variant => Some(#lit),)* _ => None diff --git a/vendor/clap_derive/src/dummies.rs b/vendor/clap_derive/src/dummies.rs index 74b7def1c..1f54e1dbc 100644 --- a/vendor/clap_derive/src/dummies.rs +++ b/vendor/clap_derive/src/dummies.rs @@ -18,12 +18,11 @@ pub fn parser_enum(name: &Ident) { pub fn into_app(name: &Ident) { append_dummy(quote! { - #[allow(deprecated)] impl clap::CommandFactory for #name { - fn into_app<'b>() -> clap::Command<'b> { + fn command<'b>() -> clap::Command { unimplemented!() } - fn into_app_for_update<'b>() -> clap::Command<'b> { + fn command_for_update<'b>() -> clap::Command { unimplemented!() } } @@ -47,10 +46,10 @@ pub fn subcommand(name: &Ident) { from_arg_matches(name); append_dummy(quote! { impl clap::Subcommand for #name { - fn augment_subcommands(_cmd: clap::Command<'_>) -> clap::Command<'_> { + fn augment_subcommands(_cmd: clap::Command) -> clap::Command { unimplemented!() } - fn augment_subcommands_for_update(_cmd: clap::Command<'_>) -> clap::Command<'_> { + fn augment_subcommands_for_update(_cmd: clap::Command) -> clap::Command { unimplemented!() } fn has_subcommand(name: &str) -> bool { @@ -64,10 +63,10 @@ pub fn args(name: &Ident) { from_arg_matches(name); append_dummy(quote! { impl clap::Args for #name { - fn augment_args(_cmd: clap::Command<'_>) -> clap::Command<'_> { + fn augment_args(_cmd: clap::Command) -> clap::Command { unimplemented!() } - fn augment_args_for_update(_cmd: clap::Command<'_>) -> clap::Command<'_> { + fn augment_args_for_update(_cmd: clap::Command) -> clap::Command { unimplemented!() } } @@ -83,7 +82,7 @@ pub fn value_enum(name: &Ident) { fn from_str(_input: &str, _ignore_case: bool) -> ::std::result::Result<Self, String> { unimplemented!() } - fn to_possible_value<'a>(&self) -> ::std::option::Option<clap::PossibleValue<'a>>{ + fn to_possible_value<'a>(&self) -> ::std::option::Option<clap::builder::PossibleValue>{ unimplemented!() } } diff --git a/vendor/clap_derive/src/item.rs b/vendor/clap_derive/src/item.rs new file mode 100644 index 000000000..5e8272ac0 --- /dev/null +++ b/vendor/clap_derive/src/item.rs @@ -0,0 +1,1449 @@ +// 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 std::env; + +use heck::{ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase}; +use proc_macro2::{self, Span, TokenStream}; +use proc_macro_error::abort; +use quote::{format_ident, quote, quote_spanned, ToTokens}; +use syn::DeriveInput; +use syn::{self, ext::IdentExt, spanned::Spanned, Attribute, Field, Ident, LitStr, Type, Variant}; + +use crate::attr::*; +use crate::utils::{extract_doc_comment, format_doc_comment, inner_type, is_simple_ty, Sp, Ty}; + +/// 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 Item { + name: Name, + ident: Ident, + casing: Sp<CasingStyle>, + env_casing: Sp<CasingStyle>, + ty: Option<Type>, + doc_comment: Vec<Method>, + methods: Vec<Method>, + deprecations: Vec<Deprecation>, + value_parser: Option<ValueParser>, + action: Option<Action>, + verbatim_doc_comment: bool, + force_long_help: bool, + next_display_order: Option<Method>, + next_help_heading: Option<Method>, + is_enum: bool, + is_positional: bool, + skip_group: bool, + kind: Sp<Kind>, +} + +impl Item { + pub fn from_args_struct(input: &DeriveInput, name: Name) -> Self { + let ident = input.ident.clone(); + let span = input.ident.span(); + let attrs = &input.attrs; + let argument_casing = Sp::new(DEFAULT_CASING, span); + let env_casing = Sp::new(DEFAULT_ENV_CASING, span); + let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span); + + let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind); + let parsed_attrs = ClapAttr::parse_all(attrs); + res.infer_kind(&parsed_attrs); + res.push_attrs(&parsed_attrs); + res.push_doc_comment(attrs, "about", Some("long_about")); + + res + } + + pub fn from_subcommand_enum(input: &DeriveInput, name: Name) -> Self { + let ident = input.ident.clone(); + let span = input.ident.span(); + let attrs = &input.attrs; + let argument_casing = Sp::new(DEFAULT_CASING, span); + let env_casing = Sp::new(DEFAULT_ENV_CASING, span); + let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span); + + let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind); + let parsed_attrs = ClapAttr::parse_all(attrs); + res.infer_kind(&parsed_attrs); + res.push_attrs(&parsed_attrs); + res.push_doc_comment(attrs, "about", Some("long_about")); + + res + } + + pub fn from_value_enum(input: &DeriveInput, name: Name) -> Self { + let ident = input.ident.clone(); + let span = input.ident.span(); + let attrs = &input.attrs; + let argument_casing = Sp::new(DEFAULT_CASING, span); + let env_casing = Sp::new(DEFAULT_ENV_CASING, span); + let kind = Sp::new(Kind::Value, span); + + let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind); + let parsed_attrs = ClapAttr::parse_all(attrs); + res.infer_kind(&parsed_attrs); + res.push_attrs(&parsed_attrs); + // Ignoring `push_doc_comment` as there is no top-level clap builder to add documentation + // to + + if res.has_explicit_methods() { + abort!( + res.methods[0].name.span(), + "{} doesn't exist for `ValueEnum` enums", + res.methods[0].name + ); + } + + res + } + + pub fn from_subcommand_variant( + variant: &Variant, + struct_casing: Sp<CasingStyle>, + env_casing: Sp<CasingStyle>, + ) -> Self { + let name = variant.ident.clone(); + let ident = variant.ident.clone(); + let span = variant.span(); + let ty = match variant.fields { + syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { + Ty::from_syn_ty(&unnamed[0].ty) + } + syn::Fields::Named(_) | syn::Fields::Unnamed(..) | syn::Fields::Unit => { + Sp::new(Ty::Other, span) + } + }; + let kind = Sp::new(Kind::Command(ty), span); + let mut res = Self::new( + Name::Derived(name), + ident, + None, + struct_casing, + env_casing, + kind, + ); + let parsed_attrs = ClapAttr::parse_all(&variant.attrs); + res.infer_kind(&parsed_attrs); + res.push_attrs(&parsed_attrs); + if matches!(&*res.kind, Kind::Command(_) | Kind::Subcommand(_)) { + res.push_doc_comment(&variant.attrs, "about", Some("long_about")); + } + + match &*res.kind { + Kind::Flatten(_) => { + if res.has_explicit_methods() { + abort!( + res.kind.span(), + "methods are not allowed for flattened entry" + ); + } + } + + Kind::Subcommand(_) + | Kind::ExternalSubcommand + | Kind::FromGlobal(_) + | Kind::Skip(_, _) + | Kind::Command(_) + | Kind::Value + | Kind::Arg(_) => (), + } + + res + } + + pub fn from_value_enum_variant( + variant: &Variant, + argument_casing: Sp<CasingStyle>, + env_casing: Sp<CasingStyle>, + ) -> Self { + let ident = variant.ident.clone(); + let span = variant.span(); + let kind = Sp::new(Kind::Value, span); + let mut res = Self::new( + Name::Derived(variant.ident.clone()), + ident, + None, + argument_casing, + env_casing, + kind, + ); + let parsed_attrs = ClapAttr::parse_all(&variant.attrs); + res.infer_kind(&parsed_attrs); + res.push_attrs(&parsed_attrs); + if matches!(&*res.kind, Kind::Value) { + res.push_doc_comment(&variant.attrs, "help", None); + } + + res + } + + pub fn from_args_field( + field: &Field, + struct_casing: Sp<CasingStyle>, + env_casing: Sp<CasingStyle>, + ) -> Self { + let name = field.ident.clone().unwrap(); + let ident = field.ident.clone().unwrap(); + let span = field.span(); + let ty = Ty::from_syn_ty(&field.ty); + let kind = Sp::new(Kind::Arg(ty), span); + let mut res = Self::new( + Name::Derived(name), + ident, + Some(field.ty.clone()), + struct_casing, + env_casing, + kind, + ); + let parsed_attrs = ClapAttr::parse_all(&field.attrs); + res.infer_kind(&parsed_attrs); + res.push_attrs(&parsed_attrs); + if matches!(&*res.kind, Kind::Arg(_)) { + res.push_doc_comment(&field.attrs, "help", Some("long_help")); + } + + match &*res.kind { + Kind::Flatten(_) => { + if res.has_explicit_methods() { + abort!( + res.kind.span(), + "methods are not allowed for flattened entry" + ); + } + } + + Kind::Subcommand(_) => { + if res.has_explicit_methods() { + abort!( + res.kind.span(), + "methods in attributes are not allowed for subcommand" + ); + } + } + Kind::Skip(_, _) + | Kind::FromGlobal(_) + | Kind::Arg(_) + | Kind::Command(_) + | Kind::Value + | Kind::ExternalSubcommand => {} + } + + res + } + + fn new( + name: Name, + ident: Ident, + ty: Option<Type>, + casing: Sp<CasingStyle>, + env_casing: Sp<CasingStyle>, + kind: Sp<Kind>, + ) -> Self { + Self { + name, + ident, + ty, + casing, + env_casing, + doc_comment: vec![], + methods: vec![], + deprecations: vec![], + value_parser: None, + action: None, + verbatim_doc_comment: false, + force_long_help: false, + next_display_order: None, + next_help_heading: None, + is_enum: false, + is_positional: true, + skip_group: false, + kind, + } + } + + fn push_method(&mut self, kind: AttrKind, name: Ident, arg: impl ToTokens) { + if name == "id" { + match kind { + AttrKind::Command | AttrKind::Value => { + self.deprecations.push(Deprecation { + span: name.span(), + id: "id_is_only_for_arg", + version: "4.0.0", + description: format!( + "`#[{}(id)] was allowed by mistake, instead use `#[{}(name)]`", + kind.as_str(), + kind.as_str() + ), + }); + } + AttrKind::Group | AttrKind::Arg | AttrKind::Clap | AttrKind::StructOpt => {} + } + self.name = Name::Assigned(quote!(#arg)); + } else if name == "name" { + match kind { + AttrKind::Arg => { + self.deprecations.push(Deprecation { + span: name.span(), + id: "id_is_only_for_arg", + version: "4.0.0", + description: format!( + "`#[{}(name)] was allowed by mistake, instead use `#[{}(id)]` or `#[{}(value_name)]`", + kind.as_str(), + kind.as_str(), + kind.as_str() + ), + }); + } + AttrKind::Group + | AttrKind::Command + | AttrKind::Value + | AttrKind::Clap + | AttrKind::StructOpt => {} + } + 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 infer_kind(&mut self, attrs: &[ClapAttr]) { + for attr in attrs { + if let Some(AttrValue::Call(_)) = &attr.value { + continue; + } + + let actual_attr_kind = *attr.kind.get(); + let kind = match &attr.magic { + Some(MagicAttrName::FromGlobal) => { + if attr.value.is_some() { + let expr = attr.value_or_abort(); + abort!(expr, "attribute `{}` does not accept a value", attr.name); + } + let ty = self + .kind() + .ty() + .cloned() + .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span())); + let kind = Sp::new(Kind::FromGlobal(ty), attr.name.clone().span()); + Some(kind) + } + Some(MagicAttrName::Subcommand) if attr.value.is_none() => { + if attr.value.is_some() { + let expr = attr.value_or_abort(); + abort!(expr, "attribute `{}` does not accept a value", attr.name); + } + let ty = self + .kind() + .ty() + .cloned() + .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span())); + let kind = Sp::new(Kind::Subcommand(ty), attr.name.clone().span()); + Some(kind) + } + Some(MagicAttrName::ExternalSubcommand) if attr.value.is_none() => { + if attr.value.is_some() { + let expr = attr.value_or_abort(); + abort!(expr, "attribute `{}` does not accept a value", attr.name); + } + let kind = Sp::new(Kind::ExternalSubcommand, attr.name.clone().span()); + Some(kind) + } + Some(MagicAttrName::Flatten) if attr.value.is_none() => { + if attr.value.is_some() { + let expr = attr.value_or_abort(); + abort!(expr, "attribute `{}` does not accept a value", attr.name); + } + let ty = self + .kind() + .ty() + .cloned() + .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span())); + let kind = Sp::new(Kind::Flatten(ty), attr.name.clone().span()); + Some(kind) + } + Some(MagicAttrName::Skip) if actual_attr_kind != AttrKind::Group => { + let expr = attr.value.clone(); + let kind = Sp::new( + Kind::Skip(expr, self.kind.attr_kind()), + attr.name.clone().span(), + ); + Some(kind) + } + _ => None, + }; + + if let Some(kind) = kind { + self.set_kind(kind); + } + } + } + + fn push_attrs(&mut self, attrs: &[ClapAttr]) { + for attr in attrs { + let actual_attr_kind = *attr.kind.get(); + let expected_attr_kind = self.kind.attr_kind(); + match (actual_attr_kind, expected_attr_kind) { + (AttrKind::Clap, _) | (AttrKind::StructOpt, _) => { + self.deprecations.push(Deprecation::attribute( + "4.0.0", + actual_attr_kind, + expected_attr_kind, + attr.kind.span(), + )); + } + + (AttrKind::Group, AttrKind::Command) => {} + + _ if attr.kind != expected_attr_kind => { + abort!( + attr.kind.span(), + "Expected `{}` attribute instead of `{}`", + expected_attr_kind.as_str(), + actual_attr_kind.as_str() + ); + } + + _ => {} + } + + if let Some(AttrValue::Call(tokens)) = &attr.value { + // Force raw mode with method call syntax + self.push_method(*attr.kind.get(), attr.name.clone(), quote!(#(#tokens),*)); + continue; + } + + match &attr.magic { + Some(MagicAttrName::Short) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Arg]); + + self.push_method( + *attr.kind.get(), + attr.name.clone(), + self.name.clone().translate_char(*self.casing), + ); + } + + Some(MagicAttrName::Long) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Arg]); + + self.push_method(*attr.kind.get(), attr.name.clone(), self.name.clone().translate(*self.casing)); + } + + Some(MagicAttrName::ValueParser) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Arg]); + + self.deprecations.push(Deprecation { + span: attr.name.span(), + id: "bare_value_parser", + version: "4.0.0", + description: "`#[arg(value_parser)]` is now the default and is no longer needed`".to_owned(), + }); + self.value_parser = Some(ValueParser::Implicit(attr.name.clone())); + } + + Some(MagicAttrName::Action) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Arg]); + + self.deprecations.push(Deprecation { + span: attr.name.span(), + id: "bare_action", + version: "4.0.0", + description: "`#[arg(action)]` is now the default and is no longer needed`".to_owned(), + }); + self.action = Some(Action::Implicit(attr.name.clone())); + } + + Some(MagicAttrName::Env) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Arg]); + + self.push_method( + *attr.kind.get(), + attr.name.clone(), + self.name.clone().translate(*self.env_casing), + ); + } + + Some(MagicAttrName::ValueEnum) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Arg]); + + self.is_enum = true + } + + Some(MagicAttrName::VerbatimDocComment) if attr.value.is_none() => { + self.verbatim_doc_comment = true + } + + Some(MagicAttrName::About) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Command]); + + if let Some(method) = + Method::from_env(attr.name.clone(), "CARGO_PKG_DESCRIPTION") + { + self.methods.push(method); + } + } + + Some(MagicAttrName::LongAbout) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Command]); + + self.force_long_help = true; + } + + Some(MagicAttrName::LongHelp) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Arg]); + + self.force_long_help = true; + } + + Some(MagicAttrName::Author) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Command]); + + if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_AUTHORS") { + self.methods.push(method); + } + } + + Some(MagicAttrName::Version) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Command]); + + if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_VERSION") { + self.methods.push(method); + } + } + + Some(MagicAttrName::DefaultValueT) => { + assert_attr_kind(attr, &[AttrKind::Arg]); + + let ty = if let Some(ty) = self.ty.as_ref() { + ty + } else { + abort!( + attr.name.clone(), + "#[arg(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) = &attr.value { + quote!(#expr) + } else { + quote!(<#ty as ::std::default::Default>::default()) + }; + + let val = if attrs + .iter() + .any(|a| a.magic == Some(MagicAttrName::ValueEnum)) + { + quote_spanned!(attr.name.clone().span()=> { + static DEFAULT_VALUE: clap::__macro_refs::once_cell::sync::Lazy<String> = clap::__macro_refs::once_cell::sync::Lazy::new(|| { + let val: #ty = #val; + clap::ValueEnum::to_possible_value(&val).unwrap().get_name().to_owned() + }); + let s: &'static str = &*DEFAULT_VALUE; + s + }) + } else { + quote_spanned!(attr.name.clone().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) + }); + let s: &'static str = &*DEFAULT_VALUE; + s + }) + }; + + let raw_ident = Ident::new("default_value", attr.name.clone().span()); + self.methods.push(Method::new(raw_ident, val)); + } + + Some(MagicAttrName::DefaultValuesT) => { + assert_attr_kind(attr, &[AttrKind::Arg]); + + let ty = if let Some(ty) = self.ty.as_ref() { + ty + } else { + abort!( + attr.name.clone(), + "#[arg(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 expr = attr.value_or_abort(); + + let container_type = Ty::from_syn_ty(ty); + if *container_type != Ty::Vec { + abort!( + attr.name.clone(), + "#[arg(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 attrs + .iter() + .any(|a| a.magic == Some(MagicAttrName::ValueEnum)) + { + quote_spanned!(attr.name.clone().span()=> { + { + fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=String> + where + T: ::std::borrow::Borrow<#inner_type> + { + iterable + .into_iter() + .map(|val| { + clap::ValueEnum::to_possible_value(val.borrow()).unwrap().get_name().to_owned() + }) + } + + 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).collect() + }); + + 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.iter().copied() + } + }) + } else { + quote_spanned!(attr.name.clone().span()=> { + { + fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=String> + where + T: ::std::borrow::Borrow<#inner_type> + { + iterable.into_iter().map(|val| val.borrow().to_string()) + } + + 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).collect() + }); + + 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.iter().copied() + } + }) + }; + + self.methods.push(Method::new( + Ident::new("default_values", attr.name.clone().span()), + val, + )); + } + + Some(MagicAttrName::DefaultValueOsT) => { + assert_attr_kind(attr, &[AttrKind::Arg]); + + let ty = if let Some(ty) = self.ty.as_ref() { + ty + } else { + abort!( + attr.name.clone(), + "#[arg(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) = &attr.value { + quote!(#expr) + } else { + quote!(<#ty as ::std::default::Default>::default()) + }; + + let val = if attrs + .iter() + .any(|a| a.magic == Some(MagicAttrName::ValueEnum)) + { + quote_spanned!(attr.name.clone().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; + clap::ValueEnum::to_possible_value(&val).unwrap().get_name().to_owned() + }); + let s: &'static ::std::ffi::OsStr = &*DEFAULT_VALUE; + s + }) + } else { + quote_spanned!(attr.name.clone().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) + }); + let s: &'static ::std::ffi::OsStr = &*DEFAULT_VALUE; + s + }) + }; + + let raw_ident = Ident::new("default_value", attr.name.clone().span()); + self.methods.push(Method::new(raw_ident, val)); + } + + Some(MagicAttrName::DefaultValuesOsT) => { + assert_attr_kind(attr, &[AttrKind::Arg]); + + let ty = if let Some(ty) = self.ty.as_ref() { + ty + } else { + abort!( + attr.name.clone(), + "#[arg(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 expr = attr.value_or_abort(); + + let container_type = Ty::from_syn_ty(ty); + if *container_type != Ty::Vec { + abort!( + attr.name.clone(), + "#[arg(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 attrs + .iter() + .any(|a| a.magic == Some(MagicAttrName::ValueEnum)) + { + quote_spanned!(attr.name.clone().span()=> { + { + fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=::std::ffi::OsString> + where + T: ::std::borrow::Borrow<#inner_type> + { + iterable + .into_iter() + .map(|val| { + clap::ValueEnum::to_possible_value(val.borrow()).unwrap().get_name().to_owned().into() + }) + } + + 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).collect() + }); + + 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.iter().copied() + } + }) + } else { + quote_spanned!(attr.name.clone().span()=> { + { + fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=::std::ffi::OsString> + where + T: ::std::borrow::Borrow<#inner_type> + { + iterable.into_iter().map(|val| val.borrow().into()) + } + + 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).collect() + }); + + 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.iter().copied() + } + }) + }; + + self.methods.push(Method::new( + Ident::new("default_values", attr.name.clone().span()), + val, + )); + } + + Some(MagicAttrName::NextDisplayOrder) => { + assert_attr_kind(attr, &[AttrKind::Command]); + + let expr = attr.value_or_abort(); + self.next_display_order = Some(Method::new(attr.name.clone(), quote!(#expr))); + } + + Some(MagicAttrName::NextHelpHeading) => { + assert_attr_kind(attr, &[AttrKind::Command]); + + let expr = attr.value_or_abort(); + self.next_help_heading = Some(Method::new(attr.name.clone(), quote!(#expr))); + } + + Some(MagicAttrName::RenameAll) => { + let lit = attr.lit_str_or_abort(); + self.casing = CasingStyle::from_lit(lit); + } + + Some(MagicAttrName::RenameAllEnv) => { + assert_attr_kind(attr, &[AttrKind::Command, AttrKind::Arg]); + + let lit = attr.lit_str_or_abort(); + self.env_casing = CasingStyle::from_lit(lit); + } + + Some(MagicAttrName::Skip) if actual_attr_kind == AttrKind::Group => { + self.skip_group = true; + } + + None + // Magic only for the default, otherwise just forward to the builder + | Some(MagicAttrName::Short) + | Some(MagicAttrName::Long) + | Some(MagicAttrName::Env) + | Some(MagicAttrName::About) + | Some(MagicAttrName::LongAbout) + | Some(MagicAttrName::LongHelp) + | Some(MagicAttrName::Author) + | Some(MagicAttrName::Version) + => { + let expr = attr.value_or_abort(); + self.push_method(*attr.kind.get(), attr.name.clone(), expr); + } + + // Magic only for the default, otherwise just forward to the builder + Some(MagicAttrName::ValueParser) | Some(MagicAttrName::Action) => { + let expr = attr.value_or_abort(); + self.push_method(*attr.kind.get(), attr.name.clone(), expr); + } + + // Directives that never receive a value + Some(MagicAttrName::ValueEnum) + | Some(MagicAttrName::VerbatimDocComment) => { + let expr = attr.value_or_abort(); + abort!(expr, "attribute `{}` does not accept a value", attr.name); + } + + // Kinds + Some(MagicAttrName::FromGlobal) + | Some(MagicAttrName::Subcommand) + | Some(MagicAttrName::ExternalSubcommand) + | Some(MagicAttrName::Flatten) + | Some(MagicAttrName::Skip) => { + } + } + } + + if self.has_explicit_methods() { + if let Kind::Skip(_, attr) = &*self.kind { + abort!( + self.methods[0].name.span(), + "`{}` cannot be used with `#[{}(skip)]", + self.methods[0].name, + attr.as_str(), + ); + } + if let Kind::FromGlobal(_) = &*self.kind { + abort!( + self.methods[0].name.span(), + "`{}` cannot be used with `#[arg(from_global)]", + self.methods[0].name, + ); + } + } + } + + fn push_doc_comment(&mut self, attrs: &[Attribute], short_name: &str, long_name: Option<&str>) { + let lines = extract_doc_comment(attrs); + + if !lines.is_empty() { + let (short_help, long_help) = + format_doc_comment(&lines, !self.verbatim_doc_comment, self.force_long_help); + let short_name = format_ident!("{}", short_name); + let short = Method::new( + short_name, + short_help + .map(|h| quote!(#h)) + .unwrap_or_else(|| quote!(None)), + ); + self.doc_comment.push(short); + if let Some(long_name) = long_name { + let long_name = format_ident!("{}", long_name); + let long = Method::new( + long_name, + long_help + .map(|h| quote!(#h)) + .unwrap_or_else(|| quote!(None)), + ); + self.doc_comment.push(long); + } + } + } + + fn set_kind(&mut self, kind: Sp<Kind>) { + match (self.kind.get(), kind.get()) { + (Kind::Arg(_), Kind::FromGlobal(_)) + | (Kind::Arg(_), Kind::Subcommand(_)) + | (Kind::Arg(_), Kind::Flatten(_)) + | (Kind::Arg(_), Kind::Skip(_, _)) + | (Kind::Command(_), Kind::Subcommand(_)) + | (Kind::Command(_), Kind::Flatten(_)) + | (Kind::Command(_), Kind::Skip(_, _)) + | (Kind::Command(_), Kind::ExternalSubcommand) + | (Kind::Value, Kind::Skip(_, _)) => { + self.kind = kind; + } + + (_, _) => { + let old = self.kind.name(); + let new = kind.name(); + abort!(kind.span(), "`{}` cannot be used with `{}`", new, old); + } + } + } + + 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(); + quote!( + #(#next_display_order)* + #(#next_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) -> proc_macro2::TokenStream { + let methods = &self.methods; + let doc_comment = &self.doc_comment; + quote!( #(#doc_comment)* #(#methods)* ) + } + + pub fn deprecations(&self) -> proc_macro2::TokenStream { + let deprecations = &self.deprecations; + quote!( #(#deprecations)* ) + } + + 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(); + quote!( #(#next_help_heading)* ) + } + + pub fn ident(&self) -> &Ident { + &self.ident + } + + pub fn id(&self) -> TokenStream { + self.name.clone().raw() + } + + 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(|| { + let inner_type = inner_type(field_type); + if let Some(action) = self.action.as_ref() { + let span = action.span(); + default_value_parser(inner_type, span) + } else { + 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 { + let span = self + .value_parser + .as_ref() + .map(|a| a.span()) + .unwrap_or_else(|| self.kind.span()); + default_action(field_type, span) + } + }) + } + + pub fn kind(&self) -> Sp<Kind> { + self.kind.clone() + } + + pub fn is_positional(&self) -> bool { + self.is_positional + } + + pub fn casing(&self) -> Sp<CasingStyle> { + self.casing + } + + pub fn env_casing(&self) -> Sp<CasingStyle> { + self.env_casing + } + + pub fn has_explicit_methods(&self) -> bool { + self.methods + .iter() + .any(|m| m.name != "help" && m.name != "long_help") + } + + pub fn skip_group(&self) -> bool { + self.skip_group + } +} + +#[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 | Ty::VecVec | Ty::OptionVecVec => { + 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>), + Command(Sp<Ty>), + Value, + FromGlobal(Sp<Ty>), + Subcommand(Sp<Ty>), + Flatten(Sp<Ty>), + Skip(Option<AttrValue>, AttrKind), + ExternalSubcommand, +} + +impl Kind { + pub fn name(&self) -> &'static str { + match self { + Self::Arg(_) => "arg", + Self::Command(_) => "command", + Self::Value => "value", + Self::FromGlobal(_) => "from_global", + Self::Subcommand(_) => "subcommand", + Self::Flatten(_) => "flatten", + Self::Skip(_, _) => "skip", + Self::ExternalSubcommand => "external_subcommand", + } + } + + pub fn attr_kind(&self) -> AttrKind { + match self { + Self::Arg(_) => AttrKind::Arg, + Self::Command(_) => AttrKind::Command, + Self::Value => AttrKind::Value, + Self::FromGlobal(_) => AttrKind::Arg, + Self::Subcommand(_) => AttrKind::Command, + Self::Flatten(_) => AttrKind::Command, + Self::Skip(_, kind) => *kind, + Self::ExternalSubcommand => AttrKind::Command, + } + } + + pub fn ty(&self) -> Option<&Sp<Ty>> { + match self { + Self::Arg(ty) + | Self::Command(ty) + | Self::Flatten(ty) + | Self::FromGlobal(ty) + | Self::Subcommand(ty) => Some(ty), + Self::Value | Self::Skip(_, _) | Self::ExternalSubcommand => None, + } + } +} + +#[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); + } +} + +#[derive(Clone)] +pub struct Deprecation { + pub span: Span, + pub id: &'static str, + pub version: &'static str, + pub description: String, +} + +impl Deprecation { + fn attribute(version: &'static str, old: AttrKind, new: AttrKind, span: Span) -> Self { + Self { + span, + id: "old_attribute", + version, + description: format!( + "Attribute `#[{}(...)]` has been deprecated in favor of `#[{}(...)]`", + old.as_str(), + new.as_str() + ), + } + } +} + +impl ToTokens for Deprecation { + fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) { + let tokens = if cfg!(feature = "deprecated") { + let Deprecation { + span, + id, + version, + description, + } = self; + let span = *span; + let id = Ident::new(id, span); + + quote_spanned!(span=> { + #[deprecated(since = #version, note = #description)] + fn #id() {} + #id(); + }) + } else { + quote!() + }; + + tokens.to_tokens(ts); + } +} + +fn assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind]) { + if *attr.kind.get() == AttrKind::Clap || *attr.kind.get() == AttrKind::StructOpt { + // deprecated + } else if !possible_kind.contains(attr.kind.get()) { + let options = possible_kind + .iter() + .map(|k| format!("`#[{}({})]`", k.as_str(), attr.name)) + .collect::<Vec<_>>(); + abort!( + attr.name, + "Unknown `#[{}({})]` attribute ({} exists)", + attr.kind.as_str(), + attr.name, + options.join(", ") + ); + } +} + +/// 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 +} + +/// Defines the casing for the attributes long representation. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +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 { + 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) + } + } + } +} diff --git a/vendor/clap_derive/src/lib.rs b/vendor/clap_derive/src/lib.rs index 86b7628f6..84f2f0ded 100644 --- a/vendor/clap_derive/src/lib.rs +++ b/vendor/clap_derive/src/lib.rs @@ -22,35 +22,27 @@ use proc_macro::TokenStream; use proc_macro_error::proc_macro_error; use syn::{parse_macro_input, DeriveInput}; -mod attrs; +mod attr; mod derives; mod dummies; -mod parse; +mod item; mod utils; /// Generates the `ValueEnum` impl. -#[proc_macro_derive(ValueEnum, attributes(clap))] +#[proc_macro_derive(ValueEnum, attributes(clap, value))] #[proc_macro_error] pub fn value_enum(input: TokenStream) -> TokenStream { let input: DeriveInput = parse_macro_input!(input); derives::derive_value_enum(&input).into() } -/// Generates the `ValueEnum` impl. -#[proc_macro_derive(ArgEnum, attributes(clap))] -#[proc_macro_error] -pub fn arg_enum(input: TokenStream) -> TokenStream { - let input: DeriveInput = parse_macro_input!(input); - derives::derive_value_enum(&input).into() -} - /// Generates the `Parser` implementation. /// /// This is far less verbose than defining the `clap::Command` struct manually, /// receiving an instance of `clap::ArgMatches` from conducting parsing, and then /// implementing a conversion code to instantiate an instance of the user /// context struct. -#[proc_macro_derive(Parser, attributes(clap, structopt))] +#[proc_macro_derive(Parser, attributes(clap, structopt, command, arg, group))] #[proc_macro_error] pub fn parser(input: TokenStream) -> TokenStream { let input: DeriveInput = parse_macro_input!(input); @@ -58,7 +50,7 @@ pub fn parser(input: TokenStream) -> TokenStream { } /// Generates the `Subcommand` impl. -#[proc_macro_derive(Subcommand, attributes(clap))] +#[proc_macro_derive(Subcommand, attributes(clap, command, arg, group))] #[proc_macro_error] pub fn subcommand(input: TokenStream) -> TokenStream { let input: DeriveInput = parse_macro_input!(input); @@ -66,7 +58,7 @@ pub fn subcommand(input: TokenStream) -> TokenStream { } /// Generates the `Args` impl. -#[proc_macro_derive(Args, attributes(clap))] +#[proc_macro_derive(Args, attributes(clap, command, arg, group))] #[proc_macro_error] pub fn args(input: TokenStream) -> TokenStream { let input: DeriveInput = parse_macro_input!(input); diff --git a/vendor/clap_derive/src/parse.rs b/vendor/clap_derive/src/parse.rs deleted file mode 100644 index 7d7e19cdc..000000000 --- a/vendor/clap_derive/src/parse.rs +++ /dev/null @@ -1,288 +0,0 @@ -use std::iter::FromIterator; - -use proc_macro_error::{abort, ResultExt}; -use quote::ToTokens; -use syn::{ - self, parenthesized, - parse::{Parse, ParseBuffer, ParseStream}, - punctuated::Punctuated, - Attribute, Expr, ExprLit, Ident, Lit, LitBool, LitStr, Token, -}; - -pub fn parse_clap_attributes(all_attrs: &[Attribute]) -> Vec<ClapAttr> { - all_attrs - .iter() - .filter(|attr| attr.path.is_ident("clap") || attr.path.is_ident("structopt")) - .flat_map(|attr| { - attr.parse_args_with(Punctuated::<ClapAttr, Token![,]>::parse_terminated) - .unwrap_or_abort() - }) - .collect() -} - -#[allow(clippy::large_enum_variant)] -#[derive(Clone)] -pub enum ClapAttr { - // single-identifier attributes - Short(Ident), - Long(Ident), - ValueParser(Ident), - Action(Ident), - Env(Ident), - Flatten(Ident), - ValueEnum(Ident), - FromGlobal(Ident), - Subcommand(Ident), - VerbatimDocComment(Ident), - ExternalSubcommand(Ident), - About(Ident), - Author(Ident), - Version(Ident), - - // ident = "string literal" - RenameAllEnv(Ident, LitStr), - RenameAll(Ident, LitStr), - NameLitStr(Ident, LitStr), - - // parse(parser_kind [= parser_func]) - Parse(Ident, ParserSpec), - - // ident [= arbitrary_expr] - Skip(Ident, Option<Expr>), - - // ident = arbitrary_expr - NameExpr(Ident, Expr), - DefaultValueT(Ident, Option<Expr>), - DefaultValuesT(Ident, Expr), - DefaultValueOsT(Ident, Option<Expr>), - DefaultValuesOsT(Ident, Expr), - NextDisplayOrder(Ident, Expr), - NextHelpHeading(Ident, Expr), - HelpHeading(Ident, Expr), - - // ident(arbitrary_expr,*) - MethodCall(Ident, Vec<Expr>), -} - -impl Parse for ClapAttr { - fn parse(input: ParseStream) -> syn::Result<Self> { - use self::ClapAttr::*; - - let name: Ident = input.parse()?; - let name_str = name.to_string(); - - if input.peek(Token![=]) { - // `name = value` attributes. - let assign_token = input.parse::<Token![=]>()?; // skip '=' - - if input.peek(LitStr) { - let lit: LitStr = input.parse()?; - - match &*name_str { - "rename_all" => Ok(RenameAll(name, lit)), - "rename_all_env" => Ok(RenameAllEnv(name, lit)), - - "skip" => { - let expr = ExprLit { - attrs: vec![], - lit: Lit::Str(lit), - }; - let expr = Expr::Lit(expr); - Ok(Skip(name, Some(expr))) - } - - "next_display_order" => { - let expr = ExprLit { - attrs: vec![], - lit: Lit::Str(lit), - }; - let expr = Expr::Lit(expr); - Ok(NextDisplayOrder(name, expr)) - } - - "next_help_heading" => { - let expr = ExprLit { - attrs: vec![], - lit: Lit::Str(lit), - }; - let expr = Expr::Lit(expr); - Ok(NextHelpHeading(name, expr)) - } - "help_heading" => { - let expr = ExprLit { - attrs: vec![], - lit: Lit::Str(lit), - }; - let expr = Expr::Lit(expr); - Ok(HelpHeading(name, expr)) - } - - _ => Ok(NameLitStr(name, lit)), - } - } else { - match input.parse::<Expr>() { - Ok(expr) => match &*name_str { - "skip" => Ok(Skip(name, Some(expr))), - "default_value_t" => Ok(DefaultValueT(name, Some(expr))), - "default_values_t" => Ok(DefaultValuesT(name, expr)), - "default_value_os_t" => Ok(DefaultValueOsT(name, Some(expr))), - "default_values_os_t" => Ok(DefaultValuesOsT(name, expr)), - "next_display_order" => Ok(NextDisplayOrder(name, expr)), - "next_help_heading" => Ok(NextHelpHeading(name, expr)), - "help_heading" => Ok(HelpHeading(name, expr)), - _ => Ok(NameExpr(name, expr)), - }, - - Err(_) => abort! { - assign_token, - "expected `string literal` or `expression` after `=`" - }, - } - } - } else if input.peek(syn::token::Paren) { - // `name(...)` attributes. - let nested; - parenthesized!(nested in input); - - match name_str.as_ref() { - "parse" => { - let parser_specs: Punctuated<ParserSpec, Token![,]> = - nested.parse_terminated(ParserSpec::parse)?; - - if parser_specs.len() == 1 { - Ok(Parse(name, parser_specs[0].clone())) - } else { - abort!(name, "parse must have exactly one argument") - } - } - - "raw" => match nested.parse::<LitBool>() { - Ok(bool_token) => { - let expr = ExprLit { - attrs: vec![], - lit: Lit::Bool(bool_token), - }; - let expr = Expr::Lit(expr); - Ok(MethodCall(name, vec![expr])) - } - - Err(_) => { - abort!(name, - "`#[clap(raw(...))` attributes are removed, \ - they are replaced with raw methods"; - help = "if you meant to call `clap::Arg::raw()` method \ - you should use bool literal, like `raw(true)` or `raw(false)`"; - note = raw_method_suggestion(nested); - ); - } - }, - - _ => { - let method_args: Punctuated<_, Token![,]> = - nested.parse_terminated(Expr::parse)?; - Ok(MethodCall(name, Vec::from_iter(method_args))) - } - } - } else { - // Attributes represented with a sole identifier. - match name_str.as_ref() { - "long" => Ok(Long(name)), - "short" => Ok(Short(name)), - "value_parser" => Ok(ValueParser(name)), - "action" => Ok(Action(name)), - "env" => Ok(Env(name)), - "flatten" => Ok(Flatten(name)), - "arg_enum" => Ok(ValueEnum(name)), - "value_enum" => Ok(ValueEnum(name)), - "from_global" => Ok(FromGlobal(name)), - "subcommand" => Ok(Subcommand(name)), - "external_subcommand" => Ok(ExternalSubcommand(name)), - "verbatim_doc_comment" => Ok(VerbatimDocComment(name)), - - "default_value" => { - abort!(name, - "`#[clap(default_value)` attribute (without a value) has been replaced by `#[clap(default_value_t)]`."; - help = "Change the attribute to `#[clap(default_value_t)]`"; - ) - } - "default_value_t" => Ok(DefaultValueT(name, None)), - "default_value_os_t" => Ok(DefaultValueOsT(name, None)), - "about" => (Ok(About(name))), - "author" => (Ok(Author(name))), - "version" => Ok(Version(name)), - - "skip" => Ok(Skip(name, None)), - - _ => abort!(name, "unexpected attribute: {}", name_str), - } - } - } -} - -#[derive(Clone)] -pub struct ParserSpec { - pub kind: Ident, - pub eq_token: Option<Token![=]>, - pub parse_func: Option<Expr>, -} - -impl Parse for ParserSpec { - fn parse(input: ParseStream<'_>) -> syn::Result<Self> { - let kind = input - .parse() - .map_err(|_| input.error("parser specification must start with identifier"))?; - let eq_token = input.parse()?; - let parse_func = match eq_token { - None => None, - Some(_) => Some(input.parse()?), - }; - Ok(ParserSpec { - kind, - eq_token, - parse_func, - }) - } -} - -fn raw_method_suggestion(ts: ParseBuffer) -> String { - let do_parse = move || -> Result<(Ident, Punctuated<Expr, Token![,]>), syn::Error> { - let name = ts.parse()?; - let _eq: Token![=] = ts.parse()?; - let val: LitStr = ts.parse()?; - let exprs = val.parse_with(Punctuated::<Expr, Token![,]>::parse_terminated)?; - Ok((name, exprs)) - }; - - fn to_string<T: ToTokens>(val: &T) -> String { - val.to_token_stream() - .to_string() - .replace(' ', "") - .replace(',', ", ") - } - - if let Ok((name, exprs)) = do_parse() { - let suggestion = if exprs.len() == 1 { - let val = to_string(&exprs[0]); - format!(" = {}", val) - } else { - let val = exprs - .into_iter() - .map(|expr| to_string(&expr)) - .collect::<Vec<_>>() - .join(", "); - - format!("({})", val) - }; - - format!( - "if you need to call `clap::Arg/Command::{}` method you \ - can do it like this: #[clap({}{})]", - name, name, suggestion - ) - } else { - "if you need to call some method from `clap::Arg/Command` \ - you should use raw method, see \ - https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#raw-attributes" - .into() - } -} diff --git a/vendor/clap_derive/src/utils/doc_comments.rs b/vendor/clap_derive/src/utils/doc_comments.rs index f0a5034d7..5183b6b25 100644 --- a/vendor/clap_derive/src/utils/doc_comments.rs +++ b/vendor/clap_derive/src/utils/doc_comments.rs @@ -3,43 +3,58 @@ //! #[derive(Parser)] works in terms of "paragraphs". Paragraph is a sequence of //! non-empty adjacent lines, delimited by sequences of blank (whitespace only) lines. -use crate::attrs::Method; - -use quote::{format_ident, quote}; use std::iter; -pub fn process_doc_comment(lines: Vec<String>, name: &str, preprocess: bool) -> Vec<Method> { +pub fn extract_doc_comment(attrs: &[syn::Attribute]) -> Vec<String> { + use syn::Lit::*; + use syn::Meta::*; + use syn::MetaNameValue; + // multiline comments (`/** ... */`) may have LFs (`\n`) in them, // we need to split so we could handle the lines correctly // // we also need to remove leading and trailing blank lines - let mut lines: Vec<&str> = lines + let mut lines: 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 + } + }) .skip_while(|s| is_blank(s)) - .flat_map(|s| s.split('\n')) + .flat_map(|s| { + let lines = s + .split('\n') + .map(|s| { + // remove one leading space no matter what + let s = s.strip_prefix(' ').unwrap_or(s); + s.to_owned() + }) + .collect::<Vec<_>>(); + lines + }) .collect(); while let Some(true) = lines.last().map(|s| is_blank(s)) { lines.pop(); } - // remove one leading space no matter what - for line in lines.iter_mut() { - if line.starts_with(' ') { - *line = &line[1..]; - } - } - - if lines.is_empty() { - return vec![]; - } - - let short_name = format_ident!("{}", name); - let long_name = format_ident!("long_{}", name); + lines +} +pub fn format_doc_comment( + lines: &[String], + preprocess: bool, + force_long: bool, +) -> (Option<String>, Option<String>) { if let Some(first_blank) = lines.iter().position(|s| is_blank(s)) { let (short, long) = if preprocess { - let paragraphs = split_paragraphs(&lines); + let paragraphs = split_paragraphs(lines); let short = paragraphs[0].clone(); let long = paragraphs.join("\n\n"); (remove_period(short), long) @@ -49,26 +64,24 @@ pub fn process_doc_comment(lines: Vec<String>, name: &str, preprocess: bool) -> (short, long) }; - vec![ - Method::new(short_name, quote!(#short)), - Method::new(long_name, quote!(#long)), - ] + (Some(short), Some(long)) } else { - let short = if preprocess { - let s = merge_lines(&lines); - remove_period(s) + let (short, long) = if preprocess { + let short = merge_lines(lines); + let long = force_long.then(|| short.clone()); + let short = remove_period(short); + (short, long) } else { - lines.join("\n") + let short = lines.join("\n"); + let long = force_long.then(|| short.clone()); + (short, long) }; - vec![ - Method::new(short_name, quote!(#short)), - Method::new(long_name, quote!(None)), - ] + (Some(short), long) } } -fn split_paragraphs(lines: &[&str]) -> Vec<String> { +fn split_paragraphs(lines: &[String]) -> Vec<String> { let mut last_line = 0; iter::from_fn(|| { let slice = &lines[last_line..]; @@ -102,6 +115,10 @@ fn is_blank(s: &str) -> bool { s.trim().is_empty() } -fn merge_lines(lines: &[&str]) -> String { - lines.iter().map(|s| s.trim()).collect::<Vec<_>>().join(" ") +fn merge_lines(lines: impl IntoIterator<Item = impl AsRef<str>>) -> String { + lines + .into_iter() + .map(|s| s.as_ref().trim().to_owned()) + .collect::<Vec<_>>() + .join(" ") } diff --git a/vendor/clap_derive/src/utils/mod.rs b/vendor/clap_derive/src/utils/mod.rs index 77a467c75..9f8b6f380 100644 --- a/vendor/clap_derive/src/utils/mod.rs +++ b/vendor/clap_derive/src/utils/mod.rs @@ -2,8 +2,10 @@ mod doc_comments; mod spanned; mod ty; +pub use doc_comments::extract_doc_comment; +pub use doc_comments::format_doc_comment; + pub use self::{ - doc_comments::process_doc_comment, spanned::Sp, ty::{inner_type, is_simple_ty, sub_type, subty_if_name, Ty}, }; diff --git a/vendor/clap_derive/src/utils/spanned.rs b/vendor/clap_derive/src/utils/spanned.rs index 11415f6f0..339a654e6 100644 --- a/vendor/clap_derive/src/utils/spanned.rs +++ b/vendor/clap_derive/src/utils/spanned.rs @@ -5,7 +5,7 @@ use syn::LitStr; use std::ops::{Deref, DerefMut}; /// An entity with a span attached. -#[derive(Debug, Clone)] +#[derive(Debug, Copy, Clone)] pub struct Sp<T> { val: T, span: Span, @@ -16,11 +16,8 @@ impl<T> Sp<T> { Sp { val, span } } - pub fn call_site(val: T) -> Self { - Sp { - val, - span: Span::call_site(), - } + pub fn get(&self) -> &T { + &self.val } pub fn span(&self) -> Span { diff --git a/vendor/clap_derive/src/utils/ty.rs b/vendor/clap_derive/src/utils/ty.rs index 0bcb59f27..9349bc29d 100644 --- a/vendor/clap_derive/src/utils/ty.rs +++ b/vendor/clap_derive/src/utils/ty.rs @@ -7,12 +7,15 @@ use syn::{ PathSegment, Type, TypePath, }; -#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum Ty { + Unit, Vec, + VecVec, Option, OptionOption, OptionVec, + OptionVecVec, Other, } @@ -21,13 +24,15 @@ impl Ty { use self::Ty::*; let t = |kind| Sp::new(kind, ty.span()); - if is_generic_ty(ty, "Vec") { - t(Vec) + if is_unit_ty(ty) { + t(Unit) + } else if let Some(vt) = get_vec_ty(ty, Vec, VecVec) { + t(vt) } else if let Some(subty) = subty_if_name(ty, "Option") { if is_generic_ty(subty, "Option") { t(OptionOption) - } else if is_generic_ty(subty, "Vec") { - t(OptionVec) + } else if let Some(vt) = get_vec_ty(subty, OptionVec, OptionVecVec) { + t(vt) } else { t(Option) } @@ -35,15 +40,32 @@ impl Ty { t(Other) } } + + pub fn as_str(&self) -> &'static str { + match self { + Self::Unit => "()", + Self::Vec => "Vec<T>", + Self::Option => "Option<T>", + Self::OptionOption => "Option<Option<T>>", + Self::OptionVec => "Option<Vec<T>>", + Self::VecVec => "Vec<Vec<T>>", + Self::OptionVecVec => "Option<Vec<Vec<T>>>", + Self::Other => "...other...", + } + } } pub fn inner_type(field_ty: &syn::Type) -> &syn::Type { let ty = Ty::from_syn_ty(field_ty); match *ty { Ty::Vec | Ty::Option => sub_type(field_ty).unwrap_or(field_ty), - Ty::OptionOption | Ty::OptionVec => { + Ty::OptionOption | Ty::OptionVec | Ty::VecVec => { sub_type(field_ty).and_then(sub_type).unwrap_or(field_ty) } + Ty::OptionVecVec => sub_type(field_ty) + .and_then(sub_type) + .and_then(sub_type) + .unwrap_or(field_ty), _ => field_ty, } } @@ -111,9 +133,33 @@ fn is_generic_ty(ty: &syn::Type, name: &str) -> bool { subty_if_name(ty, name).is_some() } +fn is_unit_ty(ty: &syn::Type) -> bool { + if let syn::Type::Tuple(tuple) = ty { + tuple.elems.is_empty() + } else { + false + } +} + fn only_one<I, T>(mut iter: I) -> Option<T> where I: Iterator<Item = T>, { iter.next().filter(|_| iter.next().is_none()) } + +#[cfg(feature = "unstable-v5")] +fn get_vec_ty(ty: &Type, vec_ty: Ty, vecvec_ty: Ty) -> Option<Ty> { + subty_if_name(ty, "Vec").map(|subty| { + if is_generic_ty(subty, "Vec") { + vecvec_ty + } else { + vec_ty + } + }) +} + +#[cfg(not(feature = "unstable-v5"))] +fn get_vec_ty(ty: &Type, vec_ty: Ty, _vecvec_ty: Ty) -> Option<Ty> { + is_generic_ty(ty, "Vec").then_some(vec_ty) +} |