rhg: ask the error message from `CommandError`
authorAntoine Cezar <antoine.cezar@octobus.net>
Mon, 20 Jul 2020 18:14:52 +0200
changeset 45361 47997afadf08
parent 45360 227281e76c22
child 45362 eb55274d3650
rhg: ask the error message from `CommandError` Avoid repeating the display of the same error messages in different commands. Differential Revision: https://phab.mercurial-scm.org/D8865
rust/rhg/src/commands.rs
rust/rhg/src/commands/root.rs
rust/rhg/src/error.rs
rust/rhg/src/main.rs
--- a/rust/rhg/src/commands.rs	Tue Jul 21 10:39:30 2020 +0200
+++ b/rust/rhg/src/commands.rs	Mon Jul 20 18:14:52 2020 +0200
@@ -4,6 +4,6 @@
 /// The common trait for rhg commands
 ///
 /// Normalize the interface of the commands provided by rhg
-pub trait Command {
+pub trait Command<'a> {
     fn run(&self) -> Result<(), CommandError>;
 }
--- a/rust/rhg/src/commands/root.rs	Tue Jul 21 10:39:30 2020 +0200
+++ b/rust/rhg/src/commands/root.rs	Mon Jul 20 18:14:52 2020 +0200
@@ -1,9 +1,8 @@
 use crate::commands::Command;
 use crate::error::{CommandError, CommandErrorKind};
 use crate::ui::Ui;
-use hg::operations::{FindRoot, FindRootError, FindRootErrorKind};
+use hg::operations::{FindRoot, FindRootErrorKind};
 use hg::utils::files::get_bytes_from_path;
-use std::path::PathBuf;
 
 pub const HELP_TEXT: &str = "
 Print the root directory of the current repository.
@@ -11,19 +10,28 @@
 Returns 0 on success.
 ";
 
-pub struct RootCommand {
-    ui: Ui,
+pub struct RootCommand<'a> {
+    ui: &'a Ui,
+}
+
+impl<'a> RootCommand<'a> {
+    pub fn new(ui: &'a Ui) -> Self {
+        RootCommand { ui }
+    }
 }
 
-impl RootCommand {
-    pub fn new() -> Self {
-        RootCommand { ui: Ui::new() }
-    }
+impl<'a> Command<'a> for RootCommand<'a> {
+    fn run(&self) -> Result<(), CommandError> {
+        let path_buf =
+            FindRoot::new().run().map_err(|err| match err.kind {
+                FindRootErrorKind::RootNotFound(path) => {
+                    CommandErrorKind::RootNotFound(path)
+                }
+                FindRootErrorKind::GetCurrentDirError(e) => {
+                    CommandErrorKind::CurrentDirNotFound(e)
+                }
+            })?;
 
-    fn display_found_path(
-        &self,
-        path_buf: PathBuf,
-    ) -> Result<(), CommandError> {
         let bytes = get_bytes_from_path(path_buf);
 
         // TODO use formating macro
@@ -31,46 +39,4 @@
 
         Ok(())
     }
-
-    fn display_error(&self, error: FindRootError) -> Result<(), CommandError> {
-        match error.kind {
-            FindRootErrorKind::RootNotFound(path) => {
-                let bytes = get_bytes_from_path(path);
-
-                // TODO use formating macro
-                self.ui.write_stderr(
-                    &[
-                        b"abort: no repository found in '",
-                        bytes.as_slice(),
-                        b"' (.hg not found)!\n",
-                    ]
-                    .concat(),
-                )?;
-
-                Err(CommandErrorKind::RootNotFound.into())
-            }
-            FindRootErrorKind::GetCurrentDirError(e) => {
-                // TODO use formating macro
-                self.ui.write_stderr(
-                    &[
-                        b"abort: error getting current working directory: ",
-                        e.to_string().as_bytes(),
-                        b"\n",
-                    ]
-                    .concat(),
-                )?;
-
-                Err(CommandErrorKind::CurrentDirNotFound.into())
-            }
-        }
-    }
 }
-
-impl Command for RootCommand {
-    fn run(&self) -> Result<(), CommandError> {
-        match FindRoot::new().run() {
-            Ok(path_buf) => self.display_found_path(path_buf),
-            Err(e) => self.display_error(e),
-        }
-    }
-}
--- a/rust/rhg/src/error.rs	Tue Jul 21 10:39:30 2020 +0200
+++ b/rust/rhg/src/error.rs	Mon Jul 20 18:14:52 2020 +0200
@@ -1,14 +1,16 @@
 use crate::exitcode;
 use crate::ui::UiError;
+use hg::utils::files::get_bytes_from_path;
 use std::convert::From;
+use std::path::PathBuf;
 
 /// The kind of command error
-#[derive(Debug, PartialEq)]
+#[derive(Debug)]
 pub enum CommandErrorKind {
     /// The root of the repository cannot be found
-    RootNotFound,
+    RootNotFound(PathBuf),
     /// The current directory cannot be found
-    CurrentDirNotFound,
+    CurrentDirNotFound(std::io::Error),
     /// The standard output stream cannot be written to
     StdoutError,
     /// The standard error stream cannot be written to
@@ -18,16 +20,44 @@
 impl CommandErrorKind {
     pub fn get_exit_code(&self) -> exitcode::ExitCode {
         match self {
-            CommandErrorKind::RootNotFound => exitcode::ABORT,
-            CommandErrorKind::CurrentDirNotFound => exitcode::ABORT,
+            CommandErrorKind::RootNotFound(_) => exitcode::ABORT,
+            CommandErrorKind::CurrentDirNotFound(_) => exitcode::ABORT,
             CommandErrorKind::StdoutError => exitcode::ABORT,
             CommandErrorKind::StderrError => exitcode::ABORT,
         }
     }
+
+    /// Return the message corresponding to the error kind if any
+    pub fn get_error_message_bytes(&self) -> Option<Vec<u8>> {
+        match self {
+            // TODO use formating macro
+            CommandErrorKind::RootNotFound(path) => {
+                let bytes = get_bytes_from_path(path);
+                Some(
+                    [
+                        b"abort: no repository found in '",
+                        bytes.as_slice(),
+                        b"' (.hg not found)!\n",
+                    ]
+                    .concat(),
+                )
+            }
+            // TODO use formating macro
+            CommandErrorKind::CurrentDirNotFound(e) => Some(
+                [
+                    b"abort: error getting current working directory: ",
+                    e.to_string().as_bytes(),
+                    b"\n",
+                ]
+                .concat(),
+            ),
+            _ => None,
+        }
+    }
 }
 
 /// The error type for the Command trait
-#[derive(Debug, PartialEq)]
+#[derive(Debug)]
 pub struct CommandError {
     pub kind: CommandErrorKind,
 }
@@ -37,6 +67,11 @@
     pub fn exit(&self) -> () {
         std::process::exit(self.kind.get_exit_code())
     }
+
+    /// Return the message corresponding to the command error if any
+    pub fn get_error_message_bytes(&self) -> Option<Vec<u8>> {
+        self.kind.get_error_message_bytes()
+    }
 }
 
 impl From<CommandErrorKind> for CommandError {
--- a/rust/rhg/src/main.rs	Tue Jul 21 10:39:30 2020 +0200
+++ b/rust/rhg/src/main.rs	Mon Jul 20 18:14:52 2020 +0200
@@ -22,9 +22,11 @@
         std::process::exit(exitcode::UNIMPLEMENTED_COMMAND)
     });
 
+    let ui = ui::Ui::new();
+
     let command_result = match matches.subcommand_name() {
         Some(name) => match name {
-            "root" => commands::root::RootCommand::new().run(),
+            "root" => commands::root::RootCommand::new(&ui).run(),
             _ => std::process::exit(exitcode::UNIMPLEMENTED_COMMAND),
         },
         _ => {
@@ -37,6 +39,15 @@
 
     match command_result {
         Ok(_) => std::process::exit(exitcode::OK),
-        Err(e) => e.exit(),
+        Err(e) => {
+            let message = e.get_error_message_bytes();
+            if let Some(msg) = message {
+                match ui.write_stderr(&msg) {
+                    Ok(_) => (),
+                    Err(_) => std::process::exit(exitcode::ABORT),
+                };
+            };
+            e.exit()
+        }
     }
 }