diff --git a/src/app/mod.rs b/src/app/mod.rs index a1097a2dcf5..9bcd5310c94 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -491,29 +491,27 @@ impl<'a, 'b> App<'a, 'b> { self } - /// Enables multiple command, or [`SubCommand`], level settings + /// Disables a single command, or [`SubCommand`], level setting. /// /// See [`AppSettings`] for a full list of possibilities and examples. /// /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, AppSettings}; + /// # use clap::{App, AppSettings}; /// App::new("myprog") - /// .settings(&[AppSettings::SubcommandRequired, - /// AppSettings::WaitOnError]) + /// .unset_setting(AppSettings::ColorAuto) /// # ; /// ``` /// [`SubCommand`]: ./struct.SubCommand.html /// [`AppSettings`]: ./enum.AppSettings.html - pub fn settings(mut self, settings: &[AppSettings]) -> Self { - for s in settings { - self.settings.set(*s); - } + /// [global]: ./struct.App.html#method.global_setting + pub fn unset_setting(mut self, setting: AppSettings) -> Self { + self.settings.unset(setting); self } - /// Enables a single setting that is propagated down through all child [`SubCommand`]s. + /// Enables a single setting that is propagated down through all child subcommands. /// /// See [`AppSettings`] for a full list of possibilities and examples. /// @@ -527,7 +525,6 @@ impl<'a, 'b> App<'a, 'b> { /// .global_setting(AppSettings::SubcommandRequired) /// # ; /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html /// [`AppSettings`]: ./enum.AppSettings.html pub fn global_setting(mut self, setting: AppSettings) -> Self { self.settings.set(setting); @@ -535,32 +532,7 @@ impl<'a, 'b> App<'a, 'b> { self } - /// Enables multiple settings which are propagated *down* through all child [`SubCommand`]s. - /// - /// See [`AppSettings`] for a full list of possibilities and examples. - /// - /// **NOTE**: The setting is *only* propagated *down* and not up through parent commands. - /// - /// # Examples - /// - /// ```no_run - /// # use clap::{App, Arg, AppSettings}; - /// App::new("myprog") - /// .global_settings(&[AppSettings::SubcommandRequired, - /// AppSettings::ColoredHelp]) - /// # ; - /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html - /// [`AppSettings`]: ./enum.AppSettings.html - pub fn global_settings(mut self, settings: &[AppSettings]) -> Self { - for s in settings { - self.settings.set(*s); - self.g_settings.set(*s) - } - self - } - - /// Disables a single command, or [`SubCommand`], level setting. + /// Disables a global setting, and stops propagating down to child subcommands. /// /// See [`AppSettings`] for a full list of possibilities and examples. /// @@ -571,44 +543,17 @@ impl<'a, 'b> App<'a, 'b> { /// ```no_run /// # use clap::{App, AppSettings}; /// App::new("myprog") - /// .unset_setting(AppSettings::ColorAuto) + /// .unset_global_setting(AppSettings::ColorAuto) /// # ; /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html /// [`AppSettings`]: ./enum.AppSettings.html /// [global]: ./struct.App.html#method.global_setting - pub fn unset_setting(mut self, setting: AppSettings) -> Self { + pub fn unset_global_setting(mut self, setting: AppSettings) -> Self { self.settings.unset(setting); self.g_settings.unset(setting); self } - /// Disables multiple command, or [`SubCommand`], level settings. - /// - /// See [`AppSettings`] for a full list of possibilities and examples. - /// - /// **NOTE:** The setting being unset will be unset from both local and [global] settings - /// - /// # Examples - /// - /// ```no_run - /// # use clap::{App, AppSettings}; - /// App::new("myprog") - /// .unset_settings(&[AppSettings::ColorAuto, - /// AppSettings::AllowInvalidUtf8]) - /// # ; - /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html - /// [`AppSettings`]: ./enum.AppSettings.html - /// [global]: ./struct.App.html#method.global_setting - pub fn unset_settings(mut self, settings: &[AppSettings]) -> Self { - for s in settings { - self.settings.unset(*s); - self.g_settings.unset(*s); - } - self - } - /// Sets the terminal width at which to wrap help messages. Defaults to `120`. Using `0` will /// ignore terminal widths and use source formatting. /// @@ -700,16 +645,22 @@ impl<'a, 'b> App<'a, 'b> { /// ```no_run /// # use clap::{App, Arg}; /// App::new("myprog") - /// .args( - /// &[Arg::from_usage("[debug] -d 'turns on debugging info'"), - /// Arg::with_name("input").index(1).help("the input file to use")] - /// ) + /// .args(&[ + /// Arg::from_usage("[debug] -d 'turns on debugging info'"), + /// Arg::with_name("input").index(1).help("the input file to use") + /// ]) /// # ; /// ``` /// [arguments]: ./struct.Arg.html - pub fn args(mut self, args: &[Arg<'a, 'b>]) -> Self { - for arg in args { - self.args.push(arg.clone()); + pub fn args(mut self, args: I) -> Self + where + I: IntoIterator, + T: Into>, + { + // @TODO @perf @p4 @v3-beta: maybe extend_from_slice would be possible and perform better? + // But that may also not let us do `&["-a 'some'", "-b 'other']` because of not Into + for arg in args.into_iter() { + self.args.push(arg.into()); } self } @@ -991,6 +942,43 @@ impl<'a, 'b> App<'a, 'b> { self } + /// Allows one to mutate an [`Arg`] after it's been added to an `App`. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{App, Arg}; + /// + /// let mut app = App::new("foo") + /// .arg(Arg::with_name("bar") + /// .short("b")) + /// .mut_arg("bar", |a| a.short("B")); + /// + /// let res = app.try_get_matches_from_mut(vec!["foo", "-b"]); + /// + /// // Since we changed `bar`'s short to "B" this should err as there + /// // is no `-b` anymore, only `-B` + /// + /// assert!(res.is_err()); + /// + /// let res = app.try_get_matches_from_mut(vec!["foo", "-B"]); + /// assert!(res.is_ok()); + /// ``` + /// [`Arg`]: ./struct.Arg.html + pub fn mut_arg(mut self, arg: &'a str, f: F) -> Self + where F: FnOnce(Arg<'a, 'b>) -> Arg<'a, 'b> { + let i = self.args.iter().enumerate().filter(|&(i, a)| a.name == arg).map(|(i, _)| i).next(); + let a = if let Some(idx) = i { + let mut a = self.args.swap_remove(idx); + f(a) + } else { + let mut a = Arg::with_name(arg); + f(a) + }; + self.args.push(a); + self + } + /// Prints the full help message to [`io::stdout()`] using a [`BufWriter`] using the same /// method as if someone ran `-h` to request the help message /// @@ -1153,6 +1141,40 @@ impl<'a, 'b> App<'a, 'b> { /// [`env::args_os`]: https://doc.rust-lang.org/std/env/fn.args_os.html pub fn get_matches(self) -> ArgMatches<'a> { self.get_matches_from(&mut env::args_os()) } + /// Starts the parsing process, just like [`App::get_matches`] but doesn't consume the `App` + /// + /// # Examples + /// + /// ```no_run + /// # use clap::{App, Arg}; + /// let mut app = App::new("myprog") + /// // Args and options go here... + /// ; + /// let matches = app.get_matches_mut(); + /// ``` + /// [`env::args_os`]: https://doc.rust-lang.org/std/env/fn.args_os.html + /// [`App::get_matches`]: ./struct.App.html#method.get_matches + pub fn get_matches_mut(&mut self) -> ArgMatches<'a> { + self.try_get_matches_from_mut(&mut env::args_os()).unwrap_or_else(|e| { + // Otherwise, write to stderr and exit + if e.use_stderr() { + wlnerr!("{}", e.message); + if self.settings.is_set(AppSettings::WaitOnError) { + wlnerr!("\nPress [ENTER] / [RETURN] to continue..."); + let mut s = String::new(); + let i = io::stdin(); + i.lock().read_line(&mut s).unwrap(); + } + drop(self); + drop(e); + process::exit(1); + } + + drop(self); + e.exit() + }) + } + /// Starts the parsing process. This method will return a [`clap::Result`] type instead of exiting /// the process on failed parse. By default this method gets matches from [`env::args_os`] /// @@ -1709,8 +1731,39 @@ impl<'a, 'b> App<'a, 'b> { // Deprecations impl<'a, 'b> App<'a, 'b> { - /// Deprecated - #[deprecated(since="2.14.1", note="Can never work; use explicit App::author() and App::version() calls instead. Will be removed in v3.0-beta")] + /// **Deprecated:** Use `App::global_setting( SettingOne | SettingTwo )` instead + #[deprecated(since="2.33.0", note="Use `App::global_setting( SettingOne | SettingTwo )` instead")] + pub fn global_settings(mut self, settings: &[AppSettings]) -> Self { + for s in settings { + self.settings.set(*s); + self.g_settings.set(*s) + } + self + } + + /// **Deprecated:** Use `App::setting( SettingOne | SettingTwo )` instead + #[deprecated(since="2.33.0", note="Use `App::setting( SettingOne | SettingTwo )` instead")] + pub fn settings(mut self, settings: &[AppSettings]) -> Self { + for s in settings { + self.settings.set(*s); + } + self + } + + + /// **Deprecated:** Use `App::unset_setting( SettingOne | SettingTwo )` instead + #[deprecated(since="2.33.0", note="Use `App::unset_setting( SettingOne | SettingTwo )` instead")] + pub fn unset_settings(mut self, settings: &[AppSettings]) -> Self { + for s in settings { + self.settings.unset(*s); + self.g_settings.unset(*s); + } + self + } + + /// **Deprecated:** Use explicit `App::author()` and `App::version()` calls instead. + #[deprecated(since="2.14.1", note="Can never work; use explicit App::author() and \ + App::version() calls instead. Will be removed in v3.0-beta")] pub fn with_defaults>(n: S) -> Self { App { name: n.into(), @@ -1720,12 +1773,12 @@ impl<'a, 'b> App<'a, 'b> { } } - /// **Deprecated** + /// **Deprecated:** Use #[deprecated(since="2.30.0", note="Use serde instead. Will be removed in v3.0-beta")] #[cfg(feature = "yaml")] pub fn from_yaml(yaml: &'a Yaml) -> App<'a, 'a> { App::from(yaml) } - /// **Deprecated** + /// **Deprecated:** Use #[deprecated(since="2.30.0", note="Use `App::mut_arg(\"help\", |a| a.short(\"H\"))` instead. Will be removed in v3.0-beta")] pub fn help_short + 'b>(mut self, s: S) -> Self { let c = s.as_ref() @@ -1737,7 +1790,7 @@ impl<'a, 'b> App<'a, 'b> { self } - /// **Deprecated** + /// **Deprecated:** Use #[deprecated(since="2.30.0", note="Use `App::mut_arg(\"version\", |a| a.short(\"v\"))` instead. Will be removed in v3.0-beta")] pub fn version_short>(mut self, s: S) -> Self { let c = s.as_ref() @@ -1749,49 +1802,49 @@ impl<'a, 'b> App<'a, 'b> { self } - /// **Deprecated** + /// **Deprecated:** Use #[deprecated(since="2.30.0", note="Use `App::mut_arg(\"help\", |a| a.help(\"Some message\"))` instead. Will be removed in v3.0-beta")] pub fn help_message>(mut self, s: S) -> Self { self.help_message = Some(s.into()); self } - /// **Deprecated** + /// **Deprecated:** Use #[deprecated(since="2.30.0", note="Use `App::mut_arg(\"version\", |a| a.short(\"Some message\"))` instead. Will be removed in v3.0-beta")] pub fn version_message>(mut self, s: S) -> Self { self.version_message = Some(s.into()); self } - /// **Deprecated** + /// **Deprecated:** Use #[deprecated(since="2.30.0", note="Renamed to `App::override_usage`. Will be removed in v3.0-beta")] pub fn usage>(mut self, usage: S) -> Self { self.usage_str = Some(usage.into()); self } - /// **Deprecated** + /// **Deprecated:** Use #[deprecated(since="2.30.0", note="Renamed to `App::override_help`. Will be removed in v3.0-beta")] pub fn help>(mut self, help: S) -> Self { self.help_str = Some(help.into()); self } - /// **Deprecated** + /// **Deprecated:** Use #[deprecated(since="2.30.0", note="Renamed to `App::help_template`. Will be removed in v3.0-beta")] pub fn template>(mut self, s: S) -> Self { self.template = Some(s.into()); self } - /// **Deprecated** + /// **Deprecated:** Use #[deprecated(since="2.30.0", note="Use `App::arg(Arg::from(&str)` instead. Will be removed in v3.0-beta")] pub fn arg_from_usage(mut self, usage: &'a str) -> Self { self.args.push(Arg::from_usage(usage)); self } - /// **Deprecated** + /// **Deprecated:** Use #[deprecated(since="2.30.0", note="Use `App::args(&str)` instead. Will be removed in v3.0-beta")] pub fn args_from_usage(mut self, usage: &'a str) -> Self { for line in usage.lines() { @@ -1804,7 +1857,7 @@ impl<'a, 'b> App<'a, 'b> { self } - /// **Deprecated** + /// **Deprecated:** Use #[allow(deprecated)] #[deprecated(since="2.30.0", note="Use `clap_completions crate and clap_completions::generate` instead. Will be removed in v3.0-beta")] pub fn gen_completions, S: Into>( @@ -1831,7 +1884,7 @@ impl<'a, 'b> App<'a, 'b> { self.gen_completions_to(bin_name.into(), for_shell, &mut file) } - /// **Deprecated** + /// **Deprecated:** Use #[deprecated(since="2.30.0", note="Use `clap_completions crate and clap_completions::generate_to` instead. Will be removed in v3.0-beta")] pub fn gen_completions_to>( &mut self, @@ -1848,14 +1901,14 @@ impl<'a, 'b> App<'a, 'b> { ComplGen::new(self).generate(for_shell, buf) } - /// **Deprecated** + /// **Deprecated:** Use #[deprecated(since="2.30.0", note="Renamed `App::try_get_matches` to be consistent with Rust naming conventions. Will be removed in v3.0-beta")] pub fn get_matches_safe(self) -> ClapResult> { // Start the parsing self.try_get_matches_from(&mut env::args_os()) } - /// **Deprecated** + /// **Deprecated:** Use #[deprecated(since="2.30.0", note="Renamed `App::try_get_matches_from` to be consistent with Rust naming conventions. Will be removed in v3.0-beta")] pub fn get_matches_from_safe(mut self, itr: I) -> ClapResult> where @@ -1865,7 +1918,7 @@ impl<'a, 'b> App<'a, 'b> { self.try_get_matches_from_mut(itr) } - /// **Deprecated** + /// **Deprecated:** Use #[deprecated(since="2.30.0", note="Renamed `App::try_get_matches_from_mut` to be consistent with Rust naming conventions. Will be removed in v3.0-beta")] pub fn get_matches_from_safe_borrow(&mut self, itr: I) -> ClapResult> where diff --git a/tests/app_settings.rs b/tests/app_settings.rs index 24281d0f24a..a4b2eeeb544 100644 --- a/tests/app_settings.rs +++ b/tests/app_settings.rs @@ -498,11 +498,12 @@ fn leading_double_hyphen_trailingvararg() { #[test] fn unset_setting() { - let m = App::new("unset_setting"); - assert!(m.is_set(AppSettings::AllowInvalidUtf8)); + let m = App::new("unset_setting") + .setting(AppSettings::AllArgsOverrideSelf); + assert!(m.is_set(AppSettings::AllArgsOverrideSelf)); - let m = m.unset_setting(AppSettings::AllowInvalidUtf8); - assert!(!m.is_set(AppSettings::AllowInvalidUtf8)); + let m = m.unset_setting(AppSettings::AllArgsOverrideSelf); + assert!(!m.is_set(AppSettings::AllArgsOverrideSelf)); } #[test] diff --git a/tests/tests.rs b/tests/tests.rs index c6900dffd41..e4ef1c60dfe 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -304,7 +304,7 @@ fn create_app() { #[test] fn add_multiple_arg() { let _ = App::new("test") - .args(&mut [ + .args(&[ Arg::with_name("test").short("s"), Arg::with_name("test2").short("l"), ]) diff --git a/v3_changes.md b/v3_changes.md index 41e4a29b741..9cc1456ed45 100644 --- a/v3_changes.md +++ b/v3_changes.md @@ -7,35 +7,62 @@ * In usage parser, for options `[name]... --option [val]` results in `ArgSettings::MultipleOccurrences` but `--option [val]...` results in `ArgSettings::MultipleValues` *and* `ArgSettings::MultipleOccurrences`. Before both resulted in the same thing * Allow empty values no longer default * UseValueDelimiter no longer the default -* Multpiple delima fixed (vals vs occurrences) +* Multiple delima fixed (vals vs occurrences) +* Ability to mutate args once they've been added to an `App` +* `App::args` and `App::arg` are more generic +* Can unset global settings + +# How to Upgrade + +### If you use `Arg::multiple(true)` + # Deprecations ## Simple Renames -- `App::usage` -> `App::override_usage` -- `App::help` -> `App::override_help` -- `App::template` -> `App::help_template` +### App + - `App::get_matches_safe` -> `App::try_get_matches` - `App::get_matches_from_safe` -> `App::try_get_matches_from` - `App::get_matches_safe_borrow` -> `App::try_get_matches_from_mut` +- `App::usage` -> `App::override_usage` +- `App::help` -> `App::override_help` +- `App::template` -> `App::help_template` + +### Arg + - `Arg::unset` -> `Arg::unset_setting` - `Arg::set` -> `Arg::setting` -## App Methods +## Structural Changes +### App - `App::version_message` -> `App::mut_arg` - `App::version_short` -> `App::mut_arg` - `App::help_message` -> `App::mut_arg` - `App::help_short` -> `App::mut_arg` - `App::args_from_usage` -> `App::args(&str)` -- `App::arg_from_usage` -> `App::arg(Arg::from)` +- `App::arg_from_usage` -> `App::arg(&str)` - `App::write_help` -> `&self` -> `&mut self` (#808) - `App::gen_completions` -> `clap_completions::generate` - `App::gen_completions_to` -> `clap_completions::generate_to` +- `App::settings` -> `App::setting(Setting1 | Setting2)` +- `App::unset_settings` -> `App::unset_setting(Setting1 | Setting2)` +- `App::global_settings` -> `App::global_setting(Setting1 | Setting2)` + +### Arg + - `Arg::from_usage` -> `Arg::from(&str)` -# Additions +# Additional APIs + +## App + +* `App::mut_arg` +* `App::unset_global_setting` + +## Arg