diff --git a/rust/hg-core/src/config/layer.rs b/rust/hg-core/src/config/layer.rs --- a/rust/hg-core/src/config/layer.rs +++ b/rust/hg-core/src/config/layer.rs @@ -8,6 +8,7 @@ // GNU General Public License version 2 or any later version. use crate::errors::HgError; +use crate::exitcode::CONFIG_PARSE_ERROR_ABORT; use crate::utils::files::{get_bytes_from_path, get_path_from_bytes}; use format_bytes::{format_bytes, write_bytes, DisplayBytes}; use lazy_static::lazy_static; @@ -73,11 +74,14 @@ if let Some((section, item, value)) = parse_one(arg) { layer.add(section, item, value, None); } else { - Err(HgError::abort(format!( - "abort: malformed --config option: '{}' \ + Err(HgError::abort( + format!( + "abort: malformed --config option: '{}' \ (use --config section.name=value)", - String::from_utf8_lossy(arg), - )))? + String::from_utf8_lossy(arg), + ), + CONFIG_PARSE_ERROR_ABORT, + ))? } } if layer.sections.is_empty() { diff --git a/rust/hg-core/src/errors.rs b/rust/hg-core/src/errors.rs --- a/rust/hg-core/src/errors.rs +++ b/rust/hg-core/src/errors.rs @@ -1,4 +1,5 @@ use crate::config::ConfigValueParseError; +use crate::exitcode; use std::fmt; /// Common error cases that can happen in many different APIs @@ -27,9 +28,12 @@ /// Operation cannot proceed for some other reason. /// - /// The given string is a short explanation for users, not intended to be + /// The message is a short explanation for users, not intended to be /// machine-readable. - Abort(String), + Abort { + message: String, + detailed_exit_code: exitcode::ExitCode, + }, /// A configuration value is not in the expected syntax. /// @@ -69,8 +73,15 @@ pub fn unsupported(explanation: impl Into) -> Self { HgError::UnsupportedFeature(explanation.into()) } - pub fn abort(explanation: impl Into) -> Self { - HgError::Abort(explanation.into()) + + pub fn abort( + explanation: impl Into, + exit_code: exitcode::ExitCode, + ) -> Self { + HgError::Abort { + message: explanation.into(), + detailed_exit_code: exit_code, + } } } @@ -78,7 +89,7 @@ impl fmt::Display for HgError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - HgError::Abort(explanation) => write!(f, "{}", explanation), + HgError::Abort { message, .. } => write!(f, "{}", message), HgError::IoError { error, context } => { write!(f, "abort: {}: {}", context, error) } diff --git a/rust/hg-core/src/exitcode.rs b/rust/hg-core/src/exitcode.rs new file mode 100644 --- /dev/null +++ b/rust/hg-core/src/exitcode.rs @@ -0,0 +1,19 @@ +pub type ExitCode = i32; + +/// Successful exit +pub const OK: ExitCode = 0; + +/// Generic abort +pub const ABORT: ExitCode = 255; + +// Abort when there is a config related error +pub const CONFIG_ERROR_ABORT: ExitCode = 30; + +// Abort when there is an error while parsing config +pub const CONFIG_PARSE_ERROR_ABORT: ExitCode = 10; + +/// Generic something completed but did not succeed +pub const UNSUCCESSFUL: ExitCode = 1; + +/// Command or feature not implemented by rhg +pub const UNIMPLEMENTED: ExitCode = 252; diff --git a/rust/hg-core/src/lib.rs b/rust/hg-core/src/lib.rs --- a/rust/hg-core/src/lib.rs +++ b/rust/hg-core/src/lib.rs @@ -11,6 +11,7 @@ pub mod dirstate; pub mod dirstate_tree; pub mod discovery; +pub mod exitcode; pub mod requirements; pub mod testing; // unconditionally built, for use from integration tests pub use dirstate::{ diff --git a/rust/hg-core/src/repo.rs b/rust/hg-core/src/repo.rs --- a/rust/hg-core/src/repo.rs +++ b/rust/hg-core/src/repo.rs @@ -143,6 +143,7 @@ Some(b"abort") | None => HgError::abort( "abort: share source does not support share-safe requirement\n\ (see `hg help config.format.use-share-safe` for more information)", + 30, ), _ => HgError::unsupported("share-safe downgrade"), } @@ -154,6 +155,7 @@ "abort: version mismatch: source uses share-safe \ functionality while the current share does not\n\ (see `hg help config.format.use-share-safe` for more information)", + 30, ), _ => HgError::unsupported("share-safe upgrade"), } diff --git a/rust/rhg/src/error.rs b/rust/rhg/src/error.rs --- a/rust/rhg/src/error.rs +++ b/rust/rhg/src/error.rs @@ -1,10 +1,10 @@ -use crate::exitcode; use crate::ui::utf8_to_local; use crate::ui::UiError; use crate::NoRepoInCwdError; use format_bytes::format_bytes; use hg::config::{ConfigError, ConfigParseError, ConfigValueParseError}; use hg::errors::HgError; +use hg::exitcode; use hg::repo::RepoError; use hg::revlog::revlog::RevlogError; use hg::utils::files::get_bytes_from_path; diff --git a/rust/rhg/src/exitcode.rs b/rust/rhg/src/exitcode.rs deleted file mode 100644 --- a/rust/rhg/src/exitcode.rs +++ /dev/null @@ -1,16 +0,0 @@ -pub type ExitCode = i32; - -/// Successful exit -pub const OK: ExitCode = 0; - -/// Generic abort -pub const ABORT: ExitCode = 255; - -// Abort when there is a config related error -pub const CONFIG_ERROR_ABORT: ExitCode = 30; - -/// Generic something completed but did not succeed -pub const UNSUCCESSFUL: ExitCode = 1; - -/// Command or feature not implemented by rhg -pub const UNIMPLEMENTED: ExitCode = 252; diff --git a/rust/rhg/src/main.rs b/rust/rhg/src/main.rs --- a/rust/rhg/src/main.rs +++ b/rust/rhg/src/main.rs @@ -6,6 +6,7 @@ use clap::ArgMatches; use format_bytes::{format_bytes, join}; use hg::config::Config; +use hg::exitcode; use hg::repo::{Repo, RepoError}; use hg::utils::files::{get_bytes_from_os_str, get_path_from_bytes}; use hg::utils::SliceExt; @@ -15,7 +16,6 @@ mod blackbox; mod error; -mod exitcode; mod ui; use error::CommandError;