##// END OF EJS Templates
rhg: Replace subcommand boilerplate with a macro...
Simon Sapin -
r47230:2e272cd7 default draft
parent child Browse files
Show More
@@ -1,73 +1,94 b''
1 extern crate log;
1 extern crate log;
2 use clap::App;
2 use clap::App;
3 use clap::AppSettings;
3 use clap::AppSettings;
4 use clap::ArgMatches;
4 use clap::ArgMatches;
5 use format_bytes::format_bytes;
5 use format_bytes::format_bytes;
6
6
7 mod commands;
8 mod error;
7 mod error;
9 mod exitcode;
8 mod exitcode;
10 mod ui;
9 mod ui;
11 use error::CommandError;
10 use error::CommandError;
12
11
13 fn main() {
12 fn main() {
14 env_logger::init();
13 env_logger::init();
15 let app = App::new("rhg")
14 let app = App::new("rhg")
16 .setting(AppSettings::AllowInvalidUtf8)
15 .setting(AppSettings::AllowInvalidUtf8)
17 .setting(AppSettings::SubcommandRequired)
16 .setting(AppSettings::SubcommandRequired)
18 .setting(AppSettings::VersionlessSubcommands)
17 .setting(AppSettings::VersionlessSubcommands)
19 .version("0.0.1")
18 .version("0.0.1");
20 .subcommand(commands::root::args())
19 let app = add_subcommand_args(app);
21 .subcommand(commands::files::args())
22 .subcommand(commands::cat::args())
23 .subcommand(commands::debugdata::args())
24 .subcommand(commands::debugrequirements::args());
25
26 let matches = app.clone().get_matches_safe().unwrap_or_else(|err| {
27 let _ = ui::Ui::new().writeln_stderr_str(&err.message);
28 std::process::exit(exitcode::UNIMPLEMENTED)
29 });
30
20
31 let ui = ui::Ui::new();
21 let ui = ui::Ui::new();
32
22
33 let command_result = match_subcommand(matches, &ui);
23 let matches = app.clone().get_matches_safe().unwrap_or_else(|err| {
24 let _ = ui.writeln_stderr_str(&err.message);
25 std::process::exit(exitcode::UNIMPLEMENTED)
26 });
27 let (subcommand_name, subcommand_matches) = matches.subcommand();
28 let run = subcommand_run_fn(subcommand_name)
29 .expect("unknown subcommand name from clap despite AppSettings::SubcommandRequired");
30 let args = subcommand_matches
31 .expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired");
34
32
35 let exit_code = match command_result {
33 let result = (|| -> Result<(), CommandError> {
34 let config = hg::config::Config::load()?;
35 run(&ui, &config, args)
36 })();
37
38 let exit_code = match result {
36 Ok(_) => exitcode::OK,
39 Ok(_) => exitcode::OK,
37
40
38 // Exit with a specific code and no error message to let a potential
41 // Exit with a specific code and no error message to let a potential
39 // wrapper script fallback to Python-based Mercurial.
42 // wrapper script fallback to Python-based Mercurial.
40 Err(CommandError::Unimplemented) => exitcode::UNIMPLEMENTED,
43 Err(CommandError::Unimplemented) => exitcode::UNIMPLEMENTED,
41
44
42 Err(CommandError::Abort { message }) => {
45 Err(CommandError::Abort { message }) => {
43 if !message.is_empty() {
46 if !message.is_empty() {
44 // Ignore errors when writing to stderr, we’re already exiting
47 // Ignore errors when writing to stderr, we’re already exiting
45 // with failure code so there’s not much more we can do.
48 // with failure code so there’s not much more we can do.
46 let _ =
49 let _ =
47 ui.write_stderr(&format_bytes!(b"abort: {}\n", message));
50 ui.write_stderr(&format_bytes!(b"abort: {}\n", message));
48 }
51 }
49 exitcode::ABORT
52 exitcode::ABORT
50 }
53 }
51 };
54 };
52 std::process::exit(exit_code)
55 std::process::exit(exit_code)
53 }
56 }
54
57
55 fn match_subcommand(
58 macro_rules! subcommands {
56 matches: ArgMatches,
59 ($( $command: ident )+) => {
57 ui: &ui::Ui,
60 mod commands {
58 ) -> Result<(), CommandError> {
61 $(
59 let config = hg::config::Config::load()?;
62 pub mod $command;
63 )+
64 }
60
65
61 match matches.subcommand() {
66 fn add_subcommand_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
62 ("root", Some(matches)) => commands::root::run(ui, &config, matches),
67 app
63 ("files", Some(matches)) => commands::files::run(ui, &config, matches),
68 $(
64 ("cat", Some(matches)) => commands::cat::run(ui, &config, matches),
69 .subcommand(commands::$command::args())
65 ("debugdata", Some(matches)) => {
70 )+
66 commands::debugdata::run(ui, &config, matches)
67 }
71 }
68 ("debugrequirements", Some(matches)) => {
72
69 commands::debugrequirements::run(ui, &config, matches)
73 fn subcommand_run_fn(name: &str) -> Option<fn(
70 }
74 &ui::Ui,
71 _ => unreachable!(), // Because of AppSettings::SubcommandRequired,
75 &hg::config::Config,
76 &ArgMatches,
77 ) -> Result<(), CommandError>> {
78 match name {
79 $(
80 stringify!($command) => Some(commands::$command::run),
81 )+
82 _ => None,
72 }
83 }
73 }
84 }
85 };
86 }
87
88 subcommands! {
89 cat
90 debugdata
91 debugrequirements
92 files
93 root
94 }
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now