Merge pull request #2400 from ldm0/unset-default

Support unsetting/removing the default if an option/flag is set
This commit is contained in:
Pavan Kumar Sunkara 2021-03-10 18:13:52 +05:30 committed by GitHub
commit 30d784ffd1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 162 additions and 40 deletions

View file

@ -110,7 +110,7 @@ pub struct Arg<'help> {
pub(crate) validator_os: Option<Arc<Mutex<ValidatorOs<'help>>>>,
pub(crate) val_delim: Option<char>,
pub(crate) default_vals: Vec<&'help OsStr>,
pub(crate) default_vals_ifs: VecMap<(Id, Option<&'help OsStr>, &'help OsStr)>,
pub(crate) default_vals_ifs: VecMap<(Id, Option<&'help OsStr>, Option<&'help OsStr>)>,
pub(crate) default_missing_vals: Vec<&'help OsStr>,
pub(crate) env: Option<(&'help OsStr, Option<OsString>)>,
pub(crate) terminator: Option<&'help str>,
@ -2723,7 +2723,7 @@ impl<'help> Arg<'help> {
/// .long("flag"))
/// .arg(Arg::new("other")
/// .long("other")
/// .default_value_if("flag", None, "default"))
/// .default_value_if("flag", None, Some("default")))
/// .get_matches_from(vec![
/// "prog", "--flag"
/// ]);
@ -2740,7 +2740,7 @@ impl<'help> Arg<'help> {
/// .long("flag"))
/// .arg(Arg::new("other")
/// .long("other")
/// .default_value_if("flag", None, "default"))
/// .default_value_if("flag", None, Some("default")))
/// .get_matches_from(vec![
/// "prog"
/// ]);
@ -2758,7 +2758,7 @@ impl<'help> Arg<'help> {
/// .long("opt"))
/// .arg(Arg::new("other")
/// .long("other")
/// .default_value_if("opt", Some("special"), "default"))
/// .default_value_if("opt", Some("special"), Some("default")))
/// .get_matches_from(vec![
/// "prog", "--opt", "special"
/// ]);
@ -2777,22 +2777,41 @@ impl<'help> Arg<'help> {
/// .long("opt"))
/// .arg(Arg::new("other")
/// .long("other")
/// .default_value_if("opt", Some("special"), "default"))
/// .default_value_if("opt", Some("special"), Some("default")))
/// .get_matches_from(vec![
/// "prog", "--opt", "hahaha"
/// ]);
///
/// assert_eq!(m.value_of("other"), None);
/// ```
///
/// If we want to unset the default value for an Arg based on the presence or
/// value of some other Arg.
///
/// ```rust
/// # use clap::{App, Arg};
/// let m = App::new("prog")
/// .arg(Arg::new("flag")
/// .long("flag"))
/// .arg(Arg::new("other")
/// .long("other")
/// .default_value("default")
/// .default_value_if("flag", None, None))
/// .get_matches_from(vec![
/// "prog", "--flag"
/// ]);
///
/// assert_eq!(m.value_of("other"), None);
/// ```
/// [`Arg::takes_value(true)`]: ./struct.Arg.html#method.takes_value
/// [`Arg::default_value`]: ./struct.Arg.html#method.default_value
pub fn default_value_if<T: Key>(
self,
arg_id: T,
val: Option<&'help str>,
default: &'help str,
default: Option<&'help str>,
) -> Self {
self.default_value_if_os(arg_id, val.map(OsStr::new), OsStr::new(default))
self.default_value_if_os(arg_id, val.map(OsStr::new), default.map(OsStr::new))
}
/// Provides a conditional default value in the exact same manner as [`Arg::default_value_if`]
@ -2804,7 +2823,7 @@ impl<'help> Arg<'help> {
mut self,
arg_id: T,
val: Option<&'help OsStr>,
default: &'help OsStr,
default: Option<&'help OsStr>,
) -> Self {
let l = self.default_vals_ifs.len();
self.default_vals_ifs
@ -2841,8 +2860,8 @@ impl<'help> Arg<'help> {
/// .arg(Arg::new("other")
/// .long("other")
/// .default_value_ifs(&[
/// ("flag", None, "default"),
/// ("opt", Some("channal"), "chan"),
/// ("flag", None, Some("default")),
/// ("opt", Some("channal"), Some("chan")),
/// ]))
/// .get_matches_from(vec![
/// "prog", "--opt", "channal"
@ -2861,8 +2880,8 @@ impl<'help> Arg<'help> {
/// .arg(Arg::new("other")
/// .long("other")
/// .default_value_ifs(&[
/// ("flag", None, "default"),
/// ("opt", Some("channal"), "chan"),
/// ("flag", None, Some("default")),
/// ("opt", Some("channal"), Some("chan")),
/// ]))
/// .get_matches_from(vec![
/// "prog"
@ -2885,8 +2904,8 @@ impl<'help> Arg<'help> {
/// .arg(Arg::new("other")
/// .long("other")
/// .default_value_ifs(&[
/// ("flag", None, "default"),
/// ("opt", Some("channal"), "chan"),
/// ("flag", None, Some("default")),
/// ("opt", Some("channal"), Some("chan")),
/// ]))
/// .get_matches_from(vec![
/// "prog", "--opt", "channal", "--flag"
@ -2898,10 +2917,10 @@ impl<'help> Arg<'help> {
/// [`Arg::default_value_if`]: ./struct.Arg.html#method.default_value_if
pub fn default_value_ifs<T: Key>(
mut self,
ifs: &[(T, Option<&'help str>, &'help str)],
ifs: &[(T, Option<&'help str>, Option<&'help str>)],
) -> Self {
for (arg, val, default) in ifs {
self = self.default_value_if_os(arg, val.map(OsStr::new), OsStr::new(*default));
self = self.default_value_if_os(arg, val.map(OsStr::new), default.map(OsStr::new));
}
self
}
@ -2913,10 +2932,10 @@ impl<'help> Arg<'help> {
/// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html
pub fn default_value_ifs_os<T: Key>(
mut self,
ifs: &[(T, Option<&'help OsStr>, &'help OsStr)],
ifs: &[(T, Option<&'help OsStr>, Option<&'help OsStr>)],
) -> Self {
for (arg, val, default) in ifs {
self = self.default_value_if_os(arg.key(), *val, default);
self = self.default_value_if_os(arg.key(), *val, *default);
}
self
}

View file

@ -43,7 +43,11 @@ macro_rules! yaml_tuple3 {
for ys in vec {
if let Some(tup) = ys.as_vec() {
debug_assert_eq!(3, tup.len());
$a = $a.$c(yaml_str!(tup[0]), yaml_opt_str!(tup[1]), yaml_str!(tup[2]));
$a = $a.$c(
yaml_str!(tup[0]),
yaml_opt_str!(tup[1]),
yaml_opt_str!(tup[2]),
);
} else {
panic!("Failed to convert YAML value to vec");
}

View file

@ -1435,7 +1435,9 @@ impl<'help, 'app> Parser<'help, 'app> {
};
if add {
self.add_val_to_arg(arg, ArgStr::new(default), matcher, ty, false);
if let Some(default) = default {
self.add_val_to_arg(arg, ArgStr::new(default), matcher, ty, false);
}
return;
}
}

View file

@ -121,7 +121,7 @@ fn osstr_positional_user_override() {
fn default_if_arg_present_no_default() {
let r = App::new("df")
.arg(Arg::from("--opt [FILE] 'some arg'"))
.arg(Arg::from("[arg] 'some arg'").default_value_if("opt", None, "default"))
.arg(Arg::from("[arg] 'some arg'").default_value_if("opt", None, Some("default")))
.try_get_matches_from(vec!["", "--opt", "some"]);
assert!(r.is_ok());
let m = r.unwrap();
@ -133,7 +133,7 @@ fn default_if_arg_present_no_default() {
fn default_if_arg_present_no_default_user_override() {
let r = App::new("df")
.arg(Arg::from("--opt [FILE] 'some arg'"))
.arg(Arg::from("[arg] 'some arg'").default_value_if("opt", None, "default"))
.arg(Arg::from("[arg] 'some arg'").default_value_if("opt", None, Some("default")))
.try_get_matches_from(vec!["", "--opt", "some", "other"]);
assert!(r.is_ok());
let m = r.unwrap();
@ -148,7 +148,7 @@ fn default_if_arg_present_no_arg_with_default() {
.arg(
Arg::from("[arg] 'some arg'")
.default_value("first")
.default_value_if("opt", None, "default"),
.default_value_if("opt", None, Some("default")),
)
.try_get_matches_from(vec![""]);
assert!(r.is_ok());
@ -164,7 +164,7 @@ fn default_if_arg_present_with_default() {
.arg(
Arg::from("[arg] 'some arg'")
.default_value("first")
.default_value_if("opt", None, "default"),
.default_value_if("opt", None, Some("default")),
)
.try_get_matches_from(vec!["", "--opt", "some"]);
assert!(r.is_ok());
@ -180,7 +180,7 @@ fn default_if_arg_present_with_default_user_override() {
.arg(
Arg::from("[arg] 'some arg'")
.default_value("first")
.default_value_if("opt", None, "default"),
.default_value_if("opt", None, Some("default")),
)
.try_get_matches_from(vec!["", "--opt", "some", "other"]);
assert!(r.is_ok());
@ -196,7 +196,7 @@ fn default_if_arg_present_no_arg_with_default_user_override() {
.arg(
Arg::from("[arg] 'some arg'")
.default_value("first")
.default_value_if("opt", None, "default"),
.default_value_if("opt", None, Some("default")),
)
.try_get_matches_from(vec!["", "other"]);
assert!(r.is_ok());
@ -211,7 +211,7 @@ fn default_if_arg_present_no_arg_with_default_user_override() {
fn default_if_arg_present_with_value_no_default() {
let r = App::new("df")
.arg(Arg::from("--opt [FILE] 'some arg'"))
.arg(Arg::from("[arg] 'some arg'").default_value_if("opt", Some("value"), "default"))
.arg(Arg::from("[arg] 'some arg'").default_value_if("opt", Some("value"), Some("default")))
.try_get_matches_from(vec!["", "--opt", "value"]);
assert!(r.is_ok());
let m = r.unwrap();
@ -223,7 +223,7 @@ fn default_if_arg_present_with_value_no_default() {
fn default_if_arg_present_with_value_no_default_fail() {
let r = App::new("df")
.arg(Arg::from("--opt [FILE] 'some arg'"))
.arg(Arg::from("[arg] 'some arg'").default_value_if("opt", Some("value"), "default"))
.arg(Arg::from("[arg] 'some arg'").default_value_if("opt", Some("value"), Some("default")))
.try_get_matches_from(vec!["", "--opt", "other"]);
assert!(r.is_ok());
let m = r.unwrap();
@ -234,7 +234,7 @@ fn default_if_arg_present_with_value_no_default_fail() {
fn default_if_arg_present_with_value_no_default_user_override() {
let r = App::new("df")
.arg(Arg::from("--opt [FILE] 'some arg'"))
.arg(Arg::from("[arg] 'some arg'").default_value_if("opt", Some("some"), "default"))
.arg(Arg::from("[arg] 'some arg'").default_value_if("opt", Some("some"), Some("default")))
.try_get_matches_from(vec!["", "--opt", "some", "other"]);
assert!(r.is_ok());
let m = r.unwrap();
@ -249,7 +249,7 @@ fn default_if_arg_present_with_value_no_arg_with_default() {
.arg(
Arg::from("[arg] 'some arg'")
.default_value("first")
.default_value_if("opt", Some("some"), "default"),
.default_value_if("opt", Some("some"), Some("default")),
)
.try_get_matches_from(vec![""]);
assert!(r.is_ok());
@ -265,7 +265,7 @@ fn default_if_arg_present_with_value_no_arg_with_default_fail() {
.arg(
Arg::from("[arg] 'some arg'")
.default_value("first")
.default_value_if("opt", Some("some"), "default"),
.default_value_if("opt", Some("some"), Some("default")),
)
.try_get_matches_from(vec!["", "--opt", "other"]);
assert!(r.is_ok());
@ -281,7 +281,7 @@ fn default_if_arg_present_with_value_with_default() {
.arg(
Arg::from("[arg] 'some arg'")
.default_value("first")
.default_value_if("opt", Some("some"), "default"),
.default_value_if("opt", Some("some"), Some("default")),
)
.try_get_matches_from(vec!["", "--opt", "some"]);
assert!(r.is_ok());
@ -297,7 +297,7 @@ fn default_if_arg_present_with_value_with_default_user_override() {
.arg(
Arg::from("[arg] 'some arg'")
.default_value("first")
.default_value_if("opt", Some("some"), "default"),
.default_value_if("opt", Some("some"), Some("default")),
)
.try_get_matches_from(vec!["", "--opt", "some", "other"]);
assert!(r.is_ok());
@ -313,7 +313,7 @@ fn default_if_arg_present_no_arg_with_value_with_default_user_override() {
.arg(
Arg::from("[arg] 'some arg'")
.default_value("first")
.default_value_if("opt", Some("some"), "default"),
.default_value_if("opt", Some("some"), Some("default")),
)
.try_get_matches_from(vec!["", "other"]);
assert!(r.is_ok());
@ -329,7 +329,7 @@ fn default_if_arg_present_no_arg_with_value_with_default_user_override_fail() {
.arg(
Arg::from("[arg] 'some arg'")
.default_value("first")
.default_value_if("opt", Some("some"), "default"),
.default_value_if("opt", Some("some"), Some("default")),
)
.try_get_matches_from(vec!["", "--opt", "value", "other"]);
assert!(r.is_ok());
@ -338,6 +338,78 @@ fn default_if_arg_present_no_arg_with_value_with_default_user_override_fail() {
assert_eq!(m.value_of("arg").unwrap(), "other");
}
// Unsetting the default
#[test]
fn option_default_if_arg_present_with_value_no_default() {
let r = App::new("df")
.arg(Arg::from("--opt [FILE] 'some arg'"))
.arg(Arg::from("[arg] 'some arg'").default_value_if("opt", Some("value"), Some("default")))
.try_get_matches_from(vec!["", "--opt", "value"]);
assert!(r.is_ok());
let m = r.unwrap();
assert!(m.is_present("arg"));
assert_eq!(m.value_of("arg").unwrap(), "default");
}
#[test]
fn no_default_if_arg_present_with_value_no_default() {
let r = App::new("df")
.arg(Arg::from("--opt [FILE] 'some arg'"))
.arg(Arg::from("[arg] 'some arg'").default_value_if("opt", Some("value"), None))
.try_get_matches_from(vec!["", "--opt", "value"]);
assert!(r.is_ok());
let m = r.unwrap();
assert!(!m.is_present("arg"));
}
#[test]
fn no_default_if_arg_present_with_value_with_default() {
let r = App::new("df")
.arg(Arg::from("--opt [FILE] 'some arg'"))
.arg(
Arg::from("[arg] 'some arg'")
.default_value("default")
.default_value_if("opt", Some("value"), None),
)
.try_get_matches_from(vec!["", "--opt", "value"]);
assert!(r.is_ok());
let m = r.unwrap();
assert!(!m.is_present("arg"));
}
#[test]
fn no_default_if_arg_present_with_value_with_default_user_override() {
let r = App::new("df")
.arg(Arg::from("--opt [FILE] 'some arg'"))
.arg(
Arg::from("[arg] 'some arg'")
.default_value("default")
.default_value_if("opt", Some("value"), None),
)
.try_get_matches_from(vec!["", "--opt", "value", "other"]);
assert!(r.is_ok());
let m = r.unwrap();
assert!(m.is_present("arg"));
assert_eq!(m.value_of("arg").unwrap(), "other");
}
#[test]
fn no_default_if_arg_present_no_arg_with_value_with_default() {
let r = App::new("df")
.arg(Arg::from("--opt [FILE] 'some arg'"))
.arg(
Arg::from("[arg] 'some arg'")
.default_value("default")
.default_value_if("opt", Some("value"), None),
)
.try_get_matches_from(vec!["", "--opt", "other"]);
assert!(r.is_ok());
let m = r.unwrap();
assert!(m.is_present("arg"));
assert_eq!(m.value_of("arg").unwrap(), "default");
}
// Multiple conditions
#[test]
@ -348,7 +420,10 @@ fn default_ifs_arg_present() {
.arg(
Arg::from("[arg] 'some arg'")
.default_value("first")
.default_value_ifs(&[("opt", Some("some"), "default"), ("flag", None, "flg")]),
.default_value_ifs(&[
("opt", Some("some"), Some("default")),
("flag", None, Some("flg")),
]),
)
.try_get_matches_from(vec!["", "--flag"]);
assert!(r.is_ok());
@ -357,6 +432,22 @@ fn default_ifs_arg_present() {
assert_eq!(m.value_of("arg").unwrap(), "flg");
}
#[test]
fn no_default_ifs_arg_present() {
let r = App::new("df")
.arg(Arg::from("--opt [FILE] 'some arg'"))
.arg(Arg::from("--flag 'some arg'"))
.arg(
Arg::from("[arg] 'some arg'")
.default_value("first")
.default_value_ifs(&[("opt", Some("some"), Some("default")), ("flag", None, None)]),
)
.try_get_matches_from(vec!["", "--flag"]);
assert!(r.is_ok());
let m = r.unwrap();
assert!(!m.is_present("arg"));
}
#[test]
fn default_ifs_arg_present_user_override() {
let r = App::new("df")
@ -365,7 +456,10 @@ fn default_ifs_arg_present_user_override() {
.arg(
Arg::from("[arg] 'some arg'")
.default_value("first")
.default_value_ifs(&[("opt", Some("some"), "default"), ("flag", None, "flg")]),
.default_value_ifs(&[
("opt", Some("some"), Some("default")),
("flag", None, Some("flg")),
]),
)
.try_get_matches_from(vec!["", "--flag", "value"]);
assert!(r.is_ok());
@ -382,7 +476,10 @@ fn default_ifs_arg_present_order() {
.arg(
Arg::from("[arg] 'some arg'")
.default_value("first")
.default_value_ifs(&[("opt", Some("some"), "default"), ("flag", None, "flg")]),
.default_value_ifs(&[
("opt", Some("some"), Some("default")),
("flag", None, Some("flg")),
]),
)
.try_get_matches_from(vec!["", "--opt=some", "--flag"]);
assert!(r.is_ok());

View file

@ -454,10 +454,10 @@ fn multiarg() {
let app = clap_app!(claptests =>
(@arg flag: --flag "value")
(@arg multiarg: --multiarg
default_value("flag-unset") default_value_if("flag", None, "flag-set")
default_value("flag-unset") default_value_if("flag", None, Some("flag-set"))
"multiarg")
(@arg multiarg2: --multiarg2
default_value("flag-unset") default_value_if("flag", None, "flag-set",)
default_value("flag-unset") default_value_if("flag", None, Some("flag-set"))
"multiarg2")
);