Patchwork D9969: rhg: Replace subcommand boilerplate with a macro

login
register
mail settings
Submitter phabricator
Date Feb. 8, 2021, 10:44 p.m.
Message ID <differential-rev-PHID-DREV-ggvit7quuneq4lm6zrog-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/48282/
State Superseded
Headers show

Comments

phabricator - Feb. 8, 2021, 10:44 p.m.
SimonSapin created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This removes some repetition, and will avoid additional repetition
  in the next commit.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D9969

AFFECTED FILES
  rust/rhg/src/commands.rs
  rust/rhg/src/main.rs

CHANGE DETAILS




To: SimonSapin, #hg-reviewers
Cc: mercurial-patches, mercurial-devel

Patch

diff --git a/rust/rhg/src/main.rs b/rust/rhg/src/main.rs
--- a/rust/rhg/src/main.rs
+++ b/rust/rhg/src/main.rs
@@ -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
+}
diff --git a/rust/rhg/src/commands.rs b/rust/rhg/src/commands.rs
deleted file mode 100644
--- a/rust/rhg/src/commands.rs
+++ /dev/null
@@ -1,5 +0,0 @@ 
-pub mod cat;
-pub mod debugdata;
-pub mod debugrequirements;
-pub mod files;
-pub mod root;