diff --git a/src/build/arg/mod.rs b/src/build/arg/mod.rs index a5ed40c9..6836cad7 100644 --- a/src/build/arg/mod.rs +++ b/src/build/arg/mod.rs @@ -110,7 +110,7 @@ pub struct Arg<'help> { pub(crate) validator_os: Option>>>, pub(crate) val_delim: Option, 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)>, 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( self, arg_id: T, val: Option<&'help str>, - default: &'help str, + default: impl Into>, ) -> 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>, ) -> 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( mut self, - ifs: &[(T, Option<&'help str>, &'help str)], + ifs: &[(T, Option<&'help str>, impl Into> + 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( mut self, - ifs: &[(T, Option<&'help OsStr>, &'help OsStr)], + ifs: &[( + T, + Option<&'help OsStr>, + impl Into> + 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 diff --git a/src/parse/parser.rs b/src/parse/parser.rs index e448a4a9..f77105fc 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -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; } } diff --git a/tests/default_vals.rs b/tests/default_vals.rs index c361f16d..ec8a3e45 100644 --- a/tests/default_vals.rs +++ b/tests/default_vals.rs @@ -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")