[Updated] D8612: rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`

acezar (Antoine Cezar) phabricator at mercurial-scm.org
Wed Jun 24 13:05:42 UTC 2020


acezar edited the summary of this revision.
acezar updated this revision to Diff 21697.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D8612?vs=21657&id=21697

BRANCH
  default

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8612/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8612

AFFECTED FILES
  rust/Cargo.lock
  rust/rhg/Cargo.toml
  rust/rhg/src/commands.rs
  rust/rhg/src/commands/root.rs
  rust/rhg/src/error.rs
  rust/rhg/src/exitcode.rs
  rust/rhg/src/main.rs
  rust/rhg/src/ui.rs

CHANGE DETAILS

diff --git a/rust/rhg/src/ui.rs b/rust/rhg/src/ui.rs
new file mode 100644
--- /dev/null
+++ b/rust/rhg/src/ui.rs
@@ -0,0 +1,54 @@
+use std::io;
+use std::io::Write;
+
+pub struct Ui {}
+
+/// The kind of user interface error
+pub enum UiError {
+    /// The standard output stream cannot be written to
+    StdoutError(io::Error),
+    /// The standard error stream cannot be written to
+    StderrError(io::Error),
+}
+
+/// The commandline user interface
+impl Ui {
+    pub fn new() -> Self {
+        Ui {}
+    }
+
+    /// Write bytes to stdout
+    pub fn write_stdout(&self, bytes: &[u8]) -> Result<(), UiError> {
+        let mut stdout = io::stdout();
+
+        self.write_stream(&mut stdout, bytes)
+            .or_else(|e| self.into_stdout_error(e))?;
+
+        stdout.flush().or_else(|e| self.into_stdout_error(e))
+    }
+
+    fn into_stdout_error(&self, error: io::Error) -> Result<(), UiError> {
+        self.write_stderr(
+            &[b"abort: ", error.to_string().as_bytes(), b"\n"].concat(),
+        )?;
+        Err(UiError::StdoutError(error))
+    }
+
+    /// Write bytes to stderr
+    pub fn write_stderr(&self, bytes: &[u8]) -> Result<(), UiError> {
+        let mut stderr = io::stderr();
+
+        self.write_stream(&mut stderr, bytes)
+            .or_else(|e| Err(UiError::StderrError(e)))?;
+
+        stderr.flush().or_else(|e| Err(UiError::StderrError(e)))
+    }
+
+    fn write_stream(
+        &self,
+        stream: &mut impl Write,
+        bytes: &[u8],
+    ) -> Result<(), io::Error> {
+        stream.write_all(bytes)
+    }
+}
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
@@ -1,6 +1,7 @@
 mod commands;
 mod error;
 mod exitcode;
+mod ui;
 
 fn main() {
     std::process::exit(exitcode::UNIMPLEMENTED_COMMAND)
diff --git a/rust/rhg/src/exitcode.rs b/rust/rhg/src/exitcode.rs
--- a/rust/rhg/src/exitcode.rs
+++ b/rust/rhg/src/exitcode.rs
@@ -1,4 +1,10 @@
 pub type ExitCode = i32;
 
+/// Successful exit
+pub const OK: ExitCode = 0;
+
+/// Generic abort
+pub const ABORT: ExitCode = 255;
+
 /// Command not implemented by rhg
 pub const UNIMPLEMENTED_COMMAND: ExitCode = 252;
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,3 +1,60 @@
+use crate::exitcode;
+use crate::ui::UiError;
+use std::convert::From;
+
+/// The kind of command error
+#[derive(Debug, PartialEq)]
+pub enum CommandErrorKind {
+    /// The command finished without error
+    Ok,
+    /// The root of the repository cannot be found
+    RootNotFound,
+    /// The current directory cannot be found
+    CurrentDirNotFound,
+    /// The standard output stream cannot be written to
+    StdoutError,
+    /// The standard error stream cannot be written to
+    StderrError,
+}
+
+impl CommandErrorKind {
+    pub fn get_exit_code(&self) -> exitcode::ExitCode {
+        match self {
+            CommandErrorKind::Ok => exitcode::OK,
+            CommandErrorKind::RootNotFound => exitcode::ABORT,
+            CommandErrorKind::CurrentDirNotFound => exitcode::ABORT,
+            CommandErrorKind::StdoutError => exitcode::ABORT,
+            CommandErrorKind::StderrError => exitcode::ABORT,
+        }
+    }
+}
+
 /// The error type for the Command trait
 #[derive(Debug, PartialEq)]
-pub struct CommandError {}
+pub struct CommandError {
+    pub kind: CommandErrorKind,
+}
+
+impl CommandError {
+    /// Exist the process with the corresponding exit code.
+    pub fn exit(&self) -> () {
+        std::process::exit(self.kind.get_exit_code())
+    }
+}
+
+impl From<CommandErrorKind> for CommandError {
+    fn from(kind: CommandErrorKind) -> Self {
+        CommandError { kind }
+    }
+}
+
+impl From<UiError> for CommandError {
+    fn from(error: UiError) -> Self {
+        CommandError {
+            kind: match error {
+                UiError::StdoutError(_) => CommandErrorKind::StdoutError,
+                UiError::StderrError(_) => CommandErrorKind::StderrError,
+            },
+        }
+    }
+}
diff --git a/rust/rhg/src/commands/root.rs b/rust/rhg/src/commands/root.rs
new file mode 100644
--- /dev/null
+++ b/rust/rhg/src/commands/root.rs
@@ -0,0 +1,76 @@
+use crate::commands::Command;
+use crate::error::{CommandError, CommandErrorKind};
+use crate::ui::Ui;
+use hg::operations::{FindRoot, FindRootError, FindRootErrorKind, Operation};
+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.
+
+Returns 0 on success.
+";
+
+pub struct RootCommand {
+    ui: Ui,
+}
+
+impl RootCommand {
+    pub fn new() -> Self {
+        RootCommand { ui: Ui::new() }
+    }
+
+    fn display_found_path(
+        &self,
+        path_buf: PathBuf,
+    ) -> Result<(), CommandError> {
+        let bytes = get_bytes_from_path(path_buf);
+
+        // TODO use formating macro
+        self.ui.write_stdout(&[bytes.as_slice(), b"\n"].concat())?;
+
+        Err(CommandErrorKind::Ok.into())
+    }
+
+    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),
+        }
+    }
+}
diff --git a/rust/rhg/src/commands.rs b/rust/rhg/src/commands.rs
--- a/rust/rhg/src/commands.rs
+++ b/rust/rhg/src/commands.rs
@@ -1,3 +1,4 @@
+pub mod root;
 use crate::error::CommandError;
 
 /// The common trait for rhg commands
diff --git a/rust/rhg/Cargo.toml b/rust/rhg/Cargo.toml
--- a/rust/rhg/Cargo.toml
+++ b/rust/rhg/Cargo.toml
@@ -5,4 +5,5 @@
 edition = "2018"
 
 [dependencies]
+hg-core = { path = "../hg-core"}
 
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -489,6 +489,9 @@
 [[package]]
 name = "rhg"
 version = "0.1.0"
+dependencies = [
+ "hg-core 0.1.0",
+]
 
 [[package]]
 name = "rustc_version"



To: acezar, #hg-reviewers, marmoute, Alphare
Cc: Alphare, marmoute, mercurial-patches
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.mercurial-scm.org/pipermail/mercurial-patches/attachments/20200624/4c164b80/attachment-0002.html>


More information about the Mercurial-patches mailing list