mirror of
https://github.com/clap-rs/clap
synced 2024-12-12 22:02:35 +00:00
Merge pull request #3777 from epage/set
feat(builder): Set/Append Actions
This commit is contained in:
commit
b417e62ccd
3 changed files with 185 additions and 14 deletions
|
@ -24,6 +24,53 @@
|
|||
#[non_exhaustive]
|
||||
#[allow(missing_copy_implementations)] // In the future, we may accept `Box<dyn ...>`
|
||||
pub enum ArgAction {
|
||||
/// When encountered, store the associated value(s) in [`ArgMatches`][crate::ArgMatches]
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use clap::Command;
|
||||
/// # use clap::Arg;
|
||||
/// let cmd = Command::new("mycmd")
|
||||
/// .arg(
|
||||
/// Arg::new("flag")
|
||||
/// .long("flag")
|
||||
/// .action(clap::builder::ArgAction::Set)
|
||||
/// );
|
||||
///
|
||||
/// let matches = cmd.try_get_matches_from(["mycmd", "--flag", "value"]).unwrap();
|
||||
/// assert!(matches.is_present("flag"));
|
||||
/// assert_eq!(matches.occurrences_of("flag"), 0);
|
||||
/// assert_eq!(
|
||||
/// matches.get_many::<String>("flag").unwrap_or_default().map(|v| v.as_str()).collect::<Vec<_>>(),
|
||||
/// vec!["value"]
|
||||
/// );
|
||||
/// ```
|
||||
Set,
|
||||
/// When encountered, store the associated value(s) in [`ArgMatches`][crate::ArgMatches]
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use clap::Command;
|
||||
/// # use clap::Arg;
|
||||
/// let cmd = Command::new("mycmd")
|
||||
/// .arg(
|
||||
/// Arg::new("flag")
|
||||
/// .long("flag")
|
||||
/// .multiple_occurrences(true)
|
||||
/// .action(clap::builder::ArgAction::Append)
|
||||
/// );
|
||||
///
|
||||
/// let matches = cmd.try_get_matches_from(["mycmd", "--flag", "value1", "--flag", "value2"]).unwrap();
|
||||
/// assert!(matches.is_present("flag"));
|
||||
/// assert_eq!(matches.occurrences_of("flag"), 0);
|
||||
/// assert_eq!(
|
||||
/// matches.get_many::<String>("flag").unwrap_or_default().map(|v| v.as_str()).collect::<Vec<_>>(),
|
||||
/// vec!["value1", "value2"]
|
||||
/// );
|
||||
/// ```
|
||||
Append,
|
||||
/// When encountered, store the associated value(s) in [`ArgMatches`][crate::ArgMatches]
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -201,6 +248,8 @@ pub enum ArgAction {
|
|||
impl ArgAction {
|
||||
pub(crate) fn takes_value(&self) -> bool {
|
||||
match self {
|
||||
Self::Set => true,
|
||||
Self::Append => true,
|
||||
Self::StoreValue => true,
|
||||
Self::IncOccurrence => false,
|
||||
Self::SetTrue => false,
|
||||
|
@ -213,6 +262,8 @@ impl ArgAction {
|
|||
|
||||
pub(crate) fn default_value_parser(&self) -> Option<super::ValueParser> {
|
||||
match self {
|
||||
Self::Set => None,
|
||||
Self::Append => None,
|
||||
Self::StoreValue => None,
|
||||
Self::IncOccurrence => None,
|
||||
Self::SetTrue => Some(super::ValueParser::bool()),
|
||||
|
@ -228,6 +279,8 @@ impl ArgAction {
|
|||
use crate::parser::AnyValueId;
|
||||
|
||||
match self {
|
||||
Self::Set => None,
|
||||
Self::Append => None,
|
||||
Self::StoreValue => None,
|
||||
Self::IncOccurrence => None,
|
||||
Self::SetTrue => Some(AnyValueId::of::<bool>()),
|
||||
|
|
|
@ -1138,6 +1138,41 @@ impl<'help, 'cmd> Parser<'help, 'cmd> {
|
|||
source
|
||||
);
|
||||
match arg.get_action() {
|
||||
ArgAction::Set => {
|
||||
if source == ValueSource::CommandLine
|
||||
&& matches!(ident, Some(Identifier::Short) | Some(Identifier::Long))
|
||||
{
|
||||
// Record flag's index
|
||||
self.cur_idx.set(self.cur_idx.get() + 1);
|
||||
debug!("Parser::react: cur_idx:={}", self.cur_idx.get());
|
||||
}
|
||||
matcher.remove(&arg.id);
|
||||
self.start_custom_arg(matcher, arg, source);
|
||||
self.push_arg_values(arg, raw_vals, matcher)?;
|
||||
if cfg!(debug_assertions) && matcher.needs_more_vals(arg) {
|
||||
debug!(
|
||||
"Parser::react not enough values passed in, leaving it to the validator to complain",
|
||||
);
|
||||
}
|
||||
Ok(ParseResult::ValuesDone)
|
||||
}
|
||||
ArgAction::Append => {
|
||||
if source == ValueSource::CommandLine
|
||||
&& matches!(ident, Some(Identifier::Short) | Some(Identifier::Long))
|
||||
{
|
||||
// Record flag's index
|
||||
self.cur_idx.set(self.cur_idx.get() + 1);
|
||||
debug!("Parser::react: cur_idx:={}", self.cur_idx.get());
|
||||
}
|
||||
self.start_custom_arg(matcher, arg, source);
|
||||
self.push_arg_values(arg, raw_vals, matcher)?;
|
||||
if cfg!(debug_assertions) && matcher.needs_more_vals(arg) {
|
||||
debug!(
|
||||
"Parser::react not enough values passed in, leaving it to the validator to complain",
|
||||
);
|
||||
}
|
||||
Ok(ParseResult::ValuesDone)
|
||||
}
|
||||
ArgAction::StoreValue => {
|
||||
if ident == Some(Identifier::Index)
|
||||
&& arg.is_multiple_values_set()
|
||||
|
@ -1189,10 +1224,10 @@ impl<'help, 'cmd> Parser<'help, 'cmd> {
|
|||
}
|
||||
1 => raw_vals,
|
||||
_ => {
|
||||
panic!(
|
||||
"Argument {:?} received too many values: {:?}",
|
||||
arg.id, raw_vals
|
||||
)
|
||||
debug!("Parser::react ignoring trailing values: {:?}", raw_vals);
|
||||
let mut raw_vals = raw_vals;
|
||||
raw_vals.resize(1, Default::default());
|
||||
raw_vals
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1208,10 +1243,10 @@ impl<'help, 'cmd> Parser<'help, 'cmd> {
|
|||
}
|
||||
1 => raw_vals,
|
||||
_ => {
|
||||
panic!(
|
||||
"Argument {:?} received too many values: {:?}",
|
||||
arg.id, raw_vals
|
||||
)
|
||||
debug!("Parser::react ignoring trailing values: {:?}", raw_vals);
|
||||
let mut raw_vals = raw_vals;
|
||||
raw_vals.resize(1, Default::default());
|
||||
raw_vals
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1231,10 +1266,10 @@ impl<'help, 'cmd> Parser<'help, 'cmd> {
|
|||
}
|
||||
1 => raw_vals,
|
||||
_ => {
|
||||
panic!(
|
||||
"Argument {:?} received too many values: {:?}",
|
||||
arg.id, raw_vals
|
||||
)
|
||||
debug!("Parser::react ignoring trailing values: {:?}", raw_vals);
|
||||
let mut raw_vals = raw_vals;
|
||||
raw_vals.resize(1, Default::default());
|
||||
raw_vals
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1339,14 +1374,26 @@ impl<'help, 'cmd> Parser<'help, 'cmd> {
|
|||
)?;
|
||||
}
|
||||
}
|
||||
ArgAction::SetTrue | ArgAction::SetFalse | ArgAction::Count => {
|
||||
ArgAction::Set
|
||||
| ArgAction::Append
|
||||
| ArgAction::SetTrue
|
||||
| ArgAction::SetFalse
|
||||
| ArgAction::Count => {
|
||||
let mut arg_values = Vec::new();
|
||||
let _parse_result =
|
||||
self.split_arg_values(arg, &val, trailing_values, &mut arg_values);
|
||||
let _ = self.react(
|
||||
None,
|
||||
ValueSource::EnvVariable,
|
||||
arg,
|
||||
vec![val.to_os_str().into_owned()],
|
||||
arg_values,
|
||||
matcher,
|
||||
)?;
|
||||
if let Some(_parse_result) = _parse_result {
|
||||
if _parse_result != ParseResult::ValuesDone {
|
||||
debug!("Parser::add_env: Ignoring state {:?}; env variables are outside of the parse loop", _parse_result);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Early return on `Help` or `Version`.
|
||||
ArgAction::Help | ArgAction::Version => {
|
||||
|
|
|
@ -4,6 +4,77 @@ use clap::builder::ArgAction;
|
|||
use clap::Arg;
|
||||
use clap::Command;
|
||||
|
||||
#[test]
|
||||
fn set() {
|
||||
let cmd = Command::new("test").arg(Arg::new("mammal").long("mammal").action(ArgAction::Set));
|
||||
|
||||
let matches = cmd.clone().try_get_matches_from(["test"]).unwrap();
|
||||
assert_eq!(matches.get_one::<String>("mammal"), None);
|
||||
assert_eq!(matches.is_present("mammal"), false);
|
||||
assert_eq!(matches.occurrences_of("mammal"), 0);
|
||||
assert_eq!(matches.index_of("mammal"), None);
|
||||
|
||||
let matches = cmd
|
||||
.clone()
|
||||
.try_get_matches_from(["test", "--mammal", "dog"])
|
||||
.unwrap();
|
||||
assert_eq!(matches.get_one::<String>("mammal").unwrap(), "dog");
|
||||
assert_eq!(matches.is_present("mammal"), true);
|
||||
assert_eq!(matches.occurrences_of("mammal"), 0);
|
||||
assert_eq!(matches.index_of("mammal"), Some(2));
|
||||
|
||||
let matches = cmd
|
||||
.clone()
|
||||
.try_get_matches_from(["test", "--mammal", "dog", "--mammal", "cat"])
|
||||
.unwrap();
|
||||
assert_eq!(matches.get_one::<String>("mammal").unwrap(), "cat");
|
||||
assert_eq!(matches.is_present("mammal"), true);
|
||||
assert_eq!(matches.occurrences_of("mammal"), 0);
|
||||
assert_eq!(matches.index_of("mammal"), Some(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn append() {
|
||||
let cmd = Command::new("test").arg(Arg::new("mammal").long("mammal").action(ArgAction::Append));
|
||||
|
||||
let matches = cmd.clone().try_get_matches_from(["test"]).unwrap();
|
||||
assert_eq!(matches.get_one::<String>("mammal"), None);
|
||||
assert_eq!(matches.is_present("mammal"), false);
|
||||
assert_eq!(matches.occurrences_of("mammal"), 0);
|
||||
assert_eq!(matches.index_of("mammal"), None);
|
||||
|
||||
let matches = cmd
|
||||
.clone()
|
||||
.try_get_matches_from(["test", "--mammal", "dog"])
|
||||
.unwrap();
|
||||
assert_eq!(matches.get_one::<String>("mammal").unwrap(), "dog");
|
||||
assert_eq!(matches.is_present("mammal"), true);
|
||||
assert_eq!(matches.occurrences_of("mammal"), 0);
|
||||
assert_eq!(
|
||||
matches.indices_of("mammal").unwrap().collect::<Vec<_>>(),
|
||||
vec![2]
|
||||
);
|
||||
|
||||
let matches = cmd
|
||||
.clone()
|
||||
.try_get_matches_from(["test", "--mammal", "dog", "--mammal", "cat"])
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
matches
|
||||
.get_many::<String>("mammal")
|
||||
.unwrap()
|
||||
.map(|s| s.as_str())
|
||||
.collect::<Vec<_>>(),
|
||||
vec!["dog", "cat"]
|
||||
);
|
||||
assert_eq!(matches.is_present("mammal"), true);
|
||||
assert_eq!(matches.occurrences_of("mammal"), 0);
|
||||
assert_eq!(
|
||||
matches.indices_of("mammal").unwrap().collect::<Vec<_>>(),
|
||||
vec![2, 4]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_true() {
|
||||
let cmd =
|
||||
|
|
Loading…
Reference in a new issue