rhg: Use clap’s support for global CLI arguments
authorSimon Sapin <simon.sapin@octobus.net>
Fri, 26 Feb 2021 12:16:43 +0100
changeset 46608 4e4c70401028
parent 46607 e9901d01d135
child 46609 25b1610f8534
rhg: Use clap’s support for global CLI arguments By default, clap only accepts app-level arguments (as opposed to sub-command level) to be specified before a sub-command: `rhg -R ./foo log`. Specifying them after would be rejected: `rhg log -R ./foo`. Previously we worked around that by registering global arguments both at the app level and on each sub-command, but that required looking for their value in two places. It turns out that Clap has built-in support for what we want to do, so let’s use it. Also, Clap "settings" turn out to be either global or not too. Let’s make `AllowInvalidUtf8` apply to sub-commands too. Differential Revision: https://phab.mercurial-scm.org/D10080
rust/rhg/src/main.rs
--- a/rust/rhg/src/main.rs	Wed Feb 03 16:33:10 2021 -0800
+++ b/rust/rhg/src/main.rs	Fri Feb 26 12:16:43 2021 +0100
@@ -15,39 +15,38 @@
 mod ui;
 use error::CommandError;
 
-fn add_global_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
-    app.arg(
-        Arg::with_name("repository")
-            .help("repository root directory")
-            .short("-R")
-            .long("--repository")
-            .value_name("REPO")
-            .takes_value(true),
-    )
-    .arg(
-        Arg::with_name("config")
-            .help("set/override config option (use 'section.name=value')")
-            .long("--config")
-            .value_name("CONFIG")
-            .takes_value(true)
-            // Ok: `--config section.key1=val --config section.key2=val2`
-            .multiple(true)
-            // Not ok: `--config section.key1=val section.key2=val2`
-            .number_of_values(1),
-    )
-}
-
 fn main_with_result(
     ui: &ui::Ui,
     process_start_time: &blackbox::ProcessStartTime,
 ) -> Result<(), CommandError> {
     env_logger::init();
     let app = App::new("rhg")
-        .setting(AppSettings::AllowInvalidUtf8)
+        .global_setting(AppSettings::AllowInvalidUtf8)
         .setting(AppSettings::SubcommandRequired)
         .setting(AppSettings::VersionlessSubcommands)
+        .arg(
+            Arg::with_name("repository")
+                .help("repository root directory")
+                .short("-R")
+                .long("--repository")
+                .value_name("REPO")
+                .takes_value(true)
+                // Both ok: `hg -R ./foo log` or `hg log -R ./foo`
+                .global(true),
+        )
+        .arg(
+            Arg::with_name("config")
+                .help("set/override config option (use 'section.name=value')")
+                .long("--config")
+                .value_name("CONFIG")
+                .takes_value(true)
+                .global(true)
+                // Ok: `--config section.key1=val --config section.key2=val2`
+                .multiple(true)
+                // Not ok: `--config section.key1=val section.key2=val2`
+                .number_of_values(1),
+        )
         .version("0.0.1");
-    let app = add_global_args(app);
     let app = add_subcommand_args(app);
 
     let matches = app.clone().get_matches_safe()?;
@@ -58,26 +57,15 @@
     let subcommand_args = subcommand_matches
         .expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired");
 
-    // Global arguments can be in either based on e.g. `hg -R ./foo log` v.s.
-    // `hg log -R ./foo`
-    let value_of_global_arg = |name| {
-        subcommand_args
-            .value_of_os(name)
-            .or_else(|| matches.value_of_os(name))
-    };
-    // For arguments where multiple occurences are allowed, return a
-    // possibly-iterator of all values.
-    let values_of_global_arg = |name: &str| {
-        let a = matches.values_of_os(name).into_iter().flatten();
-        let b = subcommand_args.values_of_os(name).into_iter().flatten();
-        a.chain(b)
-    };
-
-    let config_args = values_of_global_arg("config")
+    let config_args = matches
+        .values_of_os("config")
+        // Turn `Option::None` into an empty iterator:
+        .into_iter()
+        .flatten()
         .map(hg::utils::files::get_bytes_from_os_str);
     let non_repo_config = &hg::config::Config::load(config_args)?;
 
-    let repo_path = value_of_global_arg("repository").map(Path::new);
+    let repo_path = matches.value_of_os("repository").map(Path::new);
     let repo = match Repo::find(non_repo_config, repo_path) {
         Ok(repo) => Ok(repo),
         Err(RepoError::NotFound { at }) if repo_path.is_none() => {
@@ -141,7 +129,7 @@
         fn add_subcommand_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
             app
             $(
-                .subcommand(add_global_args(commands::$command::args()))
+                .subcommand(commands::$command::args())
             )+
         }