Show More
@@ -1,10 +1,11 b'' | |||||
1 | pub mod files; |
|
1 | pub mod files; | |
2 | pub mod root; |
|
2 | pub mod root; | |
3 | use crate::error::CommandError; |
|
3 | use crate::error::CommandError; | |
|
4 | use crate::ui::Ui; | |||
4 |
|
5 | |||
5 | /// The common trait for rhg commands |
|
6 | /// The common trait for rhg commands | |
6 | /// |
|
7 | /// | |
7 | /// Normalize the interface of the commands provided by rhg |
|
8 | /// Normalize the interface of the commands provided by rhg | |
8 |
pub trait Command |
|
9 | pub trait Command { | |
9 | fn run(&self) -> Result<(), CommandError>; |
|
10 | fn run(&self, ui: &Ui) -> Result<(), CommandError>; | |
10 | } |
|
11 | } |
@@ -1,60 +1,58 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::{ListTrackedFiles, ListTrackedFilesErrorKind}; |
|
4 | use hg::operations::{ListTrackedFiles, ListTrackedFilesErrorKind}; | |
5 | use hg::utils::files::{get_bytes_from_path, relativize_path}; |
|
5 | use hg::utils::files::{get_bytes_from_path, relativize_path}; | |
6 | use hg::utils::hg_path::HgPathBuf; |
|
6 | use hg::utils::hg_path::HgPathBuf; | |
7 |
|
7 | |||
8 | pub const HELP_TEXT: &str = " |
|
8 | pub const HELP_TEXT: &str = " | |
9 | List tracked files. |
|
9 | List tracked files. | |
10 |
|
10 | |||
11 | Returns 0 on success. |
|
11 | Returns 0 on success. | |
12 | "; |
|
12 | "; | |
13 |
|
13 | |||
14 |
pub struct FilesCommand |
|
14 | pub struct FilesCommand {} | |
15 | ui: &'a Ui, |
|
|||
16 | } |
|
|||
17 |
|
15 | |||
18 |
impl |
|
16 | impl FilesCommand { | |
19 |
pub fn new( |
|
17 | pub fn new() -> Self { | |
20 |
FilesCommand { |
|
18 | FilesCommand {} | |
21 | } |
|
19 | } | |
22 | } |
|
20 | } | |
23 |
|
21 | |||
24 |
impl |
|
22 | impl Command for FilesCommand { | |
25 | fn run(&self) -> Result<(), CommandError> { |
|
23 | fn run(&self, ui: &Ui) -> Result<(), CommandError> { | |
26 | let operation_builder = ListTrackedFiles::new()?; |
|
24 | let operation_builder = ListTrackedFiles::new()?; | |
27 | let operation = operation_builder.load().map_err(|err| { |
|
25 | let operation = operation_builder.load().map_err(|err| { | |
28 | CommandErrorKind::Abort(Some( |
|
26 | CommandErrorKind::Abort(Some( | |
29 | [b"abort: ", err.to_string().as_bytes(), b"\n"] |
|
27 | [b"abort: ", err.to_string().as_bytes(), b"\n"] | |
30 | .concat() |
|
28 | .concat() | |
31 | .to_vec(), |
|
29 | .to_vec(), | |
32 | )) |
|
30 | )) | |
33 | })?; |
|
31 | })?; | |
34 | let files = operation.run().map_err(|err| match err.kind { |
|
32 | let files = operation.run().map_err(|err| match err.kind { | |
35 | ListTrackedFilesErrorKind::ParseError(_) => { |
|
33 | ListTrackedFilesErrorKind::ParseError(_) => { | |
36 | CommandErrorKind::Abort(Some( |
|
34 | CommandErrorKind::Abort(Some( | |
37 | // TODO find a better error message |
|
35 | // TODO find a better error message | |
38 | b"abort: parse error\n".to_vec(), |
|
36 | b"abort: parse error\n".to_vec(), | |
39 | )) |
|
37 | )) | |
40 | } |
|
38 | } | |
41 | })?; |
|
39 | })?; | |
42 |
|
40 | |||
43 | let cwd = std::env::current_dir() |
|
41 | let cwd = std::env::current_dir() | |
44 | .or_else(|e| Err(CommandErrorKind::CurrentDirNotFound(e)))?; |
|
42 | .or_else(|e| Err(CommandErrorKind::CurrentDirNotFound(e)))?; | |
45 | let rooted_cwd = cwd |
|
43 | let rooted_cwd = cwd | |
46 | .strip_prefix(operation_builder.get_root()) |
|
44 | .strip_prefix(operation_builder.get_root()) | |
47 | .expect("cwd was already checked within the repository"); |
|
45 | .expect("cwd was already checked within the repository"); | |
48 | let rooted_cwd = HgPathBuf::from(get_bytes_from_path(rooted_cwd)); |
|
46 | let rooted_cwd = HgPathBuf::from(get_bytes_from_path(rooted_cwd)); | |
49 |
|
47 | |||
50 |
let mut stdout = |
|
48 | let mut stdout = ui.stdout_buffer(); | |
51 |
|
49 | |||
52 | for file in files { |
|
50 | for file in files { | |
53 | stdout.write_all(relativize_path(file, &rooted_cwd).as_ref())?; |
|
51 | stdout.write_all(relativize_path(file, &rooted_cwd).as_ref())?; | |
54 | stdout.write_all(b"\n")?; |
|
52 | stdout.write_all(b"\n")?; | |
55 | } |
|
53 | } | |
56 | stdout.flush()?; |
|
54 | stdout.flush()?; | |
57 |
|
55 | |||
58 | Ok(()) |
|
56 | Ok(()) | |
59 | } |
|
57 | } | |
60 | } |
|
58 | } |
@@ -1,34 +1,32 b'' | |||||
1 | use crate::commands::Command; |
|
1 | use crate::commands::Command; | |
2 | use crate::error::CommandError; |
|
2 | use crate::error::CommandError; | |
3 | use crate::ui::Ui; |
|
3 | use crate::ui::Ui; | |
4 | use hg::operations::FindRoot; |
|
4 | use hg::operations::FindRoot; | |
5 | use hg::utils::files::get_bytes_from_path; |
|
5 | use hg::utils::files::get_bytes_from_path; | |
6 |
|
6 | |||
7 | pub const HELP_TEXT: &str = " |
|
7 | pub const HELP_TEXT: &str = " | |
8 | Print the root directory of the current repository. |
|
8 | Print the root directory of the current repository. | |
9 |
|
9 | |||
10 | Returns 0 on success. |
|
10 | Returns 0 on success. | |
11 | "; |
|
11 | "; | |
12 |
|
12 | |||
13 |
pub struct RootCommand |
|
13 | pub struct RootCommand {} | |
14 | ui: &'a Ui, |
|
|||
15 | } |
|
|||
16 |
|
14 | |||
17 |
impl |
|
15 | impl RootCommand { | |
18 |
pub fn new( |
|
16 | pub fn new() -> Self { | |
19 |
RootCommand { |
|
17 | RootCommand {} | |
20 | } |
|
18 | } | |
21 | } |
|
19 | } | |
22 |
|
20 | |||
23 |
impl |
|
21 | impl Command for RootCommand { | |
24 | fn run(&self) -> Result<(), CommandError> { |
|
22 | fn run(&self, ui: &Ui) -> Result<(), CommandError> { | |
25 | let path_buf = FindRoot::new().run()?; |
|
23 | let path_buf = FindRoot::new().run()?; | |
26 |
|
24 | |||
27 | let bytes = get_bytes_from_path(path_buf); |
|
25 | let bytes = get_bytes_from_path(path_buf); | |
28 |
|
26 | |||
29 | // TODO use formating macro |
|
27 | // TODO use formating macro | |
30 |
|
|
28 | ui.write_stdout(&[bytes.as_slice(), b"\n"].concat())?; | |
31 |
|
29 | |||
32 | Ok(()) |
|
30 | Ok(()) | |
33 | } |
|
31 | } | |
34 | } |
|
32 | } |
@@ -1,57 +1,57 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 | .subcommand( |
|
20 | .subcommand( | |
21 | SubCommand::with_name("files").about(commands::files::HELP_TEXT), |
|
21 | SubCommand::with_name("files").about(commands::files::HELP_TEXT), | |
22 | ); |
|
22 | ); | |
23 |
|
23 | |||
24 | let matches = app.clone().get_matches_safe().unwrap_or_else(|_| { |
|
24 | let matches = app.clone().get_matches_safe().unwrap_or_else(|_| { | |
25 | std::process::exit(exitcode::UNIMPLEMENTED_COMMAND) |
|
25 | std::process::exit(exitcode::UNIMPLEMENTED_COMMAND) | |
26 | }); |
|
26 | }); | |
27 |
|
27 | |||
28 | let ui = ui::Ui::new(); |
|
28 | let ui = ui::Ui::new(); | |
29 |
|
29 | |||
30 | let command_result = match matches.subcommand_name() { |
|
30 | let command_result = match matches.subcommand_name() { | |
31 | Some(name) => match name { |
|
31 | Some(name) => match name { | |
32 |
"root" => commands::root::RootCommand::new( |
|
32 | "root" => commands::root::RootCommand::new().run(&ui), | |
33 |
"files" => commands::files::FilesCommand::new( |
|
33 | "files" => commands::files::FilesCommand::new().run(&ui), | |
34 | _ => std::process::exit(exitcode::UNIMPLEMENTED_COMMAND), |
|
34 | _ => std::process::exit(exitcode::UNIMPLEMENTED_COMMAND), | |
35 | }, |
|
35 | }, | |
36 | _ => { |
|
36 | _ => { | |
37 | match app.print_help() { |
|
37 | match app.print_help() { | |
38 | Ok(_) => std::process::exit(exitcode::OK), |
|
38 | Ok(_) => std::process::exit(exitcode::OK), | |
39 | Err(_) => std::process::exit(exitcode::ABORT), |
|
39 | Err(_) => std::process::exit(exitcode::ABORT), | |
40 | }; |
|
40 | }; | |
41 | } |
|
41 | } | |
42 | }; |
|
42 | }; | |
43 |
|
43 | |||
44 | match command_result { |
|
44 | match command_result { | |
45 | Ok(_) => std::process::exit(exitcode::OK), |
|
45 | Ok(_) => std::process::exit(exitcode::OK), | |
46 | Err(e) => { |
|
46 | Err(e) => { | |
47 | let message = e.get_error_message_bytes(); |
|
47 | let message = e.get_error_message_bytes(); | |
48 | if let Some(msg) = message { |
|
48 | if let Some(msg) = message { | |
49 | match ui.write_stderr(&msg) { |
|
49 | match ui.write_stderr(&msg) { | |
50 | Ok(_) => (), |
|
50 | Ok(_) => (), | |
51 | Err(_) => std::process::exit(exitcode::ABORT), |
|
51 | Err(_) => std::process::exit(exitcode::ABORT), | |
52 | }; |
|
52 | }; | |
53 | }; |
|
53 | }; | |
54 | e.exit() |
|
54 | e.exit() | |
55 | } |
|
55 | } | |
56 | } |
|
56 | } | |
57 | } |
|
57 | } |
General Comments 0
You need to be logged in to leave comments.
Login now