fix(derive): Move args to new 'get_one'/'get_many' API

This commit is contained in:
Ed Page 2022-05-13 16:04:03 -05:00
parent 72c44a32e5
commit d826ab9445
2 changed files with 84 additions and 18 deletions

View file

@ -529,25 +529,25 @@ fn gen_parsers(
let span = parser.kind.span(); let span = parser.kind.span();
let convert_type = inner_type(**ty, &field.ty); let convert_type = inner_type(**ty, &field.ty);
let id = attrs.id(); let id = attrs.id();
let (value_of, values_of, mut parse) = match *parser.kind { let (get_one, get_many, mut parse) = match *parser.kind {
FromStr => ( FromStr => (
quote_spanned!(span=> value_of), quote_spanned!(span=> get_one::<String>),
quote_spanned!(span=> values_of), quote_spanned!(span=> get_many::<String>),
quote_spanned!(func.span()=> |s| ::std::result::Result::Ok::<_, clap::Error>(#func(s))), quote_spanned!(func.span()=> |s| ::std::result::Result::Ok::<_, clap::Error>(#func(s))),
), ),
TryFromStr => ( TryFromStr => (
quote_spanned!(span=> value_of), quote_spanned!(span=> get_one::<String>),
quote_spanned!(span=> values_of), quote_spanned!(span=> get_many::<String>),
quote_spanned!(func.span()=> |s| #func(s).map_err(|err| clap::Error::raw(clap::ErrorKind::ValueValidation, format!("Invalid value for {}: {}", #id, err)))), quote_spanned!(func.span()=> |s| #func(s).map_err(|err| clap::Error::raw(clap::ErrorKind::ValueValidation, format!("Invalid value for {}: {}", #id, err)))),
), ),
FromOsStr => ( FromOsStr => (
quote_spanned!(span=> value_of_os), quote_spanned!(span=> get_one::<::std::ffi::OsString>),
quote_spanned!(span=> values_of_os), quote_spanned!(span=> get_many::<::std::ffi::OsString>),
quote_spanned!(func.span()=> |s| ::std::result::Result::Ok::<_, clap::Error>(#func(s))), quote_spanned!(func.span()=> |s| ::std::result::Result::Ok::<_, clap::Error>(#func(s))),
), ),
TryFromOsStr => ( TryFromOsStr => (
quote_spanned!(span=> value_of_os), quote_spanned!(span=> get_one::<::std::ffi::OsString>),
quote_spanned!(span=> values_of_os), quote_spanned!(span=> get_many::<::std::ffi::OsString>),
quote_spanned!(func.span()=> |s| #func(s).map_err(|err| clap::Error::raw(clap::ErrorKind::ValueValidation, format!("Invalid value for {}: {}", #id, err)))), quote_spanned!(func.span()=> |s| #func(s).map_err(|err| clap::Error::raw(clap::ErrorKind::ValueValidation, format!("Invalid value for {}: {}", #id, err)))),
), ),
FromOccurrences => ( FromOccurrences => (
@ -587,7 +587,9 @@ fn gen_parsers(
Ty::Option => { Ty::Option => {
quote_spanned! { ty.span()=> quote_spanned! { ty.span()=>
#arg_matches.#value_of(#id) #arg_matches.#get_one(#id)
.expect("unexpected type")
.map(|s| ::std::ops::Deref::deref(s))
.map(#parse) .map(#parse)
.transpose()? .transpose()?
} }
@ -595,7 +597,12 @@ fn gen_parsers(
Ty::OptionOption => quote_spanned! { ty.span()=> Ty::OptionOption => quote_spanned! { ty.span()=>
if #arg_matches.is_present(#id) { if #arg_matches.is_present(#id) {
Some(#arg_matches.#value_of(#id).map(#parse).transpose()?) Some(
#arg_matches.#get_one(#id)
.expect("unexpected type")
.map(|s| ::std::ops::Deref::deref(s))
.map(#parse).transpose()?
)
} else { } else {
None None
} }
@ -603,8 +610,9 @@ fn gen_parsers(
Ty::OptionVec => quote_spanned! { ty.span()=> Ty::OptionVec => quote_spanned! { ty.span()=>
if #arg_matches.is_present(#id) { if #arg_matches.is_present(#id) {
Some(#arg_matches.#values_of(#id) Some(#arg_matches.#get_many(#id)
.map(|v| v.map::<::std::result::Result<#convert_type, clap::Error>, _>(#parse).collect::<::std::result::Result<Vec<_>, clap::Error>>()) .expect("unexpected type")
.map(|v| v.map(|s| ::std::ops::Deref::deref(s)).map::<::std::result::Result<#convert_type, clap::Error>, _>(#parse).collect::<::std::result::Result<Vec<_>, clap::Error>>())
.transpose()? .transpose()?
.unwrap_or_else(Vec::new)) .unwrap_or_else(Vec::new))
} else { } else {
@ -614,24 +622,31 @@ fn gen_parsers(
Ty::Vec => { Ty::Vec => {
quote_spanned! { ty.span()=> quote_spanned! { ty.span()=>
#arg_matches.#values_of(#id) #arg_matches.#get_many(#id)
.map(|v| v.map::<::std::result::Result<#convert_type, clap::Error>, _>(#parse).collect::<::std::result::Result<Vec<_>, clap::Error>>()) .expect("unexpected type")
.map(|v| v.map(|s| ::std::ops::Deref::deref(s)).map::<::std::result::Result<#convert_type, clap::Error>, _>(#parse).collect::<::std::result::Result<Vec<_>, clap::Error>>())
.transpose()? .transpose()?
.unwrap_or_else(Vec::new) .unwrap_or_else(Vec::new)
} }
} }
Ty::Other if occurrences => quote_spanned! { ty.span()=> Ty::Other if occurrences => quote_spanned! { ty.span()=>
#parse(#arg_matches.#value_of(#id)) #parse(
#arg_matches.#get_one(#id)
)
}, },
Ty::Other if flag => quote_spanned! { ty.span()=> Ty::Other if flag => quote_spanned! { ty.span()=>
#parse(#arg_matches.is_present(#id)) #parse(
#arg_matches.is_present(#id)
)
}, },
Ty::Other => { Ty::Other => {
quote_spanned! { ty.span()=> quote_spanned! { ty.span()=>
#arg_matches.#value_of(#id) #arg_matches.#get_one(#id)
.expect("unexpected type")
.map(|s| ::std::ops::Deref::deref(s))
.ok_or_else(|| clap::Error::raw(clap::ErrorKind::MissingRequiredArgument, format!("The following required argument was not provided: {}", #id))) .ok_or_else(|| clap::Error::raw(clap::ErrorKind::MissingRequiredArgument, format!("The following required argument was not provided: {}", #id)))
.and_then(#parse)? .and_then(#parse)?
} }

View file

@ -95,6 +95,57 @@ fn option_from_str() {
); );
} }
#[test]
fn vec_from_str() {
#[derive(Debug, PartialEq)]
struct A;
impl<'a> From<&'a str> for A {
fn from(_: &str) -> A {
A
}
}
#[derive(Debug, Parser, PartialEq)]
struct Opt {
#[clap(parse(from_str))]
a: Vec<A>,
}
assert_eq!(
Opt { a: Vec::new() },
Opt::try_parse_from(&["test"]).unwrap()
);
assert_eq!(
Opt { a: vec![A] },
Opt::try_parse_from(&["test", "foo"]).unwrap()
);
}
#[test]
fn option_vec_from_str() {
#[derive(Debug, PartialEq)]
struct A;
impl<'a> From<&'a str> for A {
fn from(_: &str) -> A {
A
}
}
#[derive(Debug, Parser, PartialEq)]
struct Opt {
#[clap(short, parse(from_str))]
a: Option<Vec<A>>,
}
assert_eq!(Opt { a: None }, Opt::try_parse_from(&["test"]).unwrap());
assert_eq!(
Opt { a: Some(vec![A]) },
Opt::try_parse_from(&["test", "-a", "foo"]).unwrap()
);
}
#[test] #[test]
fn option_type_is_optional() { fn option_type_is_optional() {
#[derive(Parser, PartialEq, Debug)] #[derive(Parser, PartialEq, Debug)]