Skip to content

Commit

Permalink
fix(derive)!: Remove deprecated parse attribute
Browse files Browse the repository at this point in the history
epage committed Jul 22, 2022
1 parent 7307f22 commit 7068586
Showing 25 changed files with 29 additions and 852 deletions.
203 changes: 2 additions & 201 deletions clap_derive/src/attrs.rs
Original file line number Diff line number Diff line change
@@ -44,7 +44,6 @@ pub struct Attrs {
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>,
@@ -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<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()
}
@@ -1031,114 +940,6 @@ fn process_author_str(author: &str) -> String {
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 {
249 changes: 22 additions & 227 deletions clap_derive/src/derives/args.rs
Original file line number Diff line number Diff line change
@@ -13,17 +13,17 @@
// 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};
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,90 +264,48 @@ 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
}
}
}

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<Field, Comma>, 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::<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 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)
48 changes: 2 additions & 46 deletions clap_derive/src/parse.rs
Original file line number Diff line number Diff line change
@@ -43,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<Expr>),

@@ -139,24 +136,8 @@ impl Parse for ClapAttr {
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")
}
}

_ => {
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() {
@@ -192,28 +173,3 @@ impl Parse for ClapAttr {
}
}
}

#[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,
})
}
}
39 changes: 1 addition & 38 deletions src/_derive/mod.rs
Original file line number Diff line number Diff line change
@@ -216,14 +216,6 @@
//! [`Subcommand`][crate::Subcommand])
//! - When `Option<T>`, the subcommand becomes optional
//! - `from_global`: Read a [`Arg::global`][crate::Arg::global] argument (raw attribute), regardless of what subcommand you are in
//! - `parse(<kind> [= <function>])`: `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 [= <expr>]`: Ignore this field, filling in with `<expr>`
//! - Without `<expr>`: 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<T>` | optional argument | `.takes_value(true).required(false)` |
//! | `Option<Option<T>>` | 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<Vec<T>>` 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(<kind> [= <function>]))]`:
//!
//! | `<kind>` | Signature | Default `<function>` |
//! |--------------------------|---------------------------------------|---------------------------------|
//! | `from_str` | `fn(&str) -> T` | `::std::convert::From::from` |
//! | `try_from_str` (default) | `fn(&str) -> Result<T, E>` | `::std::str::FromStr::from_str` |
//! | `from_os_str` | `fn(&OsStr) -> T` | `::std::convert::From::from` |
//! | `try_from_os_str` | `fn(&OsStr) -> Result<T, E>` | (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
2 changes: 1 addition & 1 deletion src/derive.rs
Original file line number Diff line number Diff line change
@@ -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,
/// }
/// ```
61 changes: 1 addition & 60 deletions tests/derive/custom_string_parsers.rs
Original file line number Diff line number Diff line change
@@ -125,15 +125,8 @@ fn update_every_custom_parser() {
assert_eq!(NoOpOpt { b: "B" }, opt);
}

// Note: can't use `Vec<u8>` directly, as clap would instead look for
// conversion function from `&str` to `u8`.
type Bytes = Vec<u8>;

#[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()
);
}
31 changes: 0 additions & 31 deletions tests/derive/flags.rs
Original file line number Diff line number Diff line change
@@ -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)]
29 changes: 0 additions & 29 deletions tests/derive_ui/flatten_and_parse.rs

This file was deleted.

5 changes: 0 additions & 5 deletions tests/derive_ui/flatten_and_parse.stderr

This file was deleted.

21 changes: 0 additions & 21 deletions tests/derive_ui/parse_empty_try_from_os.rs

This file was deleted.

5 changes: 0 additions & 5 deletions tests/derive_ui/parse_empty_try_from_os.stderr

This file was deleted.

21 changes: 0 additions & 21 deletions tests/derive_ui/parse_function_is_not_path.rs

This file was deleted.

5 changes: 0 additions & 5 deletions tests/derive_ui/parse_function_is_not_path.stderr

This file was deleted.

21 changes: 0 additions & 21 deletions tests/derive_ui/parse_literal_spec.rs

This file was deleted.

5 changes: 0 additions & 5 deletions tests/derive_ui/parse_literal_spec.stderr

This file was deleted.

21 changes: 0 additions & 21 deletions tests/derive_ui/parse_not_zero_args.rs

This file was deleted.

5 changes: 0 additions & 5 deletions tests/derive_ui/parse_not_zero_args.stderr

This file was deleted.

21 changes: 0 additions & 21 deletions tests/derive_ui/parse_with_value_parser.rs

This file was deleted.

5 changes: 0 additions & 5 deletions tests/derive_ui/parse_with_value_parser.stderr

This file was deleted.

21 changes: 0 additions & 21 deletions tests/derive_ui/struct_parse.rs

This file was deleted.

5 changes: 0 additions & 5 deletions tests/derive_ui/struct_parse.stderr

This file was deleted.

37 changes: 0 additions & 37 deletions tests/derive_ui/subcommand_and_parse.rs

This file was deleted.

5 changes: 0 additions & 5 deletions tests/derive_ui/subcommand_and_parse.stderr

This file was deleted.

11 changes: 0 additions & 11 deletions tests/derive_ui/unsupported_parser.rs

This file was deleted.

5 changes: 0 additions & 5 deletions tests/derive_ui/unsupported_parser.stderr

This file was deleted.

0 comments on commit 7068586

Please sign in to comment.