mirror of
https://github.com/clap-rs/clap
synced 2024-12-12 13:52:34 +00:00
feat(derive): Expose control over Actions
This is the derive support for #3774 (see also #3775, #3777) This combined with `value_parser` replaces `parser`. The main frustration with this is that `ArgAction::Count` (the replacement for `parse(from_occurrences)` must be a `u64`. We could come up with a magic attribute that is meant to be the value parser's parsed type. We could then use `TryFrom` to convert the parsed type to the user's type to allow more. That is an exercise for the future. Alternatively, we have #3792. Prep for this included - #3782 - #3783 - #3786 - #3789 - #3793
This commit is contained in:
parent
4489f09f10
commit
647896d929
25 changed files with 293 additions and 111 deletions
|
@ -43,6 +43,7 @@ pub struct Attrs {
|
|||
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>,
|
||||
|
@ -70,6 +71,12 @@ impl Attrs {
|
|||
"`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");
|
||||
}
|
||||
|
@ -110,6 +117,12 @@ impl Attrs {
|
|||
"`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(),
|
||||
|
@ -136,6 +149,12 @@ impl Attrs {
|
|||
"`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(),
|
||||
|
@ -210,6 +229,12 @@ impl Attrs {
|
|||
"`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");
|
||||
}
|
||||
|
@ -250,6 +275,12 @@ impl Attrs {
|
|||
"`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(),
|
||||
|
@ -280,6 +311,12 @@ impl Attrs {
|
|||
"`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(),
|
||||
|
@ -333,6 +370,12 @@ impl Attrs {
|
|||
"`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()),
|
||||
|
@ -386,6 +429,7 @@ impl Attrs {
|
|||
doc_comment: vec![],
|
||||
methods: vec![],
|
||||
value_parser: None,
|
||||
action: None,
|
||||
parser: None,
|
||||
verbatim_doc_comment: None,
|
||||
next_display_order: None,
|
||||
|
@ -401,6 +445,8 @@ impl Attrs {
|
|||
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 {
|
||||
self.methods.push(Method::new(name, quote!(#arg)));
|
||||
}
|
||||
|
@ -426,6 +472,11 @@ impl Attrs {
|
|||
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));
|
||||
}
|
||||
|
@ -718,11 +769,31 @@ impl Attrs {
|
|||
let inner_type = inner_type(field_type);
|
||||
p.resolve(inner_type)
|
||||
})
|
||||
.unwrap_or_else(|| self.parser(field_type).value_parser())
|
||||
.unwrap_or_else(|| {
|
||||
if let Some(action) = self.action.as_ref() {
|
||||
let inner_type = inner_type(field_type);
|
||||
default_value_parser(inner_type, action.span())
|
||||
} else {
|
||||
self.parser(field_type).value_parser()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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() {
|
||||
default_action(field_type, value_parser.span())
|
||||
} else {
|
||||
self.parser(field_type).action()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ignore_parser(&self) -> bool {
|
||||
self.value_parser.is_some()
|
||||
self.value_parser.is_some() || self.action.is_some()
|
||||
}
|
||||
|
||||
pub fn parser(&self, field_type: &Type) -> Sp<Parser> {
|
||||
|
@ -780,15 +851,7 @@ impl ValueParser {
|
|||
fn resolve(self, inner_type: &Type) -> Method {
|
||||
match self {
|
||||
Self::Explicit(method) => method,
|
||||
Self::Implicit(ident) => {
|
||||
let func = Ident::new("value_parser", ident.span());
|
||||
Method::new(
|
||||
func,
|
||||
quote_spanned! { ident.span()=>
|
||||
clap::value_parser!(#inner_type)
|
||||
},
|
||||
)
|
||||
}
|
||||
Self::Implicit(ident) => default_value_parser(inner_type, ident.span()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -800,6 +863,68 @@ impl ValueParser {
|
|||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -846,6 +971,10 @@ impl Method {
|
|||
|
||||
Some(Method::new(ident, quote!(#lit)))
|
||||
}
|
||||
|
||||
pub(crate) fn args(&self) -> &TokenStream {
|
||||
&self.args
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Method {
|
||||
|
@ -962,6 +1091,23 @@ impl Parser {
|
|||
),
|
||||
}
|
||||
}
|
||||
|
||||
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)]
|
||||
|
|
|
@ -246,6 +246,7 @@ pub fn gen_augment(
|
|||
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;
|
||||
|
@ -287,6 +288,7 @@ pub fn gen_augment(
|
|||
#possible_values
|
||||
#validator
|
||||
#value_parser
|
||||
#action
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -299,6 +301,7 @@ pub fn gen_augment(
|
|||
#possible_values
|
||||
#validator
|
||||
#value_parser
|
||||
#action
|
||||
},
|
||||
|
||||
Ty::OptionVec => quote_spanned! { ty.span()=>
|
||||
|
@ -308,6 +311,7 @@ pub fn gen_augment(
|
|||
#possible_values
|
||||
#validator
|
||||
#value_parser
|
||||
#action
|
||||
},
|
||||
|
||||
Ty::Vec => {
|
||||
|
@ -318,6 +322,7 @@ pub fn gen_augment(
|
|||
#possible_values
|
||||
#validator
|
||||
#value_parser
|
||||
#action
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -331,13 +336,18 @@ pub fn gen_augment(
|
|||
|
||||
Ty::Other => {
|
||||
let required = attrs.find_default_method().is_none() && !override_required;
|
||||
// `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)
|
||||
.required(#required && #action_value.takes_values())
|
||||
#possible_values
|
||||
#validator
|
||||
#value_parser
|
||||
#action
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -27,6 +27,7 @@ pub enum ClapAttr {
|
|||
Short(Ident),
|
||||
Long(Ident),
|
||||
ValueParser(Ident),
|
||||
Action(Ident),
|
||||
Env(Ident),
|
||||
Flatten(Ident),
|
||||
ArgEnum(Ident),
|
||||
|
@ -184,6 +185,7 @@ impl Parse for ClapAttr {
|
|||
"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(ArgEnum(name)),
|
||||
|
|
|
@ -179,6 +179,10 @@ These correspond to a `clap::Arg`.
|
|||
- `value_parser [= <expr>]`: `clap::Arg::value_parser`
|
||||
- When not present: will auto-select an implementation based on the field type
|
||||
- To register a custom type's `ValueParser`, implement `ValueParserFactory`
|
||||
- When present, implies `#[clap(action)]`
|
||||
- `action [= <expr>]`: `clap::Arg::action`
|
||||
- When not present: will auto-select an action based on the field type
|
||||
- When present, implies `#[clap(value_parser)]`
|
||||
- `help = <expr>`: `clap::Arg::help`
|
||||
- When not present: [Doc comment summary](#doc-comments)
|
||||
- `long_help = <expr>`: `clap::Arg::long_help`
|
||||
|
@ -346,7 +350,7 @@ struct Robo {
|
|||
/// I am artificial superintelligence. I won't rest
|
||||
/// until I'll have destroyed humanity. Enjoy your
|
||||
/// pathetic existence, you mere mortals.
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
kill_all_humans: bool,
|
||||
}
|
||||
```
|
||||
|
|
|
@ -2,20 +2,23 @@ use clap::{arg, Args as _, Command, FromArgMatches as _, Parser};
|
|||
|
||||
#[derive(Parser, Debug)]
|
||||
struct DerivedArgs {
|
||||
#[clap(short, long)]
|
||||
#[clap(short, long, action)]
|
||||
derived: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let cli = Command::new("CLI").arg(arg!(-b - -built));
|
||||
let cli = Command::new("CLI").arg(arg!(-b - -built).action(clap::ArgAction::SetTrue));
|
||||
// Augment built args with derived args
|
||||
let cli = DerivedArgs::augment_args(cli);
|
||||
|
||||
let matches = cli.get_matches();
|
||||
println!("Value of built: {:?}", matches.is_present("built"));
|
||||
println!(
|
||||
"Value of built: {:?}",
|
||||
*matches.get_one::<bool>("built").unwrap()
|
||||
);
|
||||
println!(
|
||||
"Value of derived via ArgMatches: {:?}",
|
||||
matches.is_present("derived")
|
||||
*matches.get_one::<bool>("derived").unwrap()
|
||||
);
|
||||
|
||||
// Since DerivedArgs implements FromArgMatches, we can extract it from the unstructured ArgMatches.
|
||||
|
|
|
@ -3,7 +3,7 @@ use clap::{Command, FromArgMatches as _, Parser, Subcommand as _};
|
|||
#[derive(Parser, Debug)]
|
||||
enum Subcommands {
|
||||
Derived {
|
||||
#[clap(short, long)]
|
||||
#[clap(short, long, action)]
|
||||
derived_flag: bool,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -11,11 +11,11 @@ USAGE:
|
|||
custom-bool[EXE] [OPTIONS] --foo <FOO> <BOOM>
|
||||
|
||||
ARGS:
|
||||
<BOOM>
|
||||
<BOOM> [possible values: true, false]
|
||||
|
||||
OPTIONS:
|
||||
--bar <BAR> [default: false]
|
||||
--foo <FOO>
|
||||
--foo <FOO> [possible values: true, false]
|
||||
-h, --help Print help information
|
||||
-V, --version Print version information
|
||||
|
||||
|
@ -31,14 +31,14 @@ USAGE:
|
|||
For more information try --help
|
||||
|
||||
$ custom-bool --foo true false
|
||||
[examples/derive_ref/custom-bool.rs:32] opt = Opt {
|
||||
[examples/derive_ref/custom-bool.rs:31] opt = Opt {
|
||||
foo: true,
|
||||
bar: false,
|
||||
boom: false,
|
||||
}
|
||||
|
||||
$ custom-bool --foo true --bar true false
|
||||
[examples/derive_ref/custom-bool.rs:32] opt = Opt {
|
||||
[examples/derive_ref/custom-bool.rs:31] opt = Opt {
|
||||
foo: true,
|
||||
bar: true,
|
||||
boom: false,
|
||||
|
|
|
@ -1,21 +1,20 @@
|
|||
#![allow(deprecated)] // Can't opt-out of implicit flags until #3405
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Parser, Debug, PartialEq)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct Opt {
|
||||
// Default parser for `try_from_str` is FromStr::from_str.
|
||||
// Default parser for `Set` is FromStr::from_str.
|
||||
// `impl FromStr for bool` parses `true` or `false` so this
|
||||
// works as expected.
|
||||
#[clap(long, parse(try_from_str))]
|
||||
#[clap(long, action = clap::ArgAction::Set)]
|
||||
foo: bool,
|
||||
|
||||
// Of course, this could be done with an explicit parser function.
|
||||
#[clap(long, parse(try_from_str = true_or_false), default_value_t)]
|
||||
#[clap(long, action = clap::ArgAction::Set, value_parser = true_or_false, default_value_t)]
|
||||
bar: bool,
|
||||
|
||||
// `bool` can be positional only with explicit `parse(...)` annotation
|
||||
#[clap(parse(try_from_str))]
|
||||
// `bool` can be positional only with explicit `action` annotation
|
||||
#[clap(action = clap::ArgAction::Set)]
|
||||
boom: bool,
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ struct AddArgs {
|
|||
}
|
||||
#[derive(Parser, Debug)]
|
||||
struct RemoveArgs {
|
||||
#[clap(short, long)]
|
||||
#[clap(short, long, action)]
|
||||
force: bool,
|
||||
#[clap(value_parser)]
|
||||
name: Vec<String>,
|
||||
|
@ -69,7 +69,7 @@ impl Subcommand for CliSub {
|
|||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Cli {
|
||||
#[clap(short, long)]
|
||||
#[clap(short, long, action)]
|
||||
top_level: bool,
|
||||
#[clap(subcommand)]
|
||||
subcommand: CliSub,
|
||||
|
|
|
@ -5,7 +5,7 @@ use clap::Parser;
|
|||
#[derive(Parser)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct Cli {
|
||||
#[clap(short = 'f')]
|
||||
#[clap(short = 'f', action)]
|
||||
eff: bool,
|
||||
|
||||
#[clap(short = 'p', value_name = "PEAR", value_parser)]
|
||||
|
|
|
@ -14,8 +14,8 @@ struct Cli {
|
|||
config: Option<PathBuf>,
|
||||
|
||||
/// Turn debugging information on
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
debug: usize,
|
||||
#[clap(short, long, action = clap::ArgAction::Count)]
|
||||
debug: u64,
|
||||
|
||||
#[clap(subcommand)]
|
||||
command: Option<Commands>,
|
||||
|
@ -26,7 +26,7 @@ enum Commands {
|
|||
/// does testing things
|
||||
Test {
|
||||
/// lists test values
|
||||
#[clap(short, long)]
|
||||
#[clap(short, long, action)]
|
||||
list: bool,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use clap::Parser;
|
|||
#[derive(Parser)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct Cli {
|
||||
#[clap(short, long)]
|
||||
#[clap(short, long, action)]
|
||||
verbose: bool,
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@ use clap::Parser;
|
|||
#[derive(Parser)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct Cli {
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
verbose: usize,
|
||||
#[clap(short, long, action = clap::ArgAction::Count)]
|
||||
verbose: u64,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -13,15 +13,15 @@ struct Cli {
|
|||
set_ver: Option<String>,
|
||||
|
||||
/// auto inc major
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
major: bool,
|
||||
|
||||
/// auto inc minor
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
minor: bool,
|
||||
|
||||
/// auto inc patch
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
patch: bool,
|
||||
|
||||
/// some regular input
|
||||
|
|
|
@ -8,15 +8,15 @@ struct Cli {
|
|||
set_ver: Option<String>,
|
||||
|
||||
/// auto inc major
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
major: bool,
|
||||
|
||||
/// auto inc minor
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
minor: bool,
|
||||
|
||||
/// auto inc patch
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
patch: bool,
|
||||
|
||||
/// some regular input
|
||||
|
|
|
@ -236,17 +236,11 @@ $ 03_01_flag_bool_derive --verbose
|
|||
verbose: true
|
||||
|
||||
$ 03_01_flag_bool_derive --verbose --verbose
|
||||
? failed
|
||||
error: The argument '--verbose' was provided more than once, but cannot be used multiple times
|
||||
|
||||
USAGE:
|
||||
03_01_flag_bool_derive[EXE] [OPTIONS]
|
||||
|
||||
For more information try --help
|
||||
verbose: true
|
||||
|
||||
```
|
||||
|
||||
Or counted with `#[clap(parse(from_occurrences))]`:
|
||||
Or counted with `#[clap(action = clap::ArgAction::Count)]`:
|
||||
|
||||
[Example:](03_01_flag_count.rs)
|
||||
```console
|
||||
|
|
|
@ -23,7 +23,7 @@ fn doc_comments() {
|
|||
struct LoremIpsum {
|
||||
/// Fooify a bar
|
||||
/// and a baz
|
||||
#[clap(short, long)]
|
||||
#[clap(short, long, action)]
|
||||
foo: bool,
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,12 @@ fn help_is_better_than_comments() {
|
|||
#[clap(name = "lorem-ipsum", about = "Dolor sit amet")]
|
||||
struct LoremIpsum {
|
||||
/// Fooify a bar
|
||||
#[clap(short, long, help = "DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES")]
|
||||
#[clap(
|
||||
short,
|
||||
long,
|
||||
help = "DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES",
|
||||
action
|
||||
)]
|
||||
foo: bool,
|
||||
}
|
||||
|
||||
|
@ -71,11 +76,11 @@ fn field_long_doc_comment_both_help_long_help() {
|
|||
/// Dot is removed from multiline comments.
|
||||
///
|
||||
/// Long help
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
foo: bool,
|
||||
|
||||
/// Dot is removed from one short comment.
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
bar: bool,
|
||||
}
|
||||
|
||||
|
@ -141,7 +146,7 @@ fn verbatim_doc_comment() {
|
|||
#[derive(Parser, Debug)]
|
||||
#[clap(verbatim_doc_comment)]
|
||||
struct SeeFigure1 {
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
foo: bool,
|
||||
}
|
||||
|
||||
|
@ -171,10 +176,10 @@ fn verbatim_doc_comment_field() {
|
|||
#[derive(Parser, Debug)]
|
||||
struct Command {
|
||||
/// This help ends in a period.
|
||||
#[clap(long, verbatim_doc_comment)]
|
||||
#[clap(long, verbatim_doc_comment, action)]
|
||||
foo: bool,
|
||||
/// This help does not end in a period.
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
bar: bool,
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ use clap::Parser;
|
|||
fn bool_type_is_flag() {
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short, long)]
|
||||
#[clap(short, long, action)]
|
||||
alice: bool,
|
||||
}
|
||||
|
||||
|
@ -32,24 +32,26 @@ fn bool_type_is_flag() {
|
|||
Opt { alice: true },
|
||||
Opt::try_parse_from(&["test", "-a"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { alice: true },
|
||||
Opt::try_parse_from(&["test", "-a", "-a"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { alice: true },
|
||||
Opt::try_parse_from(&["test", "--alice"]).unwrap()
|
||||
);
|
||||
assert!(Opt::try_parse_from(&["test", "-i"]).is_err());
|
||||
assert!(Opt::try_parse_from(&["test", "-a", "foo"]).is_err());
|
||||
assert!(Opt::try_parse_from(&["test", "-a", "-a"]).is_err());
|
||||
assert!(Opt::try_parse_from(&["test", "-a", "--alice"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_occurrences() {
|
||||
fn count() {
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
#[clap(short, long, action = clap::ArgAction::Count)]
|
||||
alice: u64,
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
bob: u8,
|
||||
#[clap(short, long, action = clap::ArgAction::Count)]
|
||||
bob: u64,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
|
@ -111,9 +113,9 @@ fn non_bool_type_flag() {
|
|||
fn mixed_type_flags() {
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short, long)]
|
||||
#[clap(short, long, action)]
|
||||
alice: bool,
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
#[clap(short, long, action = clap::ArgAction::Count)]
|
||||
bob: u64,
|
||||
}
|
||||
|
||||
|
@ -191,10 +193,10 @@ fn ignore_qualified_bool_type() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn override_implicit_from_flag() {
|
||||
fn override_implicit_action() {
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(long, parse(try_from_str))]
|
||||
#[clap(long, action = clap::ArgAction::Set)]
|
||||
arg: bool,
|
||||
}
|
||||
|
||||
|
@ -213,7 +215,7 @@ fn override_implicit_from_flag() {
|
|||
fn override_implicit_from_flag_positional() {
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(parse(try_from_str))]
|
||||
#[clap(action = clap::ArgAction::Set)]
|
||||
arg: bool,
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ fn flatten_in_subcommand() {
|
|||
|
||||
#[derive(Args, PartialEq, Debug)]
|
||||
struct Add {
|
||||
#[clap(short)]
|
||||
#[clap(short, action)]
|
||||
interactive: bool,
|
||||
#[clap(flatten)]
|
||||
common: Common,
|
||||
|
@ -79,7 +79,7 @@ fn flatten_in_subcommand() {
|
|||
#[derive(Parser, PartialEq, Debug)]
|
||||
enum Opt {
|
||||
Fetch {
|
||||
#[clap(short)]
|
||||
#[clap(short, action)]
|
||||
all: bool,
|
||||
#[clap(flatten)]
|
||||
common: Common,
|
||||
|
@ -227,7 +227,7 @@ fn docstrings_ordering_with_multiple_clap() {
|
|||
/// This is the docstring for Flattened
|
||||
#[derive(Args)]
|
||||
struct Flattened {
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
foo: bool,
|
||||
}
|
||||
|
||||
|
@ -248,7 +248,7 @@ fn docstrings_ordering_with_multiple_clap_partial() {
|
|||
/// This is the docstring for Flattened
|
||||
#[derive(Args)]
|
||||
struct Flattened {
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
foo: bool,
|
||||
}
|
||||
|
||||
|
|
|
@ -274,8 +274,8 @@ fn derive_generated_error_has_full_context() {
|
|||
#[derive(Debug, Parser)]
|
||||
enum SubCommands {
|
||||
Sub {
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
verbose: u8,
|
||||
#[clap(short, long, action = clap::ArgAction::Count)]
|
||||
verbose: u64,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -339,7 +339,7 @@ OPTIONS:
|
|||
#[clap(next_display_order = 10000)]
|
||||
struct A {
|
||||
/// second flag
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
flag_a: bool,
|
||||
/// second option
|
||||
#[clap(long, value_parser)]
|
||||
|
@ -350,7 +350,7 @@ OPTIONS:
|
|||
#[clap(next_display_order = 10)]
|
||||
struct B {
|
||||
/// first flag
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
flag_b: bool,
|
||||
/// first option
|
||||
#[clap(long, value_parser)]
|
||||
|
@ -397,7 +397,7 @@ OPTIONS:
|
|||
#[derive(Args, Debug)]
|
||||
struct A {
|
||||
/// second flag
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
flag_a: bool,
|
||||
/// second option
|
||||
#[clap(long, value_parser)]
|
||||
|
@ -407,7 +407,7 @@ OPTIONS:
|
|||
#[derive(Args, Debug)]
|
||||
struct B {
|
||||
/// first flag
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
flag_b: bool,
|
||||
/// first option
|
||||
#[clap(long, value_parser)]
|
||||
|
@ -453,7 +453,7 @@ OPTIONS:
|
|||
#[derive(Args, Debug)]
|
||||
struct A {
|
||||
/// first flag
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
flag_a: bool,
|
||||
/// first option
|
||||
#[clap(long, value_parser)]
|
||||
|
@ -463,7 +463,7 @@ OPTIONS:
|
|||
#[derive(Args, Debug)]
|
||||
struct B {
|
||||
/// second flag
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
flag_b: bool,
|
||||
/// second option
|
||||
#[clap(long, value_parser)]
|
||||
|
|
|
@ -5,14 +5,14 @@ use crate::utils;
|
|||
use clap::{ArgGroup, Args, Parser, Subcommand};
|
||||
|
||||
#[test]
|
||||
fn issue_151() {
|
||||
fn issue_151_groups_within_subcommands() {
|
||||
#[derive(Args, Debug)]
|
||||
#[clap(group = ArgGroup::new("verb").required(true).multiple(true))]
|
||||
struct Opt {
|
||||
#[clap(long, group = "verb")]
|
||||
foo: bool,
|
||||
#[clap(long, group = "verb")]
|
||||
bar: bool,
|
||||
#[clap(long, group = "verb", value_parser)]
|
||||
foo: Option<String>,
|
||||
#[clap(long, group = "verb", value_parser)]
|
||||
bar: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
|
@ -22,10 +22,10 @@ fn issue_151() {
|
|||
}
|
||||
|
||||
assert!(Cli::try_parse_from(&["test"]).is_err());
|
||||
assert!(Cli::try_parse_from(&["test", "--foo"]).is_ok());
|
||||
assert!(Cli::try_parse_from(&["test", "--bar"]).is_ok());
|
||||
assert!(Cli::try_parse_from(&["test", "--zebra"]).is_err());
|
||||
assert!(Cli::try_parse_from(&["test", "--foo", "--bar"]).is_ok());
|
||||
assert!(Cli::try_parse_from(&["test", "--foo=v1"]).is_ok());
|
||||
assert!(Cli::try_parse_from(&["test", "--bar=v2"]).is_ok());
|
||||
assert!(Cli::try_parse_from(&["test", "--zebra=v3"]).is_err());
|
||||
assert!(Cli::try_parse_from(&["test", "--foo=v1", "--bar=v2"]).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -5,7 +5,7 @@ fn test_standalone_long_generates_kebab_case() {
|
|||
#[derive(Parser, Debug, PartialEq)]
|
||||
#[allow(non_snake_case)]
|
||||
struct Opt {
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
FOO_OPTION: bool,
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ fn test_standalone_long_generates_kebab_case() {
|
|||
fn test_custom_long_overwrites_default_name() {
|
||||
#[derive(Parser, Debug, PartialEq)]
|
||||
struct Opt {
|
||||
#[clap(long = "foo")]
|
||||
#[clap(long = "foo", action)]
|
||||
foo_option: bool,
|
||||
}
|
||||
|
||||
|
|
|
@ -16,9 +16,9 @@ use clap::{Parser, Subcommand};
|
|||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short, long)]
|
||||
#[clap(short, long, action)]
|
||||
force: bool,
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
#[clap(short, long, action = clap::ArgAction::Count)]
|
||||
verbose: u64,
|
||||
#[clap(subcommand)]
|
||||
cmd: Sub,
|
||||
|
@ -32,9 +32,9 @@ enum Sub {
|
|||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt2 {
|
||||
#[clap(short, long)]
|
||||
#[clap(short, long, action)]
|
||||
force: bool,
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
#[clap(short, long, action = clap::ArgAction::Count)]
|
||||
verbose: u64,
|
||||
#[clap(subcommand)]
|
||||
cmd: Option<Sub>,
|
||||
|
@ -109,7 +109,7 @@ fn test_badinput() {
|
|||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt3 {
|
||||
#[clap(short, long)]
|
||||
#[clap(short, long, action)]
|
||||
all: bool,
|
||||
#[clap(subcommand)]
|
||||
cmd: Sub2,
|
||||
|
|
|
@ -37,8 +37,11 @@ fn required_option() {
|
|||
Opt { arg: 42 },
|
||||
Opt::try_parse_from(&["test", "--arg", "42"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { arg: 42 },
|
||||
Opt::try_parse_from(&["test", "--arg", "24", "--arg", "42"]).unwrap()
|
||||
);
|
||||
assert!(Opt::try_parse_from(&["test"]).is_err());
|
||||
assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -52,8 +55,11 @@ fn option_with_default() {
|
|||
Opt { arg: 24 },
|
||||
Opt::try_parse_from(&["test", "-a24"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { arg: 42 },
|
||||
Opt::try_parse_from(&["test", "-a", "24", "-a", "42"]).unwrap()
|
||||
);
|
||||
assert_eq!(Opt { arg: 42 }, Opt::try_parse_from(&["test"]).unwrap());
|
||||
assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -67,8 +73,11 @@ fn option_with_raw_default() {
|
|||
Opt { arg: 24 },
|
||||
Opt::try_parse_from(&["test", "-a24"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { arg: 42 },
|
||||
Opt::try_parse_from(&["test", "-a", "24", "-a", "42"]).unwrap()
|
||||
);
|
||||
assert_eq!(Opt { arg: 42 }, Opt::try_parse_from(&["test"]).unwrap());
|
||||
assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -157,8 +166,11 @@ fn option_type_is_optional() {
|
|||
Opt { arg: Some(42) },
|
||||
Opt::try_parse_from(&["test", "-a42"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { arg: Some(42) },
|
||||
Opt::try_parse_from(&["test", "-a", "24", "-a", "42"]).unwrap()
|
||||
);
|
||||
assert_eq!(Opt { arg: None }, Opt::try_parse_from(&["test"]).unwrap());
|
||||
assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -176,8 +188,8 @@ fn required_with_option_type() {
|
|||
#[derive(Debug, PartialEq, Eq, Subcommand)]
|
||||
enum SubCommands {
|
||||
ExSub {
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
verbose: u8,
|
||||
#[clap(short, long, action = clap::ArgAction::Count)]
|
||||
verbose: u64,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -238,8 +250,13 @@ fn option_option_type_is_optional_value() {
|
|||
Opt { arg: Some(None) },
|
||||
Opt::try_parse_from(&["test", "-a"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: Some(Some(42))
|
||||
},
|
||||
Opt::try_parse_from(&["test", "-a", "24", "-a", "42"]).unwrap()
|
||||
);
|
||||
assert_eq!(Opt { arg: None }, Opt::try_parse_from(&["test"]).unwrap());
|
||||
assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -20,19 +20,19 @@ use clap::{Args, Parser, Subcommand};
|
|||
enum Opt {
|
||||
/// Fetch stuff from GitHub
|
||||
Fetch {
|
||||
#[clap(long)]
|
||||
#[clap(long, action)]
|
||||
all: bool,
|
||||
#[clap(short, long)]
|
||||
/// Overwrite local branches.
|
||||
#[clap(short, long, action)]
|
||||
force: bool,
|
||||
#[clap(value_parser)]
|
||||
repo: String,
|
||||
},
|
||||
|
||||
Add {
|
||||
#[clap(short, long)]
|
||||
#[clap(short, long, action)]
|
||||
interactive: bool,
|
||||
#[clap(short, long)]
|
||||
#[clap(short, long, action)]
|
||||
verbose: bool,
|
||||
},
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ fn test_tuple_commands() {
|
|||
fn global_passed_down() {
|
||||
#[derive(Debug, PartialEq, Parser)]
|
||||
struct Opt {
|
||||
#[clap(global = true, long)]
|
||||
#[clap(global = true, long, action)]
|
||||
other: bool,
|
||||
#[clap(subcommand)]
|
||||
sub: Subcommands,
|
||||
|
@ -187,7 +187,7 @@ fn global_passed_down() {
|
|||
|
||||
#[derive(Debug, PartialEq, Args)]
|
||||
struct GlobalCmd {
|
||||
#[clap(from_global)]
|
||||
#[clap(from_global, action)]
|
||||
other: bool,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue