Repository: clap-rs/clap (language: rust)

Issue:
Ability to apply a setting to all args
### Use Cases

- "in our application tests [we need] to set an argument setting on all arguments programmatically just in the test context, and we had a lot of arguments so safest, cleanest, and easiest, to be able to set it with this new function.  Essentially: `app.mut_args(|arg| Some(arg.setting(ArgSettings::HideEnvValues)))`".  See #2966
- [`AppSettings::AllArgsOverrideSelf`](https://docs.rs/clap/3.0.0-beta.5/clap/enum.AppSettings.html#variant.AllArgsOverrideSelf) is arg configuration that we want to apply everywhere.  Rather than creating specialized arguments, is there a different way?
- One option for sorting arguments in help after https://github.com/clap-rs/clap/issues/2808 is to set the same display order on all arguments
- [`App::help_heading`](https://docs.rs/clap/3.0.0-beta.5/clap/struct.App.html#method.help_heading) allows applying `Arg:help_heading` to all future arguments to take advantage of the natural grouping of arguments when adding them.

Note: in some cases, users will want this to apply globally (ie to all subcommand args)

### Solution Options

*Note: this isn't a "pick one" but we can use a mixture*

- Specialize `Arg` settings in `App` (ie **do nothing**), like
  - `AppSettings::AllArgsOverrideSelf`
  - `App::help_heading`
  - A `App::next_display_order` for #2808
  - **Con::** design / implementation is needed for each solution
  - **Con:** each solution ends up different than the others
- Direct users to use reflection + `mut_arg` to manually apply a setting (ie **do nothing**, see #2976)
  - This ended up being what we used instead of #2966 which led to it being reverted in #2994
  - **Con:** Not ergonomic for `clap_derive` users (instead of calling `Parser`, they have to call `IntoApp` and `FromArgMarches`)
- Provide a `App:args_mut(&mut self) -> impl Iterator<Item = &mut Arg>`.
  - **Con:** Not ergonomic for `clap_derive` users (instead of calling `Parser`, they have to call `IntoApp` and `FromArgMarches`)
- Provide a `App::arg_setting`
  - **Con:** Only works with `ArgSettings`, so doesn't help with `AppSettings::AllArgsOverrideSelf` or `display_order`
  - **Con:** We are deprecating settings (#2717)
- `mut_args`, modeled after `mut_arg` (see #2966, #2994)
  - **Con:** The return type is confusing (should it be just `bool`?)
  - **Con:** Policy of when to return what is unclear
- Like `mut_args` above but `Fn` doesn't have a return type
  - `mut_arg` assumes you are calling it on an arg you intend to keep, so an implicit `--version` becomes explicit, making it show up when you might have disabled it
  - #2966 carried this policy forward but provided a return value to avoid adding disabled args
  - This solution assumes that mass-setting an argument is argument-agnostic and it does not imply the intention of using that argument, so it keeps `--version` implicit
- `App::default_arg(arg: Arg)` or `App::mut_default_arg` is layered underneath each arg
  - We'd have to implement support for tracking what arguments are explicitly set or not to know what is set in the "default arg" and what not to override in each arg
  - Not all settings should be layered (like `alias` or `long`)
- `App::override_arg(arg: Arg)` or `App::mut_override_arg` is layered on top of each arg
  - We'd have to implement support for tracking what arguments are explicitly set or not to know what is set in the "override arg"
  - Not all settings should be layered (like `alias` or `long`)

For when to apply these to subcommands, we could
- Have a `global_` variant of any function, like today's ``global_settings`
- Accept a `global: bool` or `scope: enum Scope { Local, Inherit }` argument in any function
- Layer Subcommands on top of their immediate parent App (see also https://github.com/clap-rs/clap/issues/2717)
  - Like with default and override args, this will require tracking what configuration is explicitly set
  - Not all settings should be layered, like `alias` (used for subcommands)


Discussion / hints:
@repi in case you have any further thoughts on this topic
> Only works with ArgSettings, so doesn't help with AppSettings::AllArgsOverrideSelf or display_order

Hmm.. it would work if we make an `ArgSetting::ArgOverrideSelf` and then `AllArgsOverrideSelf` is basically setting that on all args in the app.

I am not sure what the scenario with display_order is though.
> Hmm.. it would work if we make an ArgSetting::ArgOverrideSelf and then AllArgsOverrideSelf is basically setting that on all args in the app.

I keep going back and forth on whether that should be the default behavior.

> I am not sure what the scenario with display_order is though.

From the use cases:

> One option for sorting arguments in help after https://github.com/clap-rs/clap/issues/2808 is to set the same display order on all arguments

Fix patch:
diff --git a/clap_builder/src/builder/command.rs b/clap_builder/src/builder/command.rs
index b2efdbc7ebb..48ee1a9e2f4 100644
--- a/clap_builder/src/builder/command.rs
+++ b/clap_builder/src/builder/command.rs
@@ -252,6 +252,52 @@ impl Command {
         self
     }
 
+    /// Allows one to mutate all [`Arg`]s after they've been added to a [`Command`].
+    ///
+    /// This does not affect the built-in `--help` or `--version` arguments.
+    ///
+    /// # Examples
+    ///
+    #[cfg_attr(feature = "string", doc = "```")]
+    #[cfg_attr(not(feature = "string"), doc = "```ignore")]
+    /// # use clap_builder as clap;
+    /// # use clap::{Command, Arg, ArgAction};
+    ///
+    /// let mut cmd = Command::new("foo")
+    ///     .arg(Arg::new("bar")
+    ///         .long("bar")
+    ///         .action(ArgAction::SetTrue))
+    ///     .arg(Arg::new("baz")
+    ///         .long("baz")
+    ///         .action(ArgAction::SetTrue))
+    ///     .mut_args(|a| {
+    ///         if let Some(l) = a.get_long().map(|l| format!("prefix-{l}")) {
+    ///             a.long(l)
+    ///         } else {
+    ///             a
+    ///         }
+    ///     });
+    ///
+    /// let res = cmd.try_get_matches_from_mut(vec!["foo", "--bar"]);
+    ///
+    /// // Since we changed `bar`'s long to "prefix-bar" this should err as there
+    /// // is no `--bar` anymore, only `--prefix-bar`.
+    ///
+    /// assert!(res.is_err());
+    ///
+    /// let res = cmd.try_get_matches_from_mut(vec!["foo", "--prefix-bar"]);
+    /// assert!(res.is_ok());
+    /// ```
+    #[must_use]
+    #[cfg_attr(debug_assertions, track_caller)]
+    pub fn mut_args<F>(mut self, f: F) -> Self
+    where
+        F: FnMut(Arg) -> Arg,
+    {
+        self.args.mut_args(f);
+        self
+    }
+
     /// Allows one to mutate a [`Command`] after it's been added as a subcommand.
     ///
     /// This can be useful for modifying auto-generated arguments of nested subcommands with
diff --git a/clap_builder/src/mkeymap.rs b/clap_builder/src/mkeymap.rs
index e9d6ab8442e..301c7bc566c 100644
--- a/clap_builder/src/mkeymap.rs
+++ b/clap_builder/src/mkeymap.rs
@@ -123,6 +123,15 @@ impl MKeyMap {
         self.args.iter_mut()
     }
 
+    /// Mutate every argument.
+    pub(crate) fn mut_args<F>(&mut self, f: F)
+    where
+        F: FnMut(Arg) -> Arg,
+    {
+        let mut args = std::mem::take(&mut self.args);
+        self.args.extend(args.drain(..).map(f));
+    }
+
     /// We need a lazy build here since some we may change args after creating
     /// the map, you can checkout who uses `args_mut`.
     pub(crate) fn _build(&mut self) {


Test patch: