feat(build/arg/mod.rs): default_vals_if supports an Option as the default

This commit is contained in:
Steven Engler 2021-01-17 02:28:04 -05:00 committed by ldm0
parent 42c03775b5
commit 6626365359
3 changed files with 134 additions and 14 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>,
@ -2693,7 +2693,8 @@ impl<'help> Arg<'help> {
/// Specifies the value of the argument if `arg` has been used at runtime. If `val` is set to
/// `None`, `arg` only needs to be present. If `val` is set to `"some-val"` then `arg` must be
/// present at runtime **and** have the value `val`.
/// present at runtime **and** have the value `val`. Setting `default` to `None` removes any
/// existing default.
///
/// **NOTE:** This setting is perfectly compatible with [`Arg::default_value`] but slightly
/// different. `Arg::default_value` *only* takes affect when the user has not provided this arg
@ -2723,7 +2724,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 +2741,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 +2759,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 +2778,44 @@ 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);
/// ```
///
/// We can also remove a default if the flag was set.
///
/// ```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: impl Into<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.into().map(|s| OsStr::new(s)),
)
}
/// Provides a conditional default value in the exact same manner as [`Arg::default_value_if`]
@ -2804,11 +2827,11 @@ impl<'help> Arg<'help> {
mut self,
arg_id: T,
val: Option<&'help OsStr>,
default: &'help OsStr,
default: impl Into<Option<&'help OsStr>>,
) -> Self {
let l = self.default_vals_ifs.len();
self.default_vals_ifs
.insert(l, (arg_id.into(), val, default));
.insert(l, (arg_id.into(), val, default.into()));
self.takes_value(true)
}
@ -2898,10 +2921,12 @@ 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>, impl Into<Option<&'help str>> + Copy)],
) -> Self {
for (arg, val, default) in ifs {
self = self.default_value_if_os(arg, val.map(OsStr::new), OsStr::new(*default));
let default: Option<&'help str> = (*default).into();
self =
self.default_value_if_os(arg, val.map(OsStr::new), default.map(|s| OsStr::new(s)));
}
self
}
@ -2913,9 +2938,14 @@ 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>,
impl Into<Option<&'help OsStr>> + Copy,
)],
) -> Self {
for (arg, val, default) in ifs {
let default: Option<&'help OsStr> = (*default).into();
self = self.default_value_if_os(arg.key(), *val, default);
}
self

View file

@ -1427,7 +1427,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

@ -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]
@ -357,6 +429,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")