Show More
@@ -1,9 +1,9 b'' | |||
|
1 | 1 | pub mod root; |
|
2 | 2 | use crate::error::CommandError; |
|
3 | 3 | |
|
4 | 4 | /// The common trait for rhg commands |
|
5 | 5 | /// |
|
6 | 6 | /// Normalize the interface of the commands provided by rhg |
|
7 | pub trait Command { | |
|
7 | pub trait Command<'a> { | |
|
8 | 8 | fn run(&self) -> Result<(), CommandError>; |
|
9 | 9 | } |
@@ -1,76 +1,42 b'' | |||
|
1 | 1 | use crate::commands::Command; |
|
2 | 2 | use crate::error::{CommandError, CommandErrorKind}; |
|
3 | 3 | use crate::ui::Ui; |
|
4 |
use hg::operations::{FindRoot, |
|
|
4 | use hg::operations::{FindRoot, FindRootErrorKind}; | |
|
5 | 5 | use hg::utils::files::get_bytes_from_path; |
|
6 | use std::path::PathBuf; | |
|
7 | 6 | |
|
8 | 7 | pub const HELP_TEXT: &str = " |
|
9 | 8 | Print the root directory of the current repository. |
|
10 | 9 | |
|
11 | 10 | Returns 0 on success. |
|
12 | 11 | "; |
|
13 | 12 | |
|
14 | pub struct RootCommand { | |
|
15 | ui: Ui, | |
|
13 | pub struct RootCommand<'a> { | |
|
14 | ui: &'a Ui, | |
|
15 | } | |
|
16 | ||
|
17 | impl<'a> RootCommand<'a> { | |
|
18 | pub fn new(ui: &'a Ui) -> Self { | |
|
19 | RootCommand { ui } | |
|
20 | } | |
|
16 | 21 | } |
|
17 | 22 | |
|
18 | impl RootCommand { | |
|
19 | pub fn new() -> Self { | |
|
20 | RootCommand { ui: Ui::new() } | |
|
21 | } | |
|
23 | impl<'a> Command<'a> for RootCommand<'a> { | |
|
24 | fn run(&self) -> Result<(), CommandError> { | |
|
25 | let path_buf = | |
|
26 | FindRoot::new().run().map_err(|err| match err.kind { | |
|
27 | FindRootErrorKind::RootNotFound(path) => { | |
|
28 | CommandErrorKind::RootNotFound(path) | |
|
29 | } | |
|
30 | FindRootErrorKind::GetCurrentDirError(e) => { | |
|
31 | CommandErrorKind::CurrentDirNotFound(e) | |
|
32 | } | |
|
33 | })?; | |
|
22 | 34 | |
|
23 | fn display_found_path( | |
|
24 | &self, | |
|
25 | path_buf: PathBuf, | |
|
26 | ) -> Result<(), CommandError> { | |
|
27 | 35 | let bytes = get_bytes_from_path(path_buf); |
|
28 | 36 | |
|
29 | 37 | // TODO use formating macro |
|
30 | 38 | self.ui.write_stdout(&[bytes.as_slice(), b"\n"].concat())?; |
|
31 | 39 | |
|
32 | 40 | Ok(()) |
|
33 | 41 | } |
|
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 | 42 | } |
|
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 | } |
@@ -1,57 +1,92 b'' | |||
|
1 | 1 | use crate::exitcode; |
|
2 | 2 | use crate::ui::UiError; |
|
3 | use hg::utils::files::get_bytes_from_path; | |
|
3 | 4 | use std::convert::From; |
|
5 | use std::path::PathBuf; | |
|
4 | 6 | |
|
5 | 7 | /// The kind of command error |
|
6 |
#[derive(Debug |
|
|
8 | #[derive(Debug)] | |
|
7 | 9 | pub enum CommandErrorKind { |
|
8 | 10 | /// The root of the repository cannot be found |
|
9 | RootNotFound, | |
|
11 | RootNotFound(PathBuf), | |
|
10 | 12 | /// The current directory cannot be found |
|
11 | CurrentDirNotFound, | |
|
13 | CurrentDirNotFound(std::io::Error), | |
|
12 | 14 | /// The standard output stream cannot be written to |
|
13 | 15 | StdoutError, |
|
14 | 16 | /// The standard error stream cannot be written to |
|
15 | 17 | StderrError, |
|
16 | 18 | } |
|
17 | 19 | |
|
18 | 20 | impl CommandErrorKind { |
|
19 | 21 | pub fn get_exit_code(&self) -> exitcode::ExitCode { |
|
20 | 22 | match self { |
|
21 | CommandErrorKind::RootNotFound => exitcode::ABORT, | |
|
22 | CommandErrorKind::CurrentDirNotFound => exitcode::ABORT, | |
|
23 | CommandErrorKind::RootNotFound(_) => exitcode::ABORT, | |
|
24 | CommandErrorKind::CurrentDirNotFound(_) => exitcode::ABORT, | |
|
23 | 25 | CommandErrorKind::StdoutError => exitcode::ABORT, |
|
24 | 26 | CommandErrorKind::StderrError => exitcode::ABORT, |
|
25 | 27 | } |
|
26 | 28 | } |
|
29 | ||
|
30 | /// Return the message corresponding to the error kind if any | |
|
31 | pub fn get_error_message_bytes(&self) -> Option<Vec<u8>> { | |
|
32 | match self { | |
|
33 | // TODO use formating macro | |
|
34 | CommandErrorKind::RootNotFound(path) => { | |
|
35 | let bytes = get_bytes_from_path(path); | |
|
36 | Some( | |
|
37 | [ | |
|
38 | b"abort: no repository found in '", | |
|
39 | bytes.as_slice(), | |
|
40 | b"' (.hg not found)!\n", | |
|
41 | ] | |
|
42 | .concat(), | |
|
43 | ) | |
|
44 | } | |
|
45 | // TODO use formating macro | |
|
46 | CommandErrorKind::CurrentDirNotFound(e) => Some( | |
|
47 | [ | |
|
48 | b"abort: error getting current working directory: ", | |
|
49 | e.to_string().as_bytes(), | |
|
50 | b"\n", | |
|
51 | ] | |
|
52 | .concat(), | |
|
53 | ), | |
|
54 | _ => None, | |
|
55 | } | |
|
56 | } | |
|
27 | 57 | } |
|
28 | 58 | |
|
29 | 59 | /// The error type for the Command trait |
|
30 |
#[derive(Debug |
|
|
60 | #[derive(Debug)] | |
|
31 | 61 | pub struct CommandError { |
|
32 | 62 | pub kind: CommandErrorKind, |
|
33 | 63 | } |
|
34 | 64 | |
|
35 | 65 | impl CommandError { |
|
36 | 66 | /// Exist the process with the corresponding exit code. |
|
37 | 67 | pub fn exit(&self) -> () { |
|
38 | 68 | std::process::exit(self.kind.get_exit_code()) |
|
39 | 69 | } |
|
70 | ||
|
71 | /// Return the message corresponding to the command error if any | |
|
72 | pub fn get_error_message_bytes(&self) -> Option<Vec<u8>> { | |
|
73 | self.kind.get_error_message_bytes() | |
|
74 | } | |
|
40 | 75 | } |
|
41 | 76 | |
|
42 | 77 | impl From<CommandErrorKind> for CommandError { |
|
43 | 78 | fn from(kind: CommandErrorKind) -> Self { |
|
44 | 79 | CommandError { kind } |
|
45 | 80 | } |
|
46 | 81 | } |
|
47 | 82 | |
|
48 | 83 | impl From<UiError> for CommandError { |
|
49 | 84 | fn from(error: UiError) -> Self { |
|
50 | 85 | CommandError { |
|
51 | 86 | kind: match error { |
|
52 | 87 | UiError::StdoutError(_) => CommandErrorKind::StdoutError, |
|
53 | 88 | UiError::StderrError(_) => CommandErrorKind::StderrError, |
|
54 | 89 | }, |
|
55 | 90 | } |
|
56 | 91 | } |
|
57 | 92 | } |
@@ -1,42 +1,53 b'' | |||
|
1 | 1 | use clap::App; |
|
2 | 2 | use clap::AppSettings; |
|
3 | 3 | use clap::SubCommand; |
|
4 | 4 | |
|
5 | 5 | mod commands; |
|
6 | 6 | mod error; |
|
7 | 7 | mod exitcode; |
|
8 | 8 | mod ui; |
|
9 | 9 | use commands::Command; |
|
10 | 10 | |
|
11 | 11 | fn main() { |
|
12 | 12 | let mut app = App::new("rhg") |
|
13 | 13 | .setting(AppSettings::AllowInvalidUtf8) |
|
14 | 14 | .setting(AppSettings::SubcommandRequired) |
|
15 | 15 | .setting(AppSettings::VersionlessSubcommands) |
|
16 | 16 | .version("0.0.1") |
|
17 | 17 | .subcommand( |
|
18 | 18 | SubCommand::with_name("root").about(commands::root::HELP_TEXT), |
|
19 | 19 | ); |
|
20 | 20 | |
|
21 | 21 | let matches = app.clone().get_matches_safe().unwrap_or_else(|_| { |
|
22 | 22 | std::process::exit(exitcode::UNIMPLEMENTED_COMMAND) |
|
23 | 23 | }); |
|
24 | 24 | |
|
25 | let ui = ui::Ui::new(); | |
|
26 | ||
|
25 | 27 | let command_result = match matches.subcommand_name() { |
|
26 | 28 | Some(name) => match name { |
|
27 | "root" => commands::root::RootCommand::new().run(), | |
|
29 | "root" => commands::root::RootCommand::new(&ui).run(), | |
|
28 | 30 | _ => std::process::exit(exitcode::UNIMPLEMENTED_COMMAND), |
|
29 | 31 | }, |
|
30 | 32 | _ => { |
|
31 | 33 | match app.print_help() { |
|
32 | 34 | Ok(_) => std::process::exit(exitcode::OK), |
|
33 | 35 | Err(_) => std::process::exit(exitcode::ABORT), |
|
34 | 36 | }; |
|
35 | 37 | } |
|
36 | 38 | }; |
|
37 | 39 | |
|
38 | 40 | match command_result { |
|
39 | 41 | Ok(_) => std::process::exit(exitcode::OK), |
|
40 |
Err(e) => |
|
|
42 | Err(e) => { | |
|
43 | let message = e.get_error_message_bytes(); | |
|
44 | if let Some(msg) = message { | |
|
45 | match ui.write_stderr(&msg) { | |
|
46 | Ok(_) => (), | |
|
47 | Err(_) => std::process::exit(exitcode::ABORT), | |
|
48 | }; | |
|
49 | }; | |
|
50 | e.exit() | |
|
51 | } | |
|
41 | 52 | } |
|
42 | 53 | } |
General Comments 0
You need to be logged in to leave comments.
Login now