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
--- 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
+}