mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 14:22:34 +00:00
feat(clap_derive): Add default_values_t
and default_values_os_t
This commit is contained in:
parent
732a21b1bf
commit
04e0ed7474
7 changed files with 406 additions and 0 deletions
|
@ -516,6 +516,81 @@ impl Attrs {
|
|||
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
|
||||
|
@ -556,6 +631,84 @@ impl Attrs {
|
|||
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)));
|
||||
}
|
||||
|
|
|
@ -51,7 +51,9 @@ pub enum ClapAttr {
|
|||
// 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),
|
||||
|
@ -120,7 +122,9 @@ impl Parse for ClapAttr {
|
|||
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)),
|
||||
|
|
|
@ -223,9 +223,15 @@
|
|||
//! - `default_value_t [= <expr>]`: [`Arg::default_value`][crate::Arg::default_value] and [`Arg::required(false)`][crate::Arg::required]
|
||||
//! - Requires `std::fmt::Display` or `#[clap(value_enum)]`
|
||||
//! - Without `<expr>`, relies on `Default::default()`
|
||||
//! - `default_values_t = <expr>`: [`Arg::default_values`][crate::Arg::default_values] and [`Arg::required(false)`][crate::Arg::required]
|
||||
//! - Requires field arg to be of type `Vec<T>` and `T` to implement `std::fmt::Display` or `#[clap(value_enum)]`
|
||||
//! - `<expr>` must implement `IntoIterator<T>`
|
||||
//! - `default_value_os_t [= <expr>]`: [`Arg::default_value_os`][crate::Arg::default_value_os] and [`Arg::required(false)`][crate::Arg::required]
|
||||
//! - Requires `std::convert::Into<OsString>` or `#[clap(value_enum)]`
|
||||
//! - Without `<expr>`, relies on `Default::default()`
|
||||
//! - `default_values_os_t = <expr>`: [`Arg::default_values_os`][crate::Arg::default_values_os] and [`Arg::required(false)`][crate::Arg::required]
|
||||
//! - Requires field arg to be of type `Vec<T>` and `T` to implement `std::convert::Into<OsString>` or `#[clap(value_enum)]`
|
||||
//! - `<expr>` must implement `IntoIterator<T>`
|
||||
//!
|
||||
//! ### Value Enum Attributes
|
||||
//!
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use clap::{CommandFactory, Parser};
|
||||
|
||||
use crate::utils;
|
||||
|
@ -44,6 +46,124 @@ fn auto_default_value_t() {
|
|||
assert!(help.contains("[default: 0]"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_values_t() {
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(default_values_t = vec![1, 2, 3])]
|
||||
arg1: Vec<i32>,
|
||||
|
||||
#[clap(long, default_values_t = &[4, 5, 6])]
|
||||
arg2: Vec<i32>,
|
||||
|
||||
#[clap(long, default_values_t = [7, 8, 9])]
|
||||
arg3: Vec<i32>,
|
||||
|
||||
#[clap(long, default_values_t = 10..=12)]
|
||||
arg4: Vec<i32>,
|
||||
|
||||
#[clap(long, default_values_t = vec!["hello".to_string(), "world".to_string()])]
|
||||
arg5: Vec<String>,
|
||||
|
||||
#[clap(long, default_values_t = &vec!["foo".to_string(), "bar".to_string()])]
|
||||
arg6: Vec<String>,
|
||||
}
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg1: vec![1, 2, 3],
|
||||
arg2: vec![4, 5, 6],
|
||||
arg3: vec![7, 8, 9],
|
||||
arg4: vec![10, 11, 12],
|
||||
arg5: vec!["hello".to_string(), "world".to_string()],
|
||||
arg6: vec!["foo".to_string(), "bar".to_string()],
|
||||
},
|
||||
Opt::try_parse_from(&["test"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg1: vec![1],
|
||||
arg2: vec![4, 5, 6],
|
||||
arg3: vec![7, 8, 9],
|
||||
arg4: vec![10, 11, 12],
|
||||
arg5: vec!["hello".to_string(), "world".to_string()],
|
||||
arg6: vec!["foo".to_string(), "bar".to_string()],
|
||||
},
|
||||
Opt::try_parse_from(&["test", "1"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg1: vec![1, 2, 3],
|
||||
arg2: vec![4, 5, 6],
|
||||
arg3: vec![7, 8, 9],
|
||||
arg4: vec![42, 15],
|
||||
arg5: vec!["baz".to_string()],
|
||||
arg6: vec!["foo".to_string(), "bar".to_string()],
|
||||
},
|
||||
Opt::try_parse_from(&["test", "--arg4", "42", "--arg4", "15", "--arg5", "baz"]).unwrap()
|
||||
);
|
||||
|
||||
let help = utils::get_long_help::<Opt>();
|
||||
assert!(help.contains("[default: 1 2 3]"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_value_os_t() {
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(default_value_os_t = PathBuf::from("abc.def"))]
|
||||
arg: PathBuf,
|
||||
}
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: PathBuf::from("abc.def")
|
||||
},
|
||||
Opt::try_parse_from(&["test"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: PathBuf::from("ghi")
|
||||
},
|
||||
Opt::try_parse_from(&["test", "ghi"]).unwrap()
|
||||
);
|
||||
|
||||
let help = utils::get_long_help::<Opt>();
|
||||
assert!(help.contains("[default: abc.def]"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_values_os_t() {
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(
|
||||
default_values_os_t = vec![PathBuf::from("abc.def"), PathBuf::from("123.foo")]
|
||||
)]
|
||||
arg1: Vec<PathBuf>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
default_values_os_t = &[PathBuf::from("bar.baz")]
|
||||
)]
|
||||
arg2: Vec<PathBuf>,
|
||||
}
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg1: vec![PathBuf::from("abc.def"), PathBuf::from("123.foo")],
|
||||
arg2: vec![PathBuf::from("bar.baz")]
|
||||
},
|
||||
Opt::try_parse_from(&["test"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg1: vec![PathBuf::from("ghi")],
|
||||
arg2: vec![PathBuf::from("baz.bar"), PathBuf::from("foo.bar")]
|
||||
},
|
||||
Opt::try_parse_from(&["test", "ghi", "--arg2", "baz.bar", "--arg2", "foo.bar"]).unwrap()
|
||||
);
|
||||
|
||||
let help = utils::get_long_help::<Opt>();
|
||||
assert!(help.contains("[default: abc.def 123.foo]"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detect_os_variant() {
|
||||
#![allow(unused_parens)] // needed for `as_ref` call
|
||||
|
|
|
@ -78,6 +78,108 @@ fn default_value() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vec_for_default_values_t() {
|
||||
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
|
||||
enum ArgChoice {
|
||||
Foo,
|
||||
Bar,
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(value_enum, default_values_t = vec![ArgChoice::Foo, ArgChoice::Bar])]
|
||||
arg1: Vec<ArgChoice>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
value_enum,
|
||||
default_values_t = clap::ValueEnum::value_variants()
|
||||
)]
|
||||
arg2: Vec<ArgChoice>,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg1: vec![ArgChoice::Foo],
|
||||
arg2: vec![ArgChoice::Foo, ArgChoice::Bar]
|
||||
},
|
||||
Opt::try_parse_from(&["", "foo"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg1: vec![ArgChoice::Bar],
|
||||
arg2: vec![ArgChoice::Foo, ArgChoice::Bar]
|
||||
},
|
||||
Opt::try_parse_from(&["", "bar"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg1: vec![ArgChoice::Foo, ArgChoice::Bar],
|
||||
arg2: vec![ArgChoice::Foo, ArgChoice::Bar]
|
||||
},
|
||||
Opt::try_parse_from(&[""]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg1: vec![ArgChoice::Foo, ArgChoice::Bar],
|
||||
arg2: vec![ArgChoice::Foo]
|
||||
},
|
||||
Opt::try_parse_from(&["", "--arg2", "foo"]).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vec_for_default_values_os_t() {
|
||||
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
|
||||
enum ArgChoice {
|
||||
Foo,
|
||||
Bar,
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(value_enum, default_values_os_t = vec![ArgChoice::Foo, ArgChoice::Bar])]
|
||||
arg: Vec<ArgChoice>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
value_enum,
|
||||
default_values_os_t = clap::ValueEnum::value_variants()
|
||||
)]
|
||||
arg2: Vec<ArgChoice>,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: vec![ArgChoice::Foo],
|
||||
arg2: vec![ArgChoice::Foo, ArgChoice::Bar]
|
||||
},
|
||||
Opt::try_parse_from(&["", "foo"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: vec![ArgChoice::Bar],
|
||||
arg2: vec![ArgChoice::Foo, ArgChoice::Bar]
|
||||
},
|
||||
Opt::try_parse_from(&["", "bar"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: vec![ArgChoice::Foo, ArgChoice::Bar],
|
||||
arg2: vec![ArgChoice::Foo, ArgChoice::Bar]
|
||||
},
|
||||
Opt::try_parse_from(&[""]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: vec![ArgChoice::Foo, ArgChoice::Bar],
|
||||
arg2: vec![ArgChoice::Foo]
|
||||
},
|
||||
Opt::try_parse_from(&["", "--arg2", "foo"]).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_word_is_renamed_kebab() {
|
||||
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
|
||||
|
|
13
tests/derive_ui/default_values_t_invalid.rs
Normal file
13
tests/derive_ui/default_values_t_invalid.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use clap::Parser;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
#[clap(default_values_t = [1, 2, 3])]
|
||||
value: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
8
tests/derive_ui/default_values_t_invalid.stderr
Normal file
8
tests/derive_ui/default_values_t_invalid.stderr
Normal file
|
@ -0,0 +1,8 @@
|
|||
error: #[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
|
||||
|
||||
--> tests/derive_ui/default_values_t_invalid.rs:6:12
|
||||
|
|
||||
6 | #[clap(default_values_t = [1, 2, 3])]
|
||||
| ^^^^^^^^^^^^^^^^
|
Loading…
Reference in a new issue