Show More
@@ -0,0 +1,76 | |||||
|
1 | use crate::commands::Command; | |||
|
2 | use crate::error::{CommandError, CommandErrorKind}; | |||
|
3 | use crate::ui::Ui; | |||
|
4 | use hg::operations::{FindRoot, FindRootError, FindRootErrorKind, Operation}; | |||
|
5 | use hg::utils::files::get_bytes_from_path; | |||
|
6 | use std::path::PathBuf; | |||
|
7 | ||||
|
8 | pub const HELP_TEXT: &str = " | |||
|
9 | Print the root directory of the current repository. | |||
|
10 | ||||
|
11 | Returns 0 on success. | |||
|
12 | "; | |||
|
13 | ||||
|
14 | pub struct RootCommand { | |||
|
15 | ui: Ui, | |||
|
16 | } | |||
|
17 | ||||
|
18 | impl RootCommand { | |||
|
19 | pub fn new() -> Self { | |||
|
20 | RootCommand { ui: Ui::new() } | |||
|
21 | } | |||
|
22 | ||||
|
23 | fn display_found_path( | |||
|
24 | &self, | |||
|
25 | path_buf: PathBuf, | |||
|
26 | ) -> Result<(), CommandError> { | |||
|
27 | let bytes = get_bytes_from_path(path_buf); | |||
|
28 | ||||
|
29 | // TODO use formating macro | |||
|
30 | self.ui.write_stdout(&[bytes.as_slice(), b"\n"].concat())?; | |||
|
31 | ||||
|
32 | Err(CommandErrorKind::Ok.into()) | |||
|
33 | } | |||
|
34 | ||||
|
35 | fn display_error(&self, error: FindRootError) -> Result<(), CommandError> { | |||
|
36 | match error.kind { | |||
|
37 | FindRootErrorKind::RootNotFound(path) => { | |||
|
38 | let bytes = get_bytes_from_path(path); | |||
|
39 | ||||
|
40 | // TODO use formating macro | |||
|
41 | self.ui.write_stderr( | |||
|
42 | &[ | |||
|
43 | b"abort: no repository found in '", | |||
|
44 | bytes.as_slice(), | |||
|
45 | b"' (.hg not found)!\n", | |||
|
46 | ] | |||
|
47 | .concat(), | |||
|
48 | )?; | |||
|
49 | ||||
|
50 | Err(CommandErrorKind::RootNotFound.into()) | |||
|
51 | } | |||
|
52 | FindRootErrorKind::GetCurrentDirError(e) => { | |||
|
53 | // TODO use formating macro | |||
|
54 | self.ui.write_stderr( | |||
|
55 | &[ | |||
|
56 | b"abort: error getting current working directory: ", | |||
|
57 | e.to_string().as_bytes(), | |||
|
58 | b"\n", | |||
|
59 | ] | |||
|
60 | .concat(), | |||
|
61 | )?; | |||
|
62 | ||||
|
63 | Err(CommandErrorKind::CurrentDirNotFound.into()) | |||
|
64 | } | |||
|
65 | } | |||
|
66 | } | |||
|
67 | } | |||
|
68 | ||||
|
69 | impl Command for RootCommand { | |||
|
70 | fn run(&self) -> Result<(), CommandError> { | |||
|
71 | match FindRoot::new().run() { | |||
|
72 | Ok(path_buf) => self.display_found_path(path_buf), | |||
|
73 | Err(e) => self.display_error(e), | |||
|
74 | } | |||
|
75 | } | |||
|
76 | } |
@@ -0,0 +1,54 | |||||
|
1 | use std::io; | |||
|
2 | use std::io::Write; | |||
|
3 | ||||
|
4 | pub struct Ui {} | |||
|
5 | ||||
|
6 | /// The kind of user interface error | |||
|
7 | pub enum UiError { | |||
|
8 | /// The standard output stream cannot be written to | |||
|
9 | StdoutError(io::Error), | |||
|
10 | /// The standard error stream cannot be written to | |||
|
11 | StderrError(io::Error), | |||
|
12 | } | |||
|
13 | ||||
|
14 | /// The commandline user interface | |||
|
15 | impl Ui { | |||
|
16 | pub fn new() -> Self { | |||
|
17 | Ui {} | |||
|
18 | } | |||
|
19 | ||||
|
20 | /// Write bytes to stdout | |||
|
21 | pub fn write_stdout(&self, bytes: &[u8]) -> Result<(), UiError> { | |||
|
22 | let mut stdout = io::stdout(); | |||
|
23 | ||||
|
24 | self.write_stream(&mut stdout, bytes) | |||
|
25 | .or_else(|e| self.into_stdout_error(e))?; | |||
|
26 | ||||
|
27 | stdout.flush().or_else(|e| self.into_stdout_error(e)) | |||
|
28 | } | |||
|
29 | ||||
|
30 | fn into_stdout_error(&self, error: io::Error) -> Result<(), UiError> { | |||
|
31 | self.write_stderr( | |||
|
32 | &[b"abort: ", error.to_string().as_bytes(), b"\n"].concat(), | |||
|
33 | )?; | |||
|
34 | Err(UiError::StdoutError(error)) | |||
|
35 | } | |||
|
36 | ||||
|
37 | /// Write bytes to stderr | |||
|
38 | pub fn write_stderr(&self, bytes: &[u8]) -> Result<(), UiError> { | |||
|
39 | let mut stderr = io::stderr(); | |||
|
40 | ||||
|
41 | self.write_stream(&mut stderr, bytes) | |||
|
42 | .or_else(|e| Err(UiError::StderrError(e)))?; | |||
|
43 | ||||
|
44 | stderr.flush().or_else(|e| Err(UiError::StderrError(e))) | |||
|
45 | } | |||
|
46 | ||||
|
47 | fn write_stream( | |||
|
48 | &self, | |||
|
49 | stream: &mut impl Write, | |||
|
50 | bytes: &[u8], | |||
|
51 | ) -> Result<(), io::Error> { | |||
|
52 | stream.write_all(bytes) | |||
|
53 | } | |||
|
54 | } |
@@ -489,6 +489,9 dependencies = [ | |||||
489 | [[package]] |
|
489 | [[package]] | |
490 | name = "rhg" |
|
490 | name = "rhg" | |
491 | version = "0.1.0" |
|
491 | version = "0.1.0" | |
|
492 | dependencies = [ | |||
|
493 | "hg-core 0.1.0", | |||
|
494 | ] | |||
492 |
|
495 | |||
493 | [[package]] |
|
496 | [[package]] | |
494 | name = "rustc_version" |
|
497 | name = "rustc_version" |
@@ -5,4 +5,5 authors = ["Antoine Cezar <antoine.cezar | |||||
5 | edition = "2018" |
|
5 | edition = "2018" | |
6 |
|
6 | |||
7 | [dependencies] |
|
7 | [dependencies] | |
|
8 | hg-core = { path = "../hg-core"} | |||
8 |
|
9 |
@@ -1,3 +1,4 | |||||
|
1 | pub mod root; | |||
1 | use crate::error::CommandError; |
|
2 | use crate::error::CommandError; | |
2 |
|
3 | |||
3 | /// The common trait for rhg commands |
|
4 | /// The common trait for rhg commands |
@@ -1,3 +1,60 | |||||
|
1 | use crate::exitcode; | |||
|
2 | use crate::ui::UiError; | |||
|
3 | use std::convert::From; | |||
|
4 | ||||
|
5 | /// The kind of command error | |||
|
6 | #[derive(Debug, PartialEq)] | |||
|
7 | pub enum CommandErrorKind { | |||
|
8 | /// The command finished without error | |||
|
9 | Ok, | |||
|
10 | /// The root of the repository cannot be found | |||
|
11 | RootNotFound, | |||
|
12 | /// The current directory cannot be found | |||
|
13 | CurrentDirNotFound, | |||
|
14 | /// The standard output stream cannot be written to | |||
|
15 | StdoutError, | |||
|
16 | /// The standard error stream cannot be written to | |||
|
17 | StderrError, | |||
|
18 | } | |||
|
19 | ||||
|
20 | impl CommandErrorKind { | |||
|
21 | pub fn get_exit_code(&self) -> exitcode::ExitCode { | |||
|
22 | match self { | |||
|
23 | CommandErrorKind::Ok => exitcode::OK, | |||
|
24 | CommandErrorKind::RootNotFound => exitcode::ABORT, | |||
|
25 | CommandErrorKind::CurrentDirNotFound => exitcode::ABORT, | |||
|
26 | CommandErrorKind::StdoutError => exitcode::ABORT, | |||
|
27 | CommandErrorKind::StderrError => exitcode::ABORT, | |||
|
28 | } | |||
|
29 | } | |||
|
30 | } | |||
|
31 | ||||
1 | /// The error type for the Command trait |
|
32 | /// The error type for the Command trait | |
2 | #[derive(Debug, PartialEq)] |
|
33 | #[derive(Debug, PartialEq)] | |
3 |
pub struct CommandError { |
|
34 | pub struct CommandError { | |
|
35 | pub kind: CommandErrorKind, | |||
|
36 | } | |||
|
37 | ||||
|
38 | impl CommandError { | |||
|
39 | /// Exist the process with the corresponding exit code. | |||
|
40 | pub fn exit(&self) -> () { | |||
|
41 | std::process::exit(self.kind.get_exit_code()) | |||
|
42 | } | |||
|
43 | } | |||
|
44 | ||||
|
45 | impl From<CommandErrorKind> for CommandError { | |||
|
46 | fn from(kind: CommandErrorKind) -> Self { | |||
|
47 | CommandError { kind } | |||
|
48 | } | |||
|
49 | } | |||
|
50 | ||||
|
51 | impl From<UiError> for CommandError { | |||
|
52 | fn from(error: UiError) -> Self { | |||
|
53 | CommandError { | |||
|
54 | kind: match error { | |||
|
55 | UiError::StdoutError(_) => CommandErrorKind::StdoutError, | |||
|
56 | UiError::StderrError(_) => CommandErrorKind::StderrError, | |||
|
57 | }, | |||
|
58 | } | |||
|
59 | } | |||
|
60 | } |
@@ -1,4 +1,10 | |||||
1 | pub type ExitCode = i32; |
|
1 | pub type ExitCode = i32; | |
2 |
|
2 | |||
|
3 | /// Successful exit | |||
|
4 | pub const OK: ExitCode = 0; | |||
|
5 | ||||
|
6 | /// Generic abort | |||
|
7 | pub const ABORT: ExitCode = 255; | |||
|
8 | ||||
3 | /// Command not implemented by rhg |
|
9 | /// Command not implemented by rhg | |
4 | pub const UNIMPLEMENTED_COMMAND: ExitCode = 252; |
|
10 | pub const UNIMPLEMENTED_COMMAND: ExitCode = 252; |
General Comments 0
You need to be logged in to leave comments.
Login now