main.rs
138 lines
| 4.2 KiB
| application/rls-services+xml
|
RustLexer
Antoine Cezar
|
r46101 | extern crate log; | ||
Antoine Cezar
|
r45593 | use clap::App; | ||
use clap::AppSettings; | ||||
Simon Sapin
|
r47253 | use clap::Arg; | ||
Antoine Cezar
|
r46100 | use clap::ArgMatches; | ||
Simon Sapin
|
r47174 | use format_bytes::format_bytes; | ||
Simon Sapin
|
r47253 | use std::path::Path; | ||
Antoine Cezar
|
r45593 | |||
Antoine Cezar
|
r45515 | mod error; | ||
Antoine Cezar
|
r45503 | mod exitcode; | ||
Antoine Cezar
|
r45592 | mod ui; | ||
Antoine Cezar
|
r46100 | use error::CommandError; | ||
Antoine Cezar
|
r45503 | |||
Simon Sapin
|
r47253 | 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), | ||||
) | ||||
Simon Sapin
|
r47254 | .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), | ||||
) | ||||
Simon Sapin
|
r47253 | } | ||
Antoine Cezar
|
r45503 | fn main() { | ||
Antoine Cezar
|
r46101 | env_logger::init(); | ||
Antoine Cezar
|
r46100 | let app = App::new("rhg") | ||
Antoine Cezar
|
r45593 | .setting(AppSettings::AllowInvalidUtf8) | ||
.setting(AppSettings::SubcommandRequired) | ||||
.setting(AppSettings::VersionlessSubcommands) | ||||
Simon Sapin
|
r47252 | .version("0.0.1"); | ||
Simon Sapin
|
r47253 | let app = add_global_args(app); | ||
Simon Sapin
|
r47252 | let app = add_subcommand_args(app); | ||
Antoine Cezar
|
r45593 | |||
Antoine Cezar
|
r45920 | let ui = ui::Ui::new(); | ||
Simon Sapin
|
r47252 | let matches = app.clone().get_matches_safe().unwrap_or_else(|err| { | ||
let _ = ui.writeln_stderr_str(&err.message); | ||||
std::process::exit(exitcode::UNIMPLEMENTED) | ||||
}); | ||||
Simon Sapin
|
r47253 | |||
Simon Sapin
|
r47252 | 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"); | ||||
Antoine Cezar
|
r45593 | |||
Simon Sapin
|
r47253 | // Global arguments can be in either based on e.g. `hg -R ./foo log` v.s. | ||
// `hg log -R ./foo` | ||||
Simon Sapin
|
r47254 | let value_of_global_arg = | ||
Simon Sapin
|
r47253 | |name| args.value_of_os(name).or_else(|| matches.value_of_os(name)); | ||
Simon Sapin
|
r47254 | // 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 = args.values_of_os(name).into_iter().flatten(); | ||||
a.chain(b) | ||||
}; | ||||
Simon Sapin
|
r47253 | |||
Simon Sapin
|
r47254 | let repo_path = value_of_global_arg("repository").map(Path::new); | ||
Simon Sapin
|
r47252 | let result = (|| -> Result<(), CommandError> { | ||
Simon Sapin
|
r47254 | let config_args = values_of_global_arg("config") | ||
// `get_bytes_from_path` works for OsStr the same as for Path | ||||
.map(hg::utils::files::get_bytes_from_path); | ||||
let config = hg::config::Config::load(config_args)?; | ||||
Simon Sapin
|
r47253 | run(&ui, &config, repo_path, args) | ||
Simon Sapin
|
r47252 | })(); | ||
let exit_code = match result { | ||||
Simon Sapin
|
r47174 | Ok(_) => exitcode::OK, | ||
// Exit with a specific code and no error message to let a potential | ||||
// wrapper script fallback to Python-based Mercurial. | ||||
Err(CommandError::Unimplemented) => exitcode::UNIMPLEMENTED, | ||||
Err(CommandError::Abort { message }) => { | ||||
if !message.is_empty() { | ||||
// Ignore errors when writing to stderr, we’re already exiting | ||||
// with failure code so there’s not much more we can do. | ||||
let _ = | ||||
ui.write_stderr(&format_bytes!(b"abort: {}\n", message)); | ||||
} | ||||
exitcode::ABORT | ||||
Antoine Cezar
|
r45920 | } | ||
Simon Sapin
|
r47174 | }; | ||
std::process::exit(exit_code) | ||||
Antoine Cezar
|
r45503 | } | ||
Antoine Cezar
|
r46100 | |||
Simon Sapin
|
r47252 | macro_rules! subcommands { | ||
($( $command: ident )+) => { | ||||
mod commands { | ||||
$( | ||||
pub mod $command; | ||||
)+ | ||||
} | ||||
fn add_subcommand_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { | ||||
app | ||||
$( | ||||
Simon Sapin
|
r47253 | .subcommand(add_global_args(commands::$command::args())) | ||
Simon Sapin
|
r47252 | )+ | ||
} | ||||
Simon Sapin
|
r47213 | |||
Simon Sapin
|
r47252 | fn subcommand_run_fn(name: &str) -> Option<fn( | ||
&ui::Ui, | ||||
&hg::config::Config, | ||||
Simon Sapin
|
r47253 | Option<&Path>, | ||
Simon Sapin
|
r47252 | &ArgMatches, | ||
) -> Result<(), CommandError>> { | ||||
match name { | ||||
$( | ||||
stringify!($command) => Some(commands::$command::run), | ||||
)+ | ||||
_ => None, | ||||
} | ||||
Antoine Cezar
|
r46113 | } | ||
Simon Sapin
|
r47252 | }; | ||
Antoine Cezar
|
r46100 | } | ||
Simon Sapin
|
r47252 | |||
subcommands! { | ||||
cat | ||||
debugdata | ||||
debugrequirements | ||||
files | ||||
root | ||||
Simon Sapin
|
r47255 | config | ||
Simon Sapin
|
r47252 | } | ||