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 | 489 | [[package]] |
|
490 | 490 | name = "rhg" |
|
491 | 491 | version = "0.1.0" |
|
492 | dependencies = [ | |
|
493 | "hg-core 0.1.0", | |
|
494 | ] | |
|
492 | 495 | |
|
493 | 496 | [[package]] |
|
494 | 497 | name = "rustc_version" |
@@ -5,4 +5,5 authors = ["Antoine Cezar <antoine.cezar | |||
|
5 | 5 | edition = "2018" |
|
6 | 6 | |
|
7 | 7 | [dependencies] |
|
8 | hg-core = { path = "../hg-core"} | |
|
8 | 9 |
@@ -1,3 +1,4 | |||
|
1 | pub mod root; | |
|
1 | 2 | use crate::error::CommandError; |
|
2 | 3 | |
|
3 | 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 | 32 | /// The error type for the Command trait |
|
2 | 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 | } |
General Comments 0
You need to be logged in to leave comments.
Login now