##// END OF EJS Templates
tests: fix glob pattern for dynamic timer alignment...
tests: fix glob pattern for dynamic timer alignment The number of space characters varies depending on the number of digits of the timer, making some tests fail on slow machines in an unintended way: ```diff --- /build/mercurial-6.1/tests/test-merge-halt.t +++ /build/mercurial-6.1/tests/test-merge-halt.t.err @@ -210,6 +210,6 @@ merge halted after failed merge (see hg resolve) [240] $ hg shelve --list - default (* ago) changes to: foo (glob) + default (11s ago) changes to: foo $ hg unshelve --abort unshelve of 'default' aborted ERROR: test-merge-halt.t output changed ``` Differential Revision: https://phab.mercurial-scm.org/D12381

File last commit:

r49619:3d8fa7cf default
r49825:f64bbba2 default
Show More
main.rs
708 lines | 23.3 KiB | application/rls-services+xml | RustLexer
Antoine Cezar
rhg: Add debug timing...
r46101 extern crate log;
Simon Sapin
rhg: $HG_PENDING is not supported...
r49159 use crate::error::CommandError;
Raphaël Gomès
rhg: signal when falling back in logs...
r49622 use crate::ui::{local_to_utf8, Ui};
Antoine Cezar
rhg: add a limited `rhg root` subcommand...
r45593 use clap::App;
use clap::AppSettings;
Simon Sapin
rhg: Add support for -R and --repository command-line arguments...
r47253 use clap::Arg;
Antoine Cezar
rhg: add a limited `rhg debugdata` subcommand...
r46100 use clap::ArgMatches;
Simon Sapin
rhg: Fall back to Python if unsupported extensions are enabled...
r47467 use format_bytes::{format_bytes, join};
Pulkit Goyal
rhg: read [paths] for `--repository` value...
r48196 use hg::config::{Config, ConfigSource};
Pulkit Goyal
rhg: add exit code to HgError::Abort()...
r48199 use hg::exit_codes;
Simon Sapin
rhg: Move `Repo` object creation into `main()`...
r47335 use hg::repo::{Repo, RepoError};
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 use hg::utils::files::{get_bytes_from_os_str, get_path_from_bytes};
use hg::utils::SliceExt;
Raphaël Gomès
rhg: support the new extension suboptions syntax...
r49270 use std::collections::HashSet;
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 use std::ffi::OsString;
use std::path::PathBuf;
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 use std::process::Command;
Antoine Cezar
rhg: add a limited `rhg root` subcommand...
r45593
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 mod blackbox;
Simon Sapin
rhg: Add support for colored output...
r49584 mod color;
Antoine Cezar
rhg: add Command trait for subcommands implemented by rhg...
r45515 mod error;
Antoine Cezar
rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`...
r45592 mod ui;
Pulkit Goyal
rhg: refactor function to relativize paths in utils...
r48988 pub mod utils {
pub mod path_utils;
}
Antoine Cezar
rhg: add rhg crate...
r45503
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 fn main_with_result(
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 process_start_time: &blackbox::ProcessStartTime,
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 ui: &ui::Ui,
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 repo: Result<&Repo, &NoRepoInCwdError>,
config: &Config,
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 ) -> Result<(), CommandError> {
Simon Sapin
rhg: Colorize `rhg status` output when appropriate...
r49585 check_unsupported(config, repo)?;
Simon Sapin
rhg: Fall back to Python if unsupported extensions are enabled...
r47467
Antoine Cezar
rhg: add a limited `rhg debugdata` subcommand...
r46100 let app = App::new("rhg")
Simon Sapin
rhg: Use clap’s support for global CLI arguments...
r47351 .global_setting(AppSettings::AllowInvalidUtf8)
Simon Sapin
rhg: Fall back to Python for --version...
r47480 .global_setting(AppSettings::DisableVersion)
Antoine Cezar
rhg: add a limited `rhg root` subcommand...
r45593 .setting(AppSettings::SubcommandRequired)
.setting(AppSettings::VersionlessSubcommands)
Simon Sapin
rhg: Use clap’s support for global CLI arguments...
r47351 .arg(
Arg::with_name("repository")
.help("repository root directory")
.short("-R")
.long("--repository")
.value_name("REPO")
.takes_value(true)
// Both ok: `hg -R ./foo log` or `hg log -R ./foo`
.global(true),
)
.arg(
Arg::with_name("config")
.help("set/override config option (use 'section.name=value')")
.long("--config")
.value_name("CONFIG")
.takes_value(true)
.global(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
rhg: Add support for --cwd...
r47470 .arg(
Arg::with_name("cwd")
.help("change working directory")
.long("--cwd")
.value_name("DIR")
.takes_value(true)
.global(true),
)
Simon Sapin
rhg: Add parsing for the --color global CLI argument...
r49583 .arg(
Arg::with_name("color")
.help("when to colorize (boolean, always, auto, never, or debug)")
.long("--color")
.value_name("TYPE")
.takes_value(true)
.global(true),
)
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252 .version("0.0.1");
let app = add_subcommand_args(app);
Antoine Cezar
rhg: add a limited `rhg root` subcommand...
r45593
Simon Sapin
rhg: Remove error message on unsupported CLI arguments...
r47333 let matches = app.clone().get_matches_safe()?;
Simon Sapin
rhg: Add support for -R and --repository command-line arguments...
r47253
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252 let (subcommand_name, subcommand_matches) = matches.subcommand();
Raphaël Gomès
rhg: fallback if the current command has any generic hook defined...
r48889
Raphaël Gomès
rhg: fallback if `defaults` config is set for the current command...
r48890 // Mercurial allows users to define "defaults" for commands, fallback
// if a default is detected for the current command
let defaults = config.get_str(b"defaults", subcommand_name.as_bytes());
if defaults?.is_some() {
let msg = "`defaults` config set";
return Err(CommandError::unsupported(msg));
}
Raphaël Gomès
rhg: fallback if the current command has any generic hook defined...
r48889 for prefix in ["pre", "post", "fail"].iter() {
// Mercurial allows users to define generic hooks for commands,
// fallback if any are detected
let item = format!("{}-{}", prefix, subcommand_name);
let hook_for_command = config.get_str(b"hooks", item.as_bytes())?;
if hook_for_command.is_some() {
let msg = format!("{}-{} hook defined", prefix, subcommand_name);
return Err(CommandError::unsupported(msg));
}
}
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252 let run = subcommand_run_fn(subcommand_name)
.expect("unknown subcommand name from clap despite AppSettings::SubcommandRequired");
Simon Sapin
rhg: Group values passed to every sub-command into a struct...
r47334 let subcommand_args = subcommand_matches
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252 .expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired");
Antoine Cezar
rhg: add a limited `rhg root` subcommand...
r45593
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 let invocation = CliInvocation {
Simon Sapin
rhg: Group values passed to every sub-command into a struct...
r47334 ui,
subcommand_args,
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 config,
repo,
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 };
Raphaël Gomès
rhg: fall back if subrepos are detected...
r48891
if let Ok(repo) = repo {
// We don't support subrepos, fallback if the subrepos file is present
if repo.working_directory_vfs().join(".hgsub").exists() {
let msg = "subrepos (.hgsub is present)";
return Err(CommandError::unsupported(msg));
}
}
Raphaël Gomès
rhg: don't run `blackbox` if not activated...
r49243 if config.is_extension_enabled(b"blackbox") {
let blackbox =
blackbox::Blackbox::new(&invocation, process_start_time)?;
blackbox.log_command_start();
let result = run(&invocation);
blackbox.log_command_end(exit_code(
&result,
// TODO: show a warning or combine with original error if
// `get_bool` returns an error
config
.get_bool(b"ui", b"detailed-exit-code")
.unwrap_or(false),
));
result
} else {
run(&invocation)
}
Simon Sapin
rhg: Remove error message on unsupported CLI arguments...
r47333 }
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252
Simon Sapin
rhg: Remove error message on unsupported CLI arguments...
r47333 fn main() {
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 // Run this first, before we find out if the blackbox extension is even
// enabled, in order to include everything in-between in the duration
// measurements. Reading config files can be slow if they’re on NFS.
let process_start_time = blackbox::ProcessStartTime::now();
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 env_logger::init();
Simon Sapin
rhg: Remove error message on unsupported CLI arguments...
r47333
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 let early_args = EarlyArgs::parse(std::env::args_os());
Simon Sapin
rhg: Add support for --cwd...
r47470
let initial_current_dir = early_args.cwd.map(|cwd| {
let cwd = get_path_from_bytes(&cwd);
std::env::current_dir()
.and_then(|initial| {
std::env::set_current_dir(cwd)?;
Ok(initial)
})
.unwrap_or_else(|error| {
exit(
&None,
Simon Sapin
rhg: Make Ui::new falliable, add Ui::new_infallible...
r49582 &Ui::new_infallible(&Config::empty()),
Simon Sapin
rhg: Add support for --cwd...
r47470 OnUnsupported::Abort,
Err(CommandError::abort(format!(
"abort: {}: '{}'",
error,
cwd.display()
))),
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 false,
Simon Sapin
rhg: Add support for --cwd...
r47470 )
})
});
Pulkit Goyal
rhg: split non_repo_config and `--config` loading in different functions...
r48198 let mut non_repo_config =
Config::load_non_repo().unwrap_or_else(|error| {
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 // Normally this is decided based on config, but we don’t have that
// available. As of this writing config loading never returns an
// "unsupported" error but that is not enforced by the type system.
let on_unsupported = OnUnsupported::Abort;
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 exit(
&initial_current_dir,
Simon Sapin
rhg: Make Ui::new falliable, add Ui::new_infallible...
r49582 &Ui::new_infallible(&Config::empty()),
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 on_unsupported,
Err(error.into()),
false,
)
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 });
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423
Pulkit Goyal
rhg: split non_repo_config and `--config` loading in different functions...
r48198 non_repo_config
Simon Sapin
rhg: Add parsing for the --color global CLI argument...
r49583 .load_cli_args(early_args.config, early_args.color)
Pulkit Goyal
rhg: split non_repo_config and `--config` loading in different functions...
r48198 .unwrap_or_else(|error| {
exit(
&initial_current_dir,
Simon Sapin
rhg: Make Ui::new falliable, add Ui::new_infallible...
r49582 &Ui::new_infallible(&non_repo_config),
Arseniy Alekseyev
rhg: only complain about poorly configured fallback when falling back...
r49176 OnUnsupported::from_config(&non_repo_config),
Pulkit Goyal
rhg: split non_repo_config and `--config` loading in different functions...
r48198 Err(error.into()),
non_repo_config
.get_bool(b"ui", b"detailed-exit-code")
.unwrap_or(false),
)
});
Simon Sapin
rhg: Fall back to Python on --repository with an URL...
r47463 if let Some(repo_path_bytes) = &early_args.repo {
lazy_static::lazy_static! {
static ref SCHEME_RE: regex::bytes::Regex =
// Same as `_matchscheme` in `mercurial/util.py`
regex::bytes::Regex::new("^[a-zA-Z0-9+.\\-]+:").unwrap();
}
if SCHEME_RE.is_match(&repo_path_bytes) {
exit(
Simon Sapin
rhg: Add support for --cwd...
r47470 &initial_current_dir,
Simon Sapin
rhg: Make Ui::new falliable, add Ui::new_infallible...
r49582 &Ui::new_infallible(&non_repo_config),
Arseniy Alekseyev
rhg: only complain about poorly configured fallback when falling back...
r49176 OnUnsupported::from_config(&non_repo_config),
Simon Sapin
rhg: Fall back to Python on --repository with an URL...
r47463 Err(CommandError::UnsupportedFeature {
message: format_bytes!(
b"URL-like --repository {}",
repo_path_bytes
),
}),
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 // TODO: show a warning or combine with original error if
// `get_bool` returns an error
non_repo_config
.get_bool(b"ui", b"detailed-exit-code")
.unwrap_or(false),
Simon Sapin
rhg: Fall back to Python on --repository with an URL...
r47463 )
}
}
Pulkit Goyal
rhg: read [paths] for `--repository` value...
r48196 let repo_arg = early_args.repo.unwrap_or(Vec::new());
let repo_path: Option<PathBuf> = {
if repo_arg.is_empty() {
None
} else {
let local_config = {
if std::env::var_os("HGRCSKIPREPO").is_none() {
Pulkit Goyal
rhg: look for repository in ancestors also instead of cwd only...
r48197 // TODO: handle errors from find_repo_root
if let Ok(current_dir_path) = Repo::find_repo_root() {
Pulkit Goyal
rhg: read [paths] for `--repository` value...
r48196 let config_files = vec![
ConfigSource::AbsPath(
current_dir_path.join(".hg/hgrc"),
),
ConfigSource::AbsPath(
current_dir_path.join(".hg/hgrc-not-shared"),
),
];
// TODO: handle errors from
// `load_from_explicit_sources`
Config::load_from_explicit_sources(config_files).ok()
} else {
None
}
} else {
None
}
};
let non_repo_config_val = {
let non_repo_val = non_repo_config.get(b"paths", &repo_arg);
match &non_repo_val {
Some(val) if val.len() > 0 => home::home_dir()
.unwrap_or_else(|| PathBuf::from("~"))
.join(get_path_from_bytes(val))
.canonicalize()
// TODO: handle error and make it similar to python
// implementation maybe?
.ok(),
_ => None,
}
};
let config_val = match &local_config {
None => non_repo_config_val,
Some(val) => {
let local_config_val = val.get(b"paths", &repo_arg);
match &local_config_val {
Some(val) if val.len() > 0 => {
// presence of a local_config assures that
// current_dir
// wont result in an Error
let canpath = hg::utils::current_dir()
.unwrap()
.join(get_path_from_bytes(val))
.canonicalize();
canpath.ok().or(non_repo_config_val)
}
_ => non_repo_config_val,
}
}
};
config_val.or(Some(get_path_from_bytes(&repo_arg).to_path_buf()))
}
};
let repo_result = match Repo::find(&non_repo_config, repo_path.to_owned())
{
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 Ok(repo) => Ok(repo),
Err(RepoError::NotFound { at }) if repo_path.is_none() => {
// Not finding a repo is not fatal yet, if `-R` was not given
Err(NoRepoInCwdError { cwd: at })
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 }
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 Err(error) => exit(
Simon Sapin
rhg: Add support for --cwd...
r47470 &initial_current_dir,
Simon Sapin
rhg: Make Ui::new falliable, add Ui::new_infallible...
r49582 &Ui::new_infallible(&non_repo_config),
Arseniy Alekseyev
rhg: only complain about poorly configured fallback when falling back...
r49176 OnUnsupported::from_config(&non_repo_config),
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 Err(error.into()),
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 // TODO: show a warning or combine with original error if
// `get_bool` returns an error
non_repo_config
.get_bool(b"ui", b"detailed-exit-code")
.unwrap_or(false),
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 ),
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 };
let config = if let Ok(repo) = &repo_result {
repo.config()
} else {
&non_repo_config
};
Simon Sapin
rhg: Make Ui::new falliable, add Ui::new_infallible...
r49582 let ui = Ui::new(&config).unwrap_or_else(|error| {
exit(
&initial_current_dir,
&Ui::new_infallible(&config),
OnUnsupported::from_config(&config),
Err(error.into()),
config
.get_bool(b"ui", b"detailed-exit-code")
.unwrap_or(false),
)
});
Arseniy Alekseyev
rhg: only complain about poorly configured fallback when falling back...
r49176 let on_unsupported = OnUnsupported::from_config(config);
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423
let result = main_with_result(
&process_start_time,
&ui,
repo_result.as_ref(),
config,
);
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 exit(
&initial_current_dir,
&ui,
on_unsupported,
result,
// TODO: show a warning or combine with original error if `get_bool`
// returns an error
config
.get_bool(b"ui", b"detailed-exit-code")
.unwrap_or(false),
)
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 }
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 fn exit_code(
result: &Result<(), CommandError>,
use_detailed_exit_code: bool,
) -> i32 {
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 match result {
Pulkit Goyal
rhg: add exit code to HgError::Abort()...
r48199 Ok(()) => exit_codes::OK,
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 Err(CommandError::Abort {
message: _,
detailed_exit_code,
}) => {
if use_detailed_exit_code {
*detailed_exit_code
} else {
Pulkit Goyal
rhg: add exit code to HgError::Abort()...
r48199 exit_codes::ABORT
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 }
}
Pulkit Goyal
rhg: add exit code to HgError::Abort()...
r48199 Err(CommandError::Unsuccessful) => exit_codes::UNSUCCESSFUL,
Simon Sapin
rhg: Simplify CommandError based on its use...
r47174
// Exit with a specific code and no error message to let a potential
// wrapper script fallback to Python-based Mercurial.
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 Err(CommandError::UnsupportedFeature { .. }) => {
Pulkit Goyal
rhg: add exit code to HgError::Abort()...
r48199 exit_codes::UNIMPLEMENTED
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 }
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 }
Antoine Cezar
rhg: add rhg crate...
r45503 }
Antoine Cezar
rhg: add a limited `rhg debugdata` subcommand...
r46100
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 fn exit(
Simon Sapin
rhg: Add support for --cwd...
r47470 initial_current_dir: &Option<PathBuf>,
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 ui: &Ui,
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 mut on_unsupported: OnUnsupported,
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 result: Result<(), CommandError>,
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 use_detailed_exit_code: bool,
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 ) -> ! {
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 if let (
OnUnsupported::Fallback { executable },
Raphaël Gomès
rhg: signal when falling back in logs...
r49622 Err(CommandError::UnsupportedFeature { message }),
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 ) = (&on_unsupported, &result)
{
let mut args = std::env::args_os();
Arseniy Alekseyev
rhg: only complain about poorly configured fallback when falling back...
r49176 let executable = match executable {
None => {
exit_no_fallback(
ui,
OnUnsupported::Abort,
Err(CommandError::abort(
"abort: 'rhg.on-unsupported=fallback' without \
'rhg.fallback-executable' set.",
)),
false,
);
}
Some(executable) => executable,
};
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 let executable_path = get_path_from_bytes(&executable);
let this_executable = args.next().expect("exepcted argv[0] to exist");
if executable_path == &PathBuf::from(this_executable) {
// Avoid spawning infinitely many processes until resource
// exhaustion.
let _ = ui.write_stderr(&format_bytes!(
b"Blocking recursive fallback. The 'rhg.fallback-executable = {}' config \
points to `rhg` itself.\n",
executable
));
on_unsupported = OnUnsupported::Abort
} else {
Raphaël Gomès
rhg: signal when falling back in logs...
r49622 log::debug!("falling back (see trace-level log)");
log::trace!("{}", local_to_utf8(message));
Arseniy Alekseyev
rhg: only complain about poorly configured fallback when falling back...
r49176 // `args` is now `argv[1..]` since we’ve already consumed
// `argv[0]`
Simon Sapin
rhg: Add support for --cwd...
r47470 let mut command = Command::new(executable_path);
command.args(args);
if let Some(initial) = initial_current_dir {
command.current_dir(initial);
}
let result = command.status();
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 match result {
Ok(status) => std::process::exit(
Pulkit Goyal
rhg: add exit code to HgError::Abort()...
r48199 status.code().unwrap_or(exit_codes::ABORT),
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 ),
Err(error) => {
let _ = ui.write_stderr(&format_bytes!(
b"tried to fall back to a '{}' sub-process but got error {}\n",
executable, format_bytes::Utf8(error)
));
on_unsupported = OnUnsupported::Abort
}
}
}
}
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 exit_no_fallback(ui, on_unsupported, result, use_detailed_exit_code)
Simon Sapin
rhg: Remove `rhg.fallback-executable=hg` default configuration...
r47482 }
fn exit_no_fallback(
ui: &Ui,
on_unsupported: OnUnsupported,
result: Result<(), CommandError>,
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 use_detailed_exit_code: bool,
Simon Sapin
rhg: Remove `rhg.fallback-executable=hg` default configuration...
r47482 ) -> ! {
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 match &result {
Ok(_) => {}
Simon Sapin
rhg: `cat` command: print error messages for missing files...
r47478 Err(CommandError::Unsuccessful) => {}
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 Err(CommandError::Abort {
message,
detailed_exit_code: _,
}) => {
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 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.
Simon Sapin
rhg: Align config file parse error formatting with Python...
r47465 let _ = ui.write_stderr(&format_bytes!(b"{}\n", message));
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 }
}
Err(CommandError::UnsupportedFeature { message }) => {
match on_unsupported {
OnUnsupported::Abort => {
let _ = ui.write_stderr(&format_bytes!(
b"unsupported feature: {}\n",
message
));
}
OnUnsupported::AbortSilent => {}
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 OnUnsupported::Fallback { .. } => unreachable!(),
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 }
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 }
}
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 std::process::exit(exit_code(&result, use_detailed_exit_code))
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 }
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
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
rhg: Use clap’s support for global CLI arguments...
r47351 .subcommand(commands::$command::args())
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252 )+
}
Simon Sapin
rhg: Parse system and user configuration at program start...
r47213
Simon Sapin
rhg: Group values passed to every sub-command into a struct...
r47334 pub type RunFn = fn(&CliInvocation) -> Result<(), CommandError>;
fn subcommand_run_fn(name: &str) -> Option<RunFn> {
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252 match name {
$(
stringify!($command) => Some(commands::$command::run),
)+
_ => None,
}
Antoine Cezar
rhg: add a limited `rhg cat -r` subcommand...
r46113 }
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252 };
Antoine Cezar
rhg: add a limited `rhg debugdata` subcommand...
r46100 }
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252
subcommands! {
cat
debugdata
debugrequirements
Arseniy Alekseyev
rhg: implement the debugignorerhg subcommand...
r49178 debugignorerhg
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252 files
root
Simon Sapin
rhg: add limited support for the `config` sub-command...
r47255 config
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 status
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252 }
Georges Racinet
rhg: Initial support for the 'status' command...
r47578
Simon Sapin
rhg: Group values passed to every sub-command into a struct...
r47334 pub struct CliInvocation<'a> {
ui: &'a Ui,
subcommand_args: &'a ArgMatches<'a>,
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 config: &'a Config,
Simon Sapin
rhg: Move `Repo` object creation into `main()`...
r47335 /// References inside `Result` is a bit peculiar but allow
/// `invocation.repo?` to work out with `&CliInvocation` since this
/// `Result` type is `Copy`.
repo: Result<&'a Repo, &'a NoRepoInCwdError>,
}
struct NoRepoInCwdError {
cwd: PathBuf,
Simon Sapin
rhg: Group values passed to every sub-command into a struct...
r47334 }
Simon Sapin
rhg: Move `Repo` object creation into `main()`...
r47335
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 /// CLI arguments to be parsed "early" in order to be able to read
/// configuration before using Clap. Ideally we would also use Clap for this,
/// see <https://github.com/clap-rs/clap/discussions/2366>.
///
/// These arguments are still declared when we do use Clap later, so that Clap
/// does not return an error for their presence.
struct EarlyArgs {
/// Values of all `--config` arguments. (Possibly none)
config: Vec<Vec<u8>>,
Simon Sapin
rhg: Add parsing for the --color global CLI argument...
r49583 /// Value of all the `--color` argument, if any.
color: Option<Vec<u8>>,
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 /// Value of the `-R` or `--repository` argument, if any.
repo: Option<Vec<u8>>,
Simon Sapin
rhg: Add support for --cwd...
r47470 /// Value of the `--cwd` argument, if any.
cwd: Option<Vec<u8>>,
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 }
impl EarlyArgs {
fn parse(args: impl IntoIterator<Item = OsString>) -> Self {
let mut args = args.into_iter().map(get_bytes_from_os_str);
let mut config = Vec::new();
Simon Sapin
rhg: Add parsing for the --color global CLI argument...
r49583 let mut color = None;
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 let mut repo = None;
Simon Sapin
rhg: Add support for --cwd...
r47470 let mut cwd = None;
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 // Use `while let` instead of `for` so that we can also call
// `args.next()` inside the loop.
while let Some(arg) = args.next() {
if arg == b"--config" {
if let Some(value) = args.next() {
config.push(value)
}
} else if let Some(value) = arg.drop_prefix(b"--config=") {
config.push(value.to_owned())
}
Simon Sapin
rhg: Add parsing for the --color global CLI argument...
r49583 if arg == b"--color" {
if let Some(value) = args.next() {
color = Some(value)
}
} else if let Some(value) = arg.drop_prefix(b"--color=") {
color = Some(value.to_owned())
}
Simon Sapin
rhg: Add support for --cwd...
r47470 if arg == b"--cwd" {
if let Some(value) = args.next() {
cwd = Some(value)
}
} else if let Some(value) = arg.drop_prefix(b"--cwd=") {
cwd = Some(value.to_owned())
}
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 if arg == b"--repository" || arg == b"-R" {
if let Some(value) = args.next() {
repo = Some(value)
}
} else if let Some(value) = arg.drop_prefix(b"--repository=") {
repo = Some(value.to_owned())
} else if let Some(value) = arg.drop_prefix(b"-R") {
repo = Some(value.to_owned())
}
Simon Sapin
rhg: Move `Repo` object creation into `main()`...
r47335 }
Simon Sapin
rhg: Add parsing for the --color global CLI argument...
r49583 Self {
config,
color,
repo,
cwd,
}
Simon Sapin
rhg: Move `Repo` object creation into `main()`...
r47335 }
}
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424
/// What to do when encountering some unsupported feature.
///
/// See `HgError::UnsupportedFeature` and `CommandError::UnsupportedFeature`.
enum OnUnsupported {
/// Print an error message describing what feature is not supported,
/// and exit with code 252.
Abort,
/// Silently exit with code 252.
AbortSilent,
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 /// Try running a Python implementation
Arseniy Alekseyev
rhg: only complain about poorly configured fallback when falling back...
r49176 Fallback { executable: Option<Vec<u8>> },
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 }
impl OnUnsupported {
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 const DEFAULT: Self = OnUnsupported::Abort;
Arseniy Alekseyev
rhg: only complain about poorly configured fallback when falling back...
r49176 fn from_config(config: &Config) -> Self {
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 match config
.get(b"rhg", b"on-unsupported")
.map(|value| value.to_ascii_lowercase())
.as_deref()
{
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 Some(b"abort") => OnUnsupported::Abort,
Some(b"abort-silent") => OnUnsupported::AbortSilent,
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 Some(b"fallback") => OnUnsupported::Fallback {
executable: config
.get(b"rhg", b"fallback-executable")
Arseniy Alekseyev
rhg: only complain about poorly configured fallback when falling back...
r49176 .map(|x| x.to_owned()),
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 },
None => Self::DEFAULT,
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 Some(_) => {
// TODO: warn about unknown config value
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 Self::DEFAULT
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 }
}
}
}
Simon Sapin
rhg: Fall back to Python if unsupported extensions are enabled...
r47467
Raphaël Gomès
rhg: support the new extension suboptions syntax...
r49270 /// The `*` extension is an edge-case for config sub-options that apply to all
/// extensions. For now, only `:required` exists, but that may change in the
/// future.
Arseniy Alekseyev
rhg: add support for narrow clones and sparse checkouts...
r49238 const SUPPORTED_EXTENSIONS: &[&[u8]] =
Raphaël Gomès
rhg: support the new extension suboptions syntax...
r49270 &[b"blackbox", b"share", b"sparse", b"narrow", b"*"];
Simon Sapin
rhg: Fall back to Python if unsupported extensions are enabled...
r47467
fn check_extensions(config: &Config) -> Result<(), CommandError> {
Raphaël Gomès
rhg: support the new extension suboptions syntax...
r49270 let enabled: HashSet<&[u8]> = config
.get_section_keys(b"extensions")
.into_iter()
.map(|extension| {
// Ignore extension suboptions. Only `required` exists for now.
// `rhg` either supports an extension or doesn't, so it doesn't
// make sense to consider the loading of an extension.
extension.split_2(b':').unwrap_or((extension, b"")).0
})
.collect();
Simon Sapin
rhg: Fall back to Python if unsupported extensions are enabled...
r47467
let mut unsupported = enabled;
for supported in SUPPORTED_EXTENSIONS {
unsupported.remove(supported);
}
Simon Sapin
rhg: Switch rhg.ignored-extensions config to Python-compatible list syntax...
r48763 if let Some(ignored_list) = config.get_list(b"rhg", b"ignored-extensions")
Simon Sapin
rhg: Add an allow-list of ignored extensions...
r47468 {
for ignored in ignored_list {
Simon Sapin
rhg: Switch rhg.ignored-extensions config to Python-compatible list syntax...
r48763 unsupported.remove(ignored.as_slice());
Simon Sapin
rhg: Add an allow-list of ignored extensions...
r47468 }
}
Simon Sapin
rhg: Fall back to Python if unsupported extensions are enabled...
r47467 if unsupported.is_empty() {
Ok(())
} else {
Err(CommandError::UnsupportedFeature {
message: format_bytes!(
Simon Sapin
rhg: Add an allow-list of ignored extensions...
r47468 b"extensions: {} (consider adding them to 'rhg.ignored-extensions' config)",
Simon Sapin
rhg: Fall back to Python if unsupported extensions are enabled...
r47467 join(unsupported, b", ")
),
})
}
}
Simon Sapin
rhg: $HG_PENDING is not supported...
r49159
Simon Sapin
rhg: Colored output is not supported...
r49163 fn check_unsupported(
config: &Config,
Simon Sapin
rhg: Sub-repositories are not supported...
r49341 repo: Result<&Repo, &NoRepoInCwdError>,
Simon Sapin
rhg: Colored output is not supported...
r49163 ) -> Result<(), CommandError> {
Simon Sapin
rhg: $HG_PENDING is not supported...
r49159 check_extensions(config)?;
if std::env::var_os("HG_PENDING").is_some() {
// TODO: only if the value is `== repo.working_directory`?
// What about relative v.s. absolute paths?
Err(CommandError::unsupported("$HG_PENDING"))?
}
Simon Sapin
rhg: Sub-repositories are not supported...
r49341 if let Ok(repo) = repo {
if repo.has_subrepos()? {
Err(CommandError::unsupported("sub-repositories"))?
}
}
Simon Sapin
rhg: [encode] and [decode] config sections are not supported...
r49162 if config.has_non_empty_section(b"encode") {
Err(CommandError::unsupported("[encode] config"))?
}
if config.has_non_empty_section(b"decode") {
Err(CommandError::unsupported("[decode] config"))?
}
Simon Sapin
rhg: $HG_PENDING is not supported...
r49159 Ok(())
}