Read about our upcoming Code of Conduct on this issue

This instance was upgraded to Heptapod 0.28.1 today

Commit f2fe1b2f authored by Pulkit Goyal's avatar Pulkit Goyal
Browse files

rhg: add exit code to HgError::Abort()

My previous attempts to have rhg end with correct exit code was more of bug
hunting. I found cases which were failing and fixed them. But as one might
expect, more tests started failing.

Let's add exit code `HgError::Abort()` and make it users explicitly tell what
exit code they want.

Differential Revision: https://phab.mercurial-scm.org/D10838
parent ef123ade2c2d
......@@ -8,6 +8,7 @@
// GNU General Public License version 2 or any later version.
use crate::errors::HgError;
use crate::exit_codes::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 @@ impl ConfigLayer {
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() {
......
use crate::config::ConfigValueParseError;
use crate::exit_codes;
use std::fmt;
/// Common error cases that can happen in many different APIs
......@@ -27,9 +28,12 @@ pub enum HgError {
/// 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: exit_codes::ExitCode,
},
/// A configuration value is not in the expected syntax.
///
......@@ -69,8 +73,15 @@ impl HgError {
pub fn unsupported(explanation: impl Into<String>) -> Self {
HgError::UnsupportedFeature(explanation.into())
}
pub fn abort(explanation: impl Into<String>) -> Self {
HgError::Abort(explanation.into())
pub fn abort(
explanation: impl Into<String>,
exit_code: exit_codes::ExitCode,
) -> Self {
HgError::Abort {
message: explanation.into(),
detailed_exit_code: exit_code,
}
}
}
......@@ -78,7 +89,7 @@ impl HgError {
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)
}
......
......@@ -9,6 +9,9 @@ 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;
......
......@@ -11,6 +11,7 @@ pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors};
pub mod dirstate;
pub mod dirstate_tree;
pub mod discovery;
pub mod exit_codes;
pub mod requirements;
pub mod testing; // unconditionally built, for use from integration tests
pub use dirstate::{
......
use crate::config::{Config, ConfigError, ConfigParseError};
use crate::errors::{HgError, IoErrorContext, IoResultExt};
use crate::exit_codes;
use crate::requirements;
use crate::utils::files::get_path_from_bytes;
use crate::utils::SliceExt;
......@@ -150,6 +151,7 @@ impl Repo {
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)",
exit_codes::ABORT,
),
_ => HgError::unsupported("share-safe downgrade"),
}
......@@ -161,6 +163,7 @@ impl Repo {
"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)",
exit_codes::ABORT,
),
_ => HgError::unsupported("share-safe upgrade"),
}
......
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::exit_codes;
use hg::repo::RepoError;
use hg::revlog::revlog::RevlogError;
use hg::utils::files::get_bytes_from_path;
......@@ -17,7 +17,7 @@ pub enum CommandError {
/// Exit with an error message and "standard" failure exit code.
Abort {
message: Vec<u8>,
detailed_exit_code: exitcode::ExitCode,
detailed_exit_code: exit_codes::ExitCode,
},
/// Exit with a failure exit code but no message.
......@@ -32,12 +32,12 @@ pub enum CommandError {
impl CommandError {
pub fn abort(message: impl AsRef<str>) -> Self {
CommandError::abort_with_exit_code(message, exitcode::ABORT)
CommandError::abort_with_exit_code(message, exit_codes::ABORT)
}
pub fn abort_with_exit_code(
message: impl AsRef<str>,
detailed_exit_code: exitcode::ExitCode,
detailed_exit_code: exit_codes::ExitCode,
) -> Self {
CommandError::Abort {
// TODO: bytes-based (instead of Unicode-based) formatting
......@@ -78,7 +78,7 @@ impl From<ConfigValueParseError> for CommandError {
fn from(error: ConfigValueParseError) -> Self {
CommandError::abort_with_exit_code(
error.to_string(),
exitcode::CONFIG_ERROR_ABORT,
exit_codes::CONFIG_ERROR_ABORT,
)
}
}
......@@ -100,7 +100,7 @@ impl From<RepoError> for CommandError {
b"abort: repository {} not found",
get_bytes_from_path(at)
),
detailed_exit_code: exitcode::ABORT,
detailed_exit_code: exit_codes::ABORT,
},
RepoError::ConfigParseError(error) => error.into(),
RepoError::Other(error) => error.into(),
......@@ -116,7 +116,7 @@ impl<'a> From<&'a NoRepoInCwdError> for CommandError {
b"abort: no repository found in '{}' (.hg not found)!",
get_bytes_from_path(cwd)
),
detailed_exit_code: exitcode::ABORT,
detailed_exit_code: exit_codes::ABORT,
}
}
}
......@@ -149,7 +149,7 @@ impl From<ConfigParseError> for CommandError {
line_message,
message
),
detailed_exit_code: exitcode::CONFIG_ERROR_ABORT,
detailed_exit_code: exit_codes::CONFIG_ERROR_ABORT,
}
}
}
......
......@@ -6,6 +6,7 @@ use clap::Arg;
use clap::ArgMatches;
use format_bytes::{format_bytes, join};
use hg::config::{Config, ConfigSource};
use hg::exit_codes;
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 @@ use std::process::Command;
mod blackbox;
mod error;
mod exitcode;
mod ui;
use error::CommandError;
......@@ -297,7 +297,7 @@ fn exit_code(
use_detailed_exit_code: bool,
) -> i32 {
match result {
Ok(()) => exitcode::OK,
Ok(()) => exit_codes::OK,
Err(CommandError::Abort {
message: _,
detailed_exit_code,
......@@ -305,15 +305,15 @@ fn exit_code(
if use_detailed_exit_code {
*detailed_exit_code
} else {
exitcode::ABORT
exit_codes::ABORT
}
}
Err(CommandError::Unsuccessful) => exitcode::UNSUCCESSFUL,
Err(CommandError::Unsuccessful) => exit_codes::UNSUCCESSFUL,
// Exit with a specific code and no error message to let a potential
// wrapper script fallback to Python-based Mercurial.
Err(CommandError::UnsupportedFeature { .. }) => {
exitcode::UNIMPLEMENTED
exit_codes::UNIMPLEMENTED
}
}
}
......@@ -352,7 +352,7 @@ fn exit(
let result = command.status();
match result {
Ok(status) => std::process::exit(
status.code().unwrap_or(exitcode::ABORT),
status.code().unwrap_or(exit_codes::ABORT),
),
Err(error) => {
let _ = ui.write_stderr(&format_bytes!(
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment