rust/rhg/src/error.rs
changeset 46445 ca3f73cc3cf4
parent 46443 43d63979a75e
child 46446 1dcd9c9975ed
--- a/rust/rhg/src/error.rs	Thu Jan 28 19:21:57 2021 +0100
+++ b/rust/rhg/src/error.rs	Thu Jan 28 19:13:55 2021 +0100
@@ -1,101 +1,65 @@
-use crate::exitcode;
 use crate::ui::utf8_to_local;
 use crate::ui::UiError;
-use format_bytes::format_bytes;
-use hg::errors::HgError;
+use hg::errors::{HgError, IoErrorContext};
 use hg::operations::FindRootError;
 use hg::revlog::revlog::RevlogError;
-use hg::utils::files::get_bytes_from_path;
 use std::convert::From;
-use std::path::PathBuf;
 
 /// The kind of command error
-#[derive(Debug, derive_more::From)]
+#[derive(Debug)]
 pub enum CommandError {
-    /// The root of the repository cannot be found
-    RootNotFound(PathBuf),
-    /// The current directory cannot be found
-    CurrentDirNotFound(std::io::Error),
-    /// The standard output stream cannot be written to
-    StdoutError,
-    /// The standard error stream cannot be written to
-    StderrError,
-    /// The command aborted
-    Abort(Option<Vec<u8>>),
+    /// Exit with an error message and "standard" failure exit code.
+    Abort { message: Vec<u8> },
+
     /// A mercurial capability as not been implemented.
+    ///
+    /// There is no error message printed in this case.
+    /// Instead, we exit with a specic status code and a wrapper script may
+    /// fallback to Python-based Mercurial.
     Unimplemented,
-    /// Common cases
-    #[from]
-    Other(HgError),
 }
 
 impl CommandError {
-    pub fn get_exit_code(&self) -> exitcode::ExitCode {
-        match self {
-            CommandError::RootNotFound(_) => exitcode::ABORT,
-            CommandError::CurrentDirNotFound(_) => exitcode::ABORT,
-            CommandError::StdoutError => exitcode::ABORT,
-            CommandError::StderrError => exitcode::ABORT,
-            CommandError::Abort(_) => exitcode::ABORT,
-            CommandError::Unimplemented => exitcode::UNIMPLEMENTED_COMMAND,
-            CommandError::Other(HgError::UnsupportedFeature(_)) => {
-                exitcode::UNIMPLEMENTED_COMMAND
-            }
-            CommandError::Other(_) => exitcode::ABORT,
+    pub fn abort(message: impl AsRef<str>) -> Self {
+        CommandError::Abort {
+            // TODO: bytes-based (instead of Unicode-based) formatting
+            // of error messages to handle non-UTF-8 filenames etc:
+            // https://www.mercurial-scm.org/wiki/EncodingStrategy#Mixing_output
+            message: utf8_to_local(message.as_ref()).into(),
         }
     }
+}
 
-    /// Return the message corresponding to the error if any
-    pub fn get_error_message_bytes(&self) -> Option<Vec<u8>> {
-        match self {
-            CommandError::RootNotFound(path) => {
-                let bytes = get_bytes_from_path(path);
-                Some(format_bytes!(
-                    b"abort: no repository found in '{}' (.hg not found)!\n",
-                    bytes.as_slice()
-                ))
-            }
-            CommandError::CurrentDirNotFound(e) => Some(format_bytes!(
-                b"abort: error getting current working directory: {}\n",
-                e.to_string().as_bytes(),
-            )),
-            CommandError::Abort(message) => message.to_owned(),
-
-            CommandError::StdoutError
-            | CommandError::StderrError
-            | CommandError::Unimplemented
-            | CommandError::Other(HgError::UnsupportedFeature(_)) => None,
-
-            CommandError::Other(e) => {
-                Some(format_bytes!(b"{}\n", e.to_string().as_bytes()))
-            }
+impl From<HgError> for CommandError {
+    fn from(error: HgError) -> Self {
+        match error {
+            HgError::UnsupportedFeature(_) => CommandError::Unimplemented,
+            _ => CommandError::abort(error.to_string()),
         }
     }
-
-    /// Exist the process with the corresponding exit code.
-    pub fn exit(&self) {
-        std::process::exit(self.get_exit_code())
-    }
 }
 
 impl From<UiError> for CommandError {
-    fn from(error: UiError) -> Self {
-        match error {
-            UiError::StdoutError(_) => CommandError::StdoutError,
-            UiError::StderrError(_) => CommandError::StderrError,
-        }
+    fn from(_error: UiError) -> Self {
+        // If we already failed writing to stdout or stderr,
+        // writing an error message to stderr about it would be likely to fail
+        // too.
+        CommandError::abort("")
     }
 }
 
 impl From<FindRootError> for CommandError {
     fn from(err: FindRootError) -> Self {
         match err {
-            FindRootError::RootNotFound(path) => {
-                CommandError::RootNotFound(path)
+            FindRootError::RootNotFound(path) => CommandError::abort(format!(
+                "no repository found in '{}' (.hg not found)!",
+                path.display()
+            )),
+            FindRootError::GetCurrentDirError(error) => HgError::IoError {
+                error,
+                context: IoErrorContext::CurrentDir,
             }
-            FindRootError::GetCurrentDirError(e) => {
-                CommandError::CurrentDirNotFound(e)
-            }
+            .into(),
         }
     }
 }
@@ -103,21 +67,15 @@
 impl From<(RevlogError, &str)> for CommandError {
     fn from((err, rev): (RevlogError, &str)) -> CommandError {
         match err {
-            RevlogError::InvalidRevision => CommandError::Abort(Some(
-                utf8_to_local(&format!(
-                    "abort: invalid revision identifier {}\n",
-                    rev
-                ))
-                .into(),
+            RevlogError::InvalidRevision => CommandError::abort(format!(
+                "invalid revision identifier {}",
+                rev
             )),
-            RevlogError::AmbiguousPrefix => CommandError::Abort(Some(
-                utf8_to_local(&format!(
-                    "abort: ambiguous revision identifier {}\n",
-                    rev
-                ))
-                .into(),
+            RevlogError::AmbiguousPrefix => CommandError::abort(format!(
+                "ambiguous revision identifier {}",
+                rev
             )),
-            RevlogError::Other(err) => CommandError::Other(err),
+            RevlogError::Other(error) => error.into(),
         }
     }
 }