diff -r 7284b524b441 -r 33f2d56acc73 rust/rhg/src/main.rs --- a/rust/rhg/src/main.rs Mon Mar 01 13:51:35 2021 +0100 +++ b/rust/rhg/src/main.rs Mon Mar 01 16:18:42 2021 +0100 @@ -84,8 +84,15 @@ let ui = ui::Ui::new(); let early_args = EarlyArgs::parse(std::env::args_os()); - let non_repo_config = Config::load(early_args.config) - .unwrap_or_else(|error| exit(&ui, Err(error.into()))); + let non_repo_config = + Config::load(early_args.config).unwrap_or_else(|error| { + // 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; + + exit(&ui, on_unsupported, Err(error.into())) + }); let repo_path = early_args.repo.as_deref().map(get_path_from_bytes); let repo_result = match Repo::find(&non_repo_config, repo_path) { @@ -94,7 +101,11 @@ // Not finding a repo is not fatal yet, if `-R` was not given Err(NoRepoInCwdError { cwd: at }) } - Err(error) => exit(&ui, Err(error.into())), + Err(error) => exit( + &ui, + OnUnsupported::from_config(&non_repo_config), + Err(error.into()), + ), }; let config = if let Ok(repo) = &repo_result { @@ -109,7 +120,7 @@ repo_result.as_ref(), config, ); - exit(&ui, result) + exit(&ui, OnUnsupported::from_config(config), result) } fn exit_code(result: &Result<(), CommandError>) -> i32 { @@ -119,16 +130,37 @@ // 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::UnsupportedFeature { .. }) => { + exitcode::UNIMPLEMENTED + } } } -fn exit(ui: &Ui, result: Result<(), CommandError>) -> ! { - if let Err(CommandError::Abort { message }) = &result { - 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)); +fn exit( + ui: &Ui, + on_unsupported: OnUnsupported, + result: Result<(), CommandError>, +) -> ! { + match &result { + Ok(_) => {} + 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)); + } + } + Err(CommandError::UnsupportedFeature { message }) => { + match on_unsupported { + OnUnsupported::Abort => { + let _ = ui.write_stderr(&format_bytes!( + b"unsupported feature: {}\n", + message + )); + } + OnUnsupported::AbortSilent => {} + } } } std::process::exit(exit_code(&result)) @@ -226,3 +258,29 @@ Self { config, repo } } } + +/// 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, +} + +impl OnUnsupported { + fn from_config(config: &Config) -> Self { + let default = OnUnsupported::Abort; + match config.get(b"rhg", b"on-unsupported") { + Some(b"abort") => OnUnsupported::Abort, + Some(b"abort-silent") => OnUnsupported::AbortSilent, + None => default, + Some(_) => { + // TODO: warn about unknown config value + default + } + } + } +}