changeset 46554:95d37db31479

rhg: Replace subcommand boilerplate with a macro This removes some repetition, and will avoid additional repetition in the next commit. Differential Revision: https://phab.mercurial-scm.org/D9969
author Simon Sapin <simon.sapin@octobus.net>
date Mon, 08 Feb 2021 21:28:52 +0100
parents 1ecaf09d9964
children d8730ff51d5a
files rust/rhg/src/commands.rs rust/rhg/src/main.rs
diffstat 2 files changed, 51 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/rust/rhg/src/commands.rs	Mon Feb 08 21:05:36 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-pub mod cat;
-pub mod debugdata;
-pub mod debugrequirements;
-pub mod files;
-pub mod root;
--- a/rust/rhg/src/main.rs	Mon Feb 08 21:05:36 2021 +0100
+++ b/rust/rhg/src/main.rs	Mon Feb 08 21:28:52 2021 +0100
@@ -4,7 +4,6 @@
 use clap::ArgMatches;
 use format_bytes::format_bytes;
 
-mod commands;
 mod error;
 mod exitcode;
 mod ui;
@@ -16,23 +15,27 @@
         .setting(AppSettings::AllowInvalidUtf8)
         .setting(AppSettings::SubcommandRequired)
         .setting(AppSettings::VersionlessSubcommands)
-        .version("0.0.1")
-        .subcommand(commands::root::args())
-        .subcommand(commands::files::args())
-        .subcommand(commands::cat::args())
-        .subcommand(commands::debugdata::args())
-        .subcommand(commands::debugrequirements::args());
-
-    let matches = app.clone().get_matches_safe().unwrap_or_else(|err| {
-        let _ = ui::Ui::new().writeln_stderr_str(&err.message);
-        std::process::exit(exitcode::UNIMPLEMENTED)
-    });
+        .version("0.0.1");
+    let app = add_subcommand_args(app);
 
     let ui = ui::Ui::new();
 
-    let command_result = match_subcommand(matches, &ui);
+    let matches = app.clone().get_matches_safe().unwrap_or_else(|err| {
+        let _ = ui.writeln_stderr_str(&err.message);
+        std::process::exit(exitcode::UNIMPLEMENTED)
+    });
+    let (subcommand_name, subcommand_matches) = matches.subcommand();
+    let run = subcommand_run_fn(subcommand_name)
+        .expect("unknown subcommand name from clap despite AppSettings::SubcommandRequired");
+    let args = subcommand_matches
+        .expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired");
 
-    let exit_code = match command_result {
+    let result = (|| -> Result<(), CommandError> {
+        let config = hg::config::Config::load()?;
+        run(&ui, &config, args)
+    })();
+
+    let exit_code = match result {
         Ok(_) => exitcode::OK,
 
         // Exit with a specific code and no error message to let a potential
@@ -52,22 +55,40 @@
     std::process::exit(exit_code)
 }
 
-fn match_subcommand(
-    matches: ArgMatches,
-    ui: &ui::Ui,
-) -> Result<(), CommandError> {
-    let config = hg::config::Config::load()?;
+macro_rules! subcommands {
+    ($( $command: ident )+) => {
+        mod commands {
+            $(
+                pub mod $command;
+            )+
+        }
+
+        fn add_subcommand_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
+            app
+            $(
+                .subcommand(commands::$command::args())
+            )+
+        }
 
-    match matches.subcommand() {
-        ("root", Some(matches)) => commands::root::run(ui, &config, matches),
-        ("files", Some(matches)) => commands::files::run(ui, &config, matches),
-        ("cat", Some(matches)) => commands::cat::run(ui, &config, matches),
-        ("debugdata", Some(matches)) => {
-            commands::debugdata::run(ui, &config, matches)
+        fn subcommand_run_fn(name: &str) -> Option<fn(
+            &ui::Ui,
+            &hg::config::Config,
+            &ArgMatches,
+        ) -> Result<(), CommandError>> {
+            match name {
+                $(
+                    stringify!($command) => Some(commands::$command::run),
+                )+
+                _ => None,
+            }
         }
-        ("debugrequirements", Some(matches)) => {
-            commands::debugrequirements::run(ui, &config, matches)
-        }
-        _ => unreachable!(), // Because of AppSettings::SubcommandRequired,
-    }
+    };
 }
+
+subcommands! {
+    cat
+    debugdata
+    debugrequirements
+    files
+    root
+}