diff --git a/clap_derive/src/attrs.rs b/clap_derive/src/attrs.rs index da38c01630b..0155fccf3a9 100644 --- a/clap_derive/src/attrs.rs +++ b/clap_derive/src/attrs.rs @@ -44,7 +44,6 @@ pub struct Attrs { methods: Vec, value_parser: Option, action: Option, - parser: Option>, verbatim_doc_comment: Option, next_display_order: Option, next_help_heading: Option, @@ -78,9 +77,6 @@ impl Attrs { "`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"), @@ -124,12 +120,6 @@ impl Attrs { "`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(), @@ -156,12 +146,6 @@ impl Attrs { "`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; @@ -236,9 +220,6 @@ impl Attrs { "`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, @@ -282,12 +263,6 @@ impl Attrs { "`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(), @@ -318,12 +293,6 @@ impl Attrs { "`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(), @@ -363,25 +332,7 @@ impl Attrs { 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()), - } - } + let ty = Ty::from_syn_ty(&field.ty); match *ty { Ty::Option => { @@ -438,7 +389,6 @@ impl Attrs { methods: vec![], value_parser: None, action: None, - parser: None, verbatim_doc_comment: None, next_display_order: None, next_help_heading: None, @@ -650,10 +600,6 @@ impl Attrs { RenameAllEnv(_, casing_lit) => { self.env_casing = CasingStyle::from_lit(casing_lit); } - - Parse(ident, spec) => { - self.parser = Some(Parser::from_spec(ident, spec)); - } } } } @@ -691,10 +637,6 @@ impl Attrs { } } - 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() @@ -770,14 +712,11 @@ impl Attrs { p.resolve(inner_type) }) .unwrap_or_else(|| { + let inner_type = inner_type(field_type); 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() { - self.parser(field_type).value_parser() } else { - let inner_type = inner_type(field_type); let span = self .action .as_ref() @@ -796,8 +735,6 @@ impl Attrs { 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() { - self.parser(field_type).action() } else { let span = self .value_parser @@ -809,42 +746,14 @@ impl Attrs { }) } - pub fn ignore_parser(&self) -> bool { - self.parser.is_none() - } - - pub fn explicit_parser(&self) -> bool { - self.parser.is_some() - } - - pub fn parser(&self, field_type: &Type) -> Sp { - self.parser - .clone() - .unwrap_or_else(|| Parser::from_type(field_type, self.kind.span())) - } - pub fn kind(&self) -> Sp { 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 { self.casing.clone() } @@ -1031,114 +940,6 @@ fn process_author_str(author: &str) -> String { res } -#[derive(Clone)] -pub struct Parser { - pub kind: Sp, - pub func: TokenStream, -} - -impl Parser { - fn from_type(field_type: &Type, span: Span) -> Sp { - 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 { - 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 { diff --git a/clap_derive/src/derives/args.rs b/clap_derive/src/derives/args.rs index 4195a081082..ea5c5539d9f 100644 --- a/clap_derive/src/derives/args.rs +++ b/clap_derive/src/derives/args.rs @@ -13,9 +13,9 @@ // MIT/Apache 2.0 license. use crate::{ - attrs::{Attrs, Kind, Name, ParserKind, DEFAULT_CASING, DEFAULT_ENV_CASING}, + attrs::{Attrs, Kind, Name, DEFAULT_CASING, DEFAULT_ENV_CASING}, dummies, - utils::{inner_type, is_simple_ty, sub_type, Sp, Ty}, + utils::{inner_type, sub_type, Sp, Ty}, }; use proc_macro2::{Ident, Span, TokenStream}; @@ -23,7 +23,7 @@ use proc_macro_error::{abort, abort_call_site}; use quote::{format_ident, quote, quote_spanned}; use syn::{ punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, Data, DataStruct, - DeriveInput, Field, Fields, Generics, Type, + DeriveInput, Field, Fields, Generics, }; pub fn derive_args(input: &DeriveInput) -> TokenStream { @@ -244,91 +244,15 @@ pub fn gen_augment( } } 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 implicit_methods = match **ty { Ty::Option => { quote_spanned! { ty.span()=> .takes_value(true) .value_name(#value_name) - #possible_values - #validator #value_parser #action } @@ -340,41 +264,23 @@ pub fn gen_augment( .min_values(0) .max_values(1) .multiple_values(false) - #possible_values - #validator #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 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 + #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,48 +288,24 @@ 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 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 + #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::Other => { let required = attrs.find_default_method().is_none() && !override_required; // `ArgAction::takes_values` is assuming `ArgAction::default_value` will be @@ -434,8 +316,6 @@ pub fn gen_augment( .takes_value(true) .value_name(#value_name) .required(#required && #action_value.takes_values()) - #possible_values - #validator #value_parser #action } @@ -447,8 +327,6 @@ pub fn gen_augment( Some(quote_spanned! { field.span()=> let #app_var = #app_var.arg({ - #parse_deprecation - #[allow(deprecated)] let arg = clap::Arg::new(#id) #implicit_methods; @@ -472,12 +350,6 @@ pub fn gen_augment( }} } -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, parent_attribute: &Attrs) -> TokenStream { let fields = fields.iter().map(|field| { let attrs = Attrs::from_field( @@ -628,72 +500,13 @@ fn gen_parsers( 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::), - quote_spanned!(span=> get_many::), - 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::), - quote_spanned!(span=> get_many::), - 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 get_one = quote_spanned!(span=> remove_one::<#convert_type>); + let get_many = quote_spanned!(span=> remove_many::<#convert_type>); + let deref = quote!(|s| s); + let parse = quote_spanned!(span=> |s| ::std::result::Result::Ok::<_, clap::Error>(s)); // Give this identifier the same hygiene // as the `arg_matches` parameter definition. This @@ -742,24 +555,6 @@ fn gen_parsers( } } - Ty::Other if occurrences => quote_spanned! { ty.span()=> - #parse( - #arg_matches.#get_one(#id) - ) - }, - - 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::Other => { quote_spanned! { ty.span()=> #arg_matches.#get_one(#id) diff --git a/clap_derive/src/parse.rs b/clap_derive/src/parse.rs index 253736cfdd7..9860f4a1fec 100644 --- a/clap_derive/src/parse.rs +++ b/clap_derive/src/parse.rs @@ -1,12 +1,11 @@ use std::iter::FromIterator; use proc_macro_error::{abort, ResultExt}; -use quote::ToTokens; use syn::{ self, parenthesized, - parse::{Parse, ParseBuffer, ParseStream}, + parse::{Parse, ParseStream}, punctuated::Punctuated, - Attribute, Expr, ExprLit, Ident, Lit, LitBool, LitStr, Token, + Attribute, Expr, ExprLit, Ident, Lit, LitStr, Token, }; pub fn parse_clap_attributes(all_attrs: &[Attribute]) -> Vec { @@ -44,9 +43,6 @@ pub enum ClapAttr { RenameAll(Ident, LitStr), NameLitStr(Ident, LitStr), - // parse(parser_kind [= parser_func]) - Parse(Ident, ParserSpec), - // ident [= arbitrary_expr] Skip(Ident, Option), @@ -140,45 +136,8 @@ impl Parse for ClapAttr { let nested; parenthesized!(nested in input); - match name_str.as_ref() { - "parse" => { - let parser_specs: Punctuated = - 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::() { - 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))) - } - } + 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() { @@ -214,71 +173,3 @@ impl Parse for ClapAttr { } } } - -#[derive(Clone)] -pub struct ParserSpec { - pub kind: Ident, - pub eq_token: Option, - pub parse_func: Option, -} - -impl Parse for ParserSpec { - fn parse(input: ParseStream<'_>) -> syn::Result { - 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), syn::Error> { - let name = ts.parse()?; - let _eq: Token![=] = ts.parse()?; - let val: LitStr = ts.parse()?; - let exprs = val.parse_with(Punctuated::::parse_terminated)?; - Ok((name, exprs)) - }; - - fn to_string(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::>() - .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/src/_derive/mod.rs b/src/_derive/mod.rs index 458d94083c6..20d978ab6d3 100644 --- a/src/_derive/mod.rs +++ b/src/_derive/mod.rs @@ -216,14 +216,6 @@ //! [`Subcommand`][crate::Subcommand]) //! - When `Option`, the subcommand becomes optional //! - `from_global`: Read a [`Arg::global`][crate::Arg::global] argument (raw attribute), regardless of what subcommand you are in -//! - `parse( [= ])`: `Arg::validator` and `ArgMatches::values_of_t` -//! - **Deprecated:** -//! - Use `value_parser(...)` for `from_str`, `try_from_str`, `from_os_str`, and `try_from_os_str` -//! - Use `action(ArgAction::Count` for `from_occurrences` -//! - Use `action(ArgAction::SetTrue` for `from_flag` -//! - Default: `try_from_str` -//! - Warning: for `Path` / `OsString`, be sure to use `try_from_os_str` -//! - See [Arg Types](#arg-types) for more details //! - `value_enum`: Parse the value using the [`ValueEnum`][crate::ValueEnum] //! - `skip [= ]`: Ignore this field, filling in with `` //! - Without ``: fills the field with `Default::default()` @@ -260,7 +252,7 @@ //! //! | Type | Effect | Implies | //! |---------------------|--------------------------------------|------------------------------------------------------------------| -//! | `bool` | flag | `#[clap(parse(from_flag))]` | +//! | `bool` | flag | `.action(ArgAction::SetTrue) | //! | `Option` | optional argument | `.takes_value(true).required(false)` | //! | `Option>` | optional value for optional argument | `.takes_value(true).required(false).min_values(0).max_values(1)` | //! | `T` | required argument | `.takes_value(true).required(!has_default)` | @@ -273,35 +265,6 @@ //! - `Option>` will be `None` instead of `vec![]` if no arguments are provided. //! - This gives the user some flexibility in designing their argument, like with `min_values(0)` //! -//! You can then support your custom type with `#[clap(parse( [= ]))]`: -//! -//! | `` | Signature | Default `` | -//! |--------------------------|---------------------------------------|---------------------------------| -//! | `from_str` | `fn(&str) -> T` | `::std::convert::From::from` | -//! | `try_from_str` (default) | `fn(&str) -> Result` | `::std::str::FromStr::from_str` | -//! | `from_os_str` | `fn(&OsStr) -> T` | `::std::convert::From::from` | -//! | `try_from_os_str` | `fn(&OsStr) -> Result` | (no default function) | -//! | `from_occurrences` | `fn(u64) -> T` | `value as T` | -//! | `from_flag` | `fn(bool) -> T` | `::std::convert::From::from` | -//! -//! Notes: -//! - `from_os_str`: -//! - Implies `arg.takes_value(true).allow_invalid_utf8(true)` -//! - `try_from_os_str`: -//! - Implies `arg.takes_value(true).allow_invalid_utf8(true)` -//! - `from_occurrences`: -//! - Implies `arg.takes_value(false).multiple_occurrences(true)` -//! - Reads from `clap::ArgMatches::occurrences_of` rather than a `get_one` function -//! - Note: operations on values, like `default_value`, are unlikely to do what you want -//! - `from_flag` -//! - Implies `arg.takes_value(false)` -//! - Reads from `clap::ArgMatches::is_present` rather than a `get_one` function -//! - Note: operations on values, like `default_value`, are unlikely to do what you want -//! -//! **Warning:** -//! - To support non-UTF8 paths, you should use `#[clap(value_parser)]` otherwise -//! `clap` will parse it as a `String` which will fail on some paths. -//! //! ## Doc Comments //! //! In clap, help messages for the whole binary can be specified diff --git a/src/derive.rs b/src/derive.rs index ddaa5e67990..c41033e5b20 100644 --- a/src/derive.rs +++ b/src/derive.rs @@ -276,7 +276,7 @@ pub trait FromArgMatches: Sized { /// /// #[derive(clap::Args)] /// struct LogArgs { -/// #[clap(long, short = 'v', parse(from_occurrences))] +/// #[clap(long, short = 'v', action = clap::ArgAction::Count)] /// verbose: i8, /// } /// ``` diff --git a/tests/derive/custom_string_parsers.rs b/tests/derive/custom_string_parsers.rs index 4c6f3d0f134..6f5d54354ea 100644 --- a/tests/derive/custom_string_parsers.rs +++ b/tests/derive/custom_string_parsers.rs @@ -125,15 +125,8 @@ fn update_every_custom_parser() { assert_eq!(NoOpOpt { b: "B" }, opt); } -// Note: can't use `Vec` directly, as clap would instead look for -// conversion function from `&str` to `u8`. -type Bytes = Vec; - #[derive(Parser, PartialEq, Debug)] struct DefaultedOpt { - #[clap(short, parse(from_str))] - bytes: Bytes, - #[clap(short)] integer: u64, @@ -145,61 +138,9 @@ struct DefaultedOpt { fn test_parser_with_default_value() { assert_eq!( DefaultedOpt { - bytes: b"E\xc2\xb2=p\xc2\xb2c\xc2\xb2+m\xc2\xb2c\xe2\x81\xb4".to_vec(), integer: 9000, path: PathBuf::from("src/lib.rs"), }, - DefaultedOpt::try_parse_from(&[ - "test", - "-b", - "E²=p²c²+m²c⁴", - "-i", - "9000", - "-p", - "src/lib.rs", - ]) - .unwrap() - ); -} - -#[derive(PartialEq, Debug)] -struct Foo(u8); - -fn foo(value: u64) -> Foo { - Foo(value as u8) -} - -#[derive(Parser, PartialEq, Debug)] -struct Occurrences { - #[clap(short, long, parse(from_occurrences))] - signed: i32, - - #[clap(short, parse(from_occurrences))] - little_signed: i8, - - #[clap(short, parse(from_occurrences))] - unsigned: usize, - - #[clap(short = 'r', parse(from_occurrences))] - little_unsigned: u8, - - #[clap(short, long, parse(from_occurrences = foo))] - custom: Foo, -} - -#[test] -fn test_parser_occurrences() { - assert_eq!( - Occurrences { - signed: 3, - little_signed: 1, - unsigned: 0, - little_unsigned: 4, - custom: Foo(5), - }, - Occurrences::try_parse_from(&[ - "test", "-s", "--signed", "--signed", "-l", "-rrrr", "-cccc", "--custom", - ]) - .unwrap() + DefaultedOpt::try_parse_from(&["test", "-i", "9000", "-p", "src/lib.rs",]).unwrap() ); } diff --git a/tests/derive/flags.rs b/tests/derive/flags.rs index 02bcc45b478..956442d716b 100644 --- a/tests/derive/flags.rs +++ b/tests/derive/flags.rs @@ -78,37 +78,6 @@ fn count() { assert!(Opt::try_parse_from(&["test", "-a", "foo"]).is_err()); } -#[test] -fn non_bool_type_flag() { - fn parse_from_flag(b: bool) -> std::sync::atomic::AtomicBool { - std::sync::atomic::AtomicBool::new(b) - } - - #[derive(Parser, Debug)] - struct Opt { - #[clap(short, long, parse(from_flag = parse_from_flag))] - alice: std::sync::atomic::AtomicBool, - #[clap(short, long, parse(from_flag))] - bob: std::sync::atomic::AtomicBool, - } - - let falsey = Opt::try_parse_from(&["test"]).unwrap(); - assert!(!falsey.alice.load(std::sync::atomic::Ordering::Relaxed)); - assert!(!falsey.bob.load(std::sync::atomic::Ordering::Relaxed)); - - let alice = Opt::try_parse_from(&["test", "-a"]).unwrap(); - assert!(alice.alice.load(std::sync::atomic::Ordering::Relaxed)); - assert!(!alice.bob.load(std::sync::atomic::Ordering::Relaxed)); - - let bob = Opt::try_parse_from(&["test", "-b"]).unwrap(); - assert!(!bob.alice.load(std::sync::atomic::Ordering::Relaxed)); - assert!(bob.bob.load(std::sync::atomic::Ordering::Relaxed)); - - let both = Opt::try_parse_from(&["test", "-b", "-a"]).unwrap(); - assert!(both.alice.load(std::sync::atomic::Ordering::Relaxed)); - assert!(both.bob.load(std::sync::atomic::Ordering::Relaxed)); -} - #[test] fn mixed_type_flags() { #[derive(Parser, PartialEq, Debug)] diff --git a/tests/derive_ui/flatten_and_parse.rs b/tests/derive_ui/flatten_and_parse.rs deleted file mode 100644 index ea52dd459af..00000000000 --- a/tests/derive_ui/flatten_and_parse.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2018 Guillaume Pinot (@TeXitoi) -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use clap::Parser; - -#[derive(Parser, Debug)] -struct DaemonOpts { - #[clap(short, value_parser)] - user: String, - #[clap(short, value_parser)] - group: String, -} - -#[derive(Parser, Debug)] -#[clap(name = "basic")] -struct Opt { - #[clap(flatten, parse(from_occurrences))] - opts: DaemonOpts, -} - -fn main() { - let opt = Opt::parse(); - println!("{:?}", opt); -} diff --git a/tests/derive_ui/flatten_and_parse.stderr b/tests/derive_ui/flatten_and_parse.stderr deleted file mode 100644 index 93aa67519f8..00000000000 --- a/tests/derive_ui/flatten_and_parse.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: parse attribute is not allowed for flattened entry - --> $DIR/flatten_and_parse.rs:22:21 - | -22 | #[clap(flatten, parse(from_occurrences))] - | ^^^^^ diff --git a/tests/derive_ui/parse_empty_try_from_os.rs b/tests/derive_ui/parse_empty_try_from_os.rs deleted file mode 100644 index 39629d80a50..00000000000 --- a/tests/derive_ui/parse_empty_try_from_os.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018 Guillaume Pinot (@TeXitoi) -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use clap::Parser; - -#[derive(Parser, Debug)] -#[clap(name = "basic")] -struct Opt { - #[clap(parse(try_from_os_str))] - s: String, -} - -fn main() { - let opt = Opt::parse(); - println!("{:?}", opt); -} diff --git a/tests/derive_ui/parse_empty_try_from_os.stderr b/tests/derive_ui/parse_empty_try_from_os.stderr deleted file mode 100644 index 9109580953e..00000000000 --- a/tests/derive_ui/parse_empty_try_from_os.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: you must set parser for `try_from_os_str` explicitly - --> $DIR/parse_empty_try_from_os.rs:14:18 - | -14 | #[clap(parse(try_from_os_str))] - | ^^^^^^^^^^^^^^^ diff --git a/tests/derive_ui/parse_function_is_not_path.rs b/tests/derive_ui/parse_function_is_not_path.rs deleted file mode 100644 index 6aa8eea7480..00000000000 --- a/tests/derive_ui/parse_function_is_not_path.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018 Guillaume Pinot (@TeXitoi) -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use clap::Parser; - -#[derive(Parser, Debug)] -#[clap(name = "basic")] -struct Opt { - #[clap(parse(from_str = "2"))] - s: String, -} - -fn main() { - let opt = Opt::parse(); - println!("{:?}", opt); -} diff --git a/tests/derive_ui/parse_function_is_not_path.stderr b/tests/derive_ui/parse_function_is_not_path.stderr deleted file mode 100644 index 230bdb33c94..00000000000 --- a/tests/derive_ui/parse_function_is_not_path.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: `parse` argument must be a function path - --> $DIR/parse_function_is_not_path.rs:14:29 - | -14 | #[clap(parse(from_str = "2"))] - | ^^^ diff --git a/tests/derive_ui/parse_literal_spec.rs b/tests/derive_ui/parse_literal_spec.rs deleted file mode 100644 index 1d35cb59d7e..00000000000 --- a/tests/derive_ui/parse_literal_spec.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018 Guillaume Pinot (@TeXitoi) -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use clap::Parser; - -#[derive(Parser, Debug)] -#[clap(name = "basic")] -struct Opt { - #[clap(parse("from_str"))] - s: String, -} - -fn main() { - let opt = Opt::parse(); - println!("{:?}", opt); -} diff --git a/tests/derive_ui/parse_literal_spec.stderr b/tests/derive_ui/parse_literal_spec.stderr deleted file mode 100644 index a5bf187b0c1..00000000000 --- a/tests/derive_ui/parse_literal_spec.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: parser specification must start with identifier - --> $DIR/parse_literal_spec.rs:14:18 - | -14 | #[clap(parse("from_str"))] - | ^^^^^^^^^^ diff --git a/tests/derive_ui/parse_not_zero_args.rs b/tests/derive_ui/parse_not_zero_args.rs deleted file mode 100644 index 997892ac1b3..00000000000 --- a/tests/derive_ui/parse_not_zero_args.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018 Guillaume Pinot (@TeXitoi) -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use clap::Parser; - -#[derive(Parser, Debug)] -#[clap(name = "basic")] -struct Opt { - #[clap(parse(from_str, from_str))] - s: String, -} - -fn main() { - let opt = Opt::parse(); - println!("{:?}", opt); -} diff --git a/tests/derive_ui/parse_not_zero_args.stderr b/tests/derive_ui/parse_not_zero_args.stderr deleted file mode 100644 index 534188dbcd6..00000000000 --- a/tests/derive_ui/parse_not_zero_args.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: parse must have exactly one argument - --> $DIR/parse_not_zero_args.rs:14:12 - | -14 | #[clap(parse(from_str, from_str))] - | ^^^^^ diff --git a/tests/derive_ui/parse_with_value_parser.rs b/tests/derive_ui/parse_with_value_parser.rs deleted file mode 100644 index ffbb6e848c5..00000000000 --- a/tests/derive_ui/parse_with_value_parser.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018 Guillaume Pinot (@TeXitoi) -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use clap::Parser; - -#[derive(Parser, Debug)] -#[clap(name = "basic")] -struct Opt { - #[clap(parse(from_str), value_parser = clap::value_parser!(String))] - s: String, -} - -fn main() { - let opt = Opt::parse(); - println!("{:?}", opt); -} diff --git a/tests/derive_ui/parse_with_value_parser.stderr b/tests/derive_ui/parse_with_value_parser.stderr deleted file mode 100644 index 1f3e33e2351..00000000000 --- a/tests/derive_ui/parse_with_value_parser.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: `value_parser` attribute conflicts with `parse` attribute - --> tests/derive_ui/parse_with_value_parser.rs:14:29 - | -14 | #[clap(parse(from_str), value_parser = clap::value_parser!(String))] - | ^^^^^^^^^^^^ diff --git a/tests/derive_ui/raw.rs b/tests/derive_ui/raw.rs deleted file mode 100644 index 350482339b1..00000000000 --- a/tests/derive_ui/raw.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2018 Guillaume Pinot (@TeXitoi) -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use clap::Parser; - -#[derive(Parser, Debug)] -struct Opt { - #[clap(raw(ignore_case = "true"))] - s: String, -} - -#[derive(Parser, Debug)] -struct Opt2 { - #[clap(raw(requires_if = r#""one", "two""#))] - s: String, -} -fn main() { - let opt = Opt::parse(); - println!("{:?}", opt); -} diff --git a/tests/derive_ui/raw.stderr b/tests/derive_ui/raw.stderr deleted file mode 100644 index 459bcc91ae8..00000000000 --- a/tests/derive_ui/raw.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error: `#[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: if you need to call `clap::Arg/Command::ignore_case` method you can do it like this: #[clap(ignore_case = true)] - - --> $DIR/raw.rs:13:12 - | -13 | #[clap(raw(ignore_case = "true"))] - | ^^^ - -error: `#[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: if you need to call `clap::Arg/Command::requires_if` method you can do it like this: #[clap(requires_if("one", "two"))] - - --> $DIR/raw.rs:19:12 - | -19 | #[clap(raw(requires_if = r#""one", "two""#))] - | ^^^ diff --git a/tests/derive_ui/struct_parse.rs b/tests/derive_ui/struct_parse.rs deleted file mode 100644 index f06ce48e6dd..00000000000 --- a/tests/derive_ui/struct_parse.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018 Guillaume Pinot (@TeXitoi) -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use clap::Parser; - -#[derive(Parser, Debug)] -#[clap(name = "basic", parse(from_str))] -struct Opt { - #[clap(short, value_parser)] - s: String, -} - -fn main() { - let opt = Opt::parse(); - println!("{:?}", opt); -} diff --git a/tests/derive_ui/struct_parse.stderr b/tests/derive_ui/struct_parse.stderr deleted file mode 100644 index c21df6bef55..00000000000 --- a/tests/derive_ui/struct_parse.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: `parse` attribute is only allowed on fields - --> $DIR/struct_parse.rs:12:24 - | -12 | #[clap(name = "basic", parse(from_str))] - | ^^^^^ diff --git a/tests/derive_ui/subcommand_and_parse.rs b/tests/derive_ui/subcommand_and_parse.rs deleted file mode 100644 index 430db74e1ee..00000000000 --- a/tests/derive_ui/subcommand_and_parse.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2018 Guillaume Pinot (@TeXitoi) -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use clap::Parser; - -#[derive(Parser, Debug)] -struct MakeCookie { - #[clap(short, value_parser)] - s: String, - - #[clap(subcommand, parse(from_occurrences))] - cmd: Command, -} - -#[derive(Parser, Debug)] -enum Command { - /// Pound acorns into flour for cookie dough. - Pound { - #[clap(value_parser)] - acorns: u32, - }, - - Sparkle { - #[clap(short, value_parser)] - color: String, - }, -} - -fn main() { - let opt = MakeCookie::parse(); - println!("{:?}", opt); -} diff --git a/tests/derive_ui/subcommand_and_parse.stderr b/tests/derive_ui/subcommand_and_parse.stderr deleted file mode 100644 index 86705ca07ea..00000000000 --- a/tests/derive_ui/subcommand_and_parse.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: parse attribute is not allowed for subcommand - --> $DIR/subcommand_and_parse.rs:16:24 - | -16 | #[clap(subcommand, parse(from_occurrences))] - | ^^^^^ diff --git a/tests/derive_ui/unsupported_parser.rs b/tests/derive_ui/unsupported_parser.rs deleted file mode 100644 index 624456ca21b..00000000000 --- a/tests/derive_ui/unsupported_parser.rs +++ /dev/null @@ -1,11 +0,0 @@ -use clap::Parser; - -#[derive(Parser, Clone, Debug)] -struct Opt { - #[clap(parse(not_a_valid_parser))] - value: i8, -} - -fn main() { - println!("{:?}", Opt::parse()); -} diff --git a/tests/derive_ui/unsupported_parser.stderr b/tests/derive_ui/unsupported_parser.stderr deleted file mode 100644 index 43949d68e92..00000000000 --- a/tests/derive_ui/unsupported_parser.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: unsupported parser `not_a_valid_parser` - --> tests/derive_ui/unsupported_parser.rs:5:18 - | -5 | #[clap(parse(not_a_valid_parser))] - | ^^^^^^^^^^^^^^^^^^