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