changeset 47407:6e49769b7f97

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
author Pulkit Goyal <7895pulkit@gmail.com>
date Mon, 07 Jun 2021 17:27:49 +0530
parents 3237ed4dcda4
children 7954ee2d7cf7
files rust/hg-core/src/config/layer.rs rust/hg-core/src/errors.rs rust/hg-core/src/exit_codes.rs rust/hg-core/src/lib.rs rust/hg-core/src/repo.rs rust/rhg/src/error.rs rust/rhg/src/exitcode.rs rust/rhg/src/main.rs
diffstat 8 files changed, 61 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- a/rust/hg-core/src/config/layer.rs	Mon Jun 07 17:19:46 2021 +0530
+++ b/rust/hg-core/src/config/layer.rs	Mon Jun 07 17:27:49 2021 +0530
@@ -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 @@
             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() {
--- a/rust/hg-core/src/errors.rs	Mon Jun 07 17:19:46 2021 +0530
+++ b/rust/hg-core/src/errors.rs	Mon Jun 07 17:27:49 2021 +0530
@@ -1,4 +1,5 @@
 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 @@
 
     /// 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 @@
     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 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)
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-core/src/exit_codes.rs	Mon Jun 07 17:27:49 2021 +0530
@@ -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;
--- a/rust/hg-core/src/lib.rs	Mon Jun 07 17:19:46 2021 +0530
+++ b/rust/hg-core/src/lib.rs	Mon Jun 07 17:27:49 2021 +0530
@@ -11,6 +11,7 @@
 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::{
--- a/rust/hg-core/src/repo.rs	Mon Jun 07 17:19:46 2021 +0530
+++ b/rust/hg-core/src/repo.rs	Mon Jun 07 17:27:49 2021 +0530
@@ -1,5 +1,6 @@
 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 @@
                     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 @@
                             "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"),
                     }
--- a/rust/rhg/src/error.rs	Mon Jun 07 17:19:46 2021 +0530
+++ b/rust/rhg/src/error.rs	Mon Jun 07 17:27:49 2021 +0530
@@ -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::exit_codes;
 use hg::repo::RepoError;
 use hg::revlog::revlog::RevlogError;
 use hg::utils::files::get_bytes_from_path;
@@ -17,7 +17,7 @@
     /// 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 @@
 
 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 @@
     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 @@
                     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 @@
                 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 @@
                 line_message,
                 message
             ),
-            detailed_exit_code: exitcode::CONFIG_ERROR_ABORT,
+            detailed_exit_code: exit_codes::CONFIG_ERROR_ABORT,
         }
     }
 }
--- a/rust/rhg/src/exitcode.rs	Mon Jun 07 17:19:46 2021 +0530
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -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;
--- a/rust/rhg/src/main.rs	Mon Jun 07 17:19:46 2021 +0530
+++ b/rust/rhg/src/main.rs	Mon Jun 07 17:27:49 2021 +0530
@@ -6,6 +6,7 @@
 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 @@
 
 mod blackbox;
 mod error;
-mod exitcode;
 mod ui;
 use error::CommandError;
 
@@ -297,7 +297,7 @@
     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 @@
             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 @@
             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!(