Show More
@@ -0,0 +1,99 b'' | |||||
|
1 | use crate::commands::Command; | |||
|
2 | use crate::error::{CommandError, CommandErrorKind}; | |||
|
3 | use crate::ui::utf8_to_local; | |||
|
4 | use crate::ui::Ui; | |||
|
5 | use hg::operations::FindRoot; | |||
|
6 | use hg::operations::{CatRev, CatRevError, CatRevErrorKind}; | |||
|
7 | use hg::utils::hg_path::HgPathBuf; | |||
|
8 | use micro_timer::timed; | |||
|
9 | use std::convert::TryFrom; | |||
|
10 | ||||
|
11 | pub const HELP_TEXT: &str = " | |||
|
12 | Output the current or given revision of files | |||
|
13 | "; | |||
|
14 | ||||
|
15 | pub struct CatCommand<'a> { | |||
|
16 | rev: Option<&'a str>, | |||
|
17 | files: Vec<&'a str>, | |||
|
18 | } | |||
|
19 | ||||
|
20 | impl<'a> CatCommand<'a> { | |||
|
21 | pub fn new(rev: Option<&'a str>, files: Vec<&'a str>) -> Self { | |||
|
22 | Self { rev, files } | |||
|
23 | } | |||
|
24 | ||||
|
25 | fn display(&self, ui: &Ui, data: &[u8]) -> Result<(), CommandError> { | |||
|
26 | ui.write_stdout(data)?; | |||
|
27 | Ok(()) | |||
|
28 | } | |||
|
29 | } | |||
|
30 | ||||
|
31 | impl<'a> Command for CatCommand<'a> { | |||
|
32 | #[timed] | |||
|
33 | fn run(&self, ui: &Ui) -> Result<(), CommandError> { | |||
|
34 | let root = FindRoot::new().run()?; | |||
|
35 | let cwd = std::env::current_dir() | |||
|
36 | .or_else(|e| Err(CommandErrorKind::CurrentDirNotFound(e)))?; | |||
|
37 | ||||
|
38 | let mut files = vec![]; | |||
|
39 | for file in self.files.iter() { | |||
|
40 | let normalized = cwd.join(&file); | |||
|
41 | let stripped = normalized | |||
|
42 | .strip_prefix(&root) | |||
|
43 | .map_err(|_| CommandErrorKind::Abort(None))?; | |||
|
44 | let hg_file = HgPathBuf::try_from(stripped.to_path_buf()) | |||
|
45 | .map_err(|_| CommandErrorKind::Abort(None))?; | |||
|
46 | files.push(hg_file); | |||
|
47 | } | |||
|
48 | ||||
|
49 | match self.rev { | |||
|
50 | Some(rev) => { | |||
|
51 | let mut operation = CatRev::new(&root, rev, &files) | |||
|
52 | .map_err(|e| map_rev_error(rev, e))?; | |||
|
53 | let data = | |||
|
54 | operation.run().map_err(|e| map_rev_error(rev, e))?; | |||
|
55 | self.display(ui, &data) | |||
|
56 | } | |||
|
57 | None => Err(CommandErrorKind::Unimplemented.into()), | |||
|
58 | } | |||
|
59 | } | |||
|
60 | } | |||
|
61 | ||||
|
62 | /// Convert `CatRevErrorKind` to `CommandError` | |||
|
63 | fn map_rev_error(rev: &str, err: CatRevError) -> CommandError { | |||
|
64 | CommandError { | |||
|
65 | kind: match err.kind { | |||
|
66 | CatRevErrorKind::IoError(err) => CommandErrorKind::Abort(Some( | |||
|
67 | utf8_to_local(&format!("abort: {}\n", err)).into(), | |||
|
68 | )), | |||
|
69 | CatRevErrorKind::InvalidRevision => CommandErrorKind::Abort(Some( | |||
|
70 | utf8_to_local(&format!( | |||
|
71 | "abort: invalid revision identifier{}\n", | |||
|
72 | rev | |||
|
73 | )) | |||
|
74 | .into(), | |||
|
75 | )), | |||
|
76 | CatRevErrorKind::UnsuportedRevlogVersion(version) => { | |||
|
77 | CommandErrorKind::Abort(Some( | |||
|
78 | utf8_to_local(&format!( | |||
|
79 | "abort: unsupported revlog version {}\n", | |||
|
80 | version | |||
|
81 | )) | |||
|
82 | .into(), | |||
|
83 | )) | |||
|
84 | } | |||
|
85 | CatRevErrorKind::CorruptedRevlog => CommandErrorKind::Abort(Some( | |||
|
86 | "abort: corrupted revlog\n".into(), | |||
|
87 | )), | |||
|
88 | CatRevErrorKind::UnknowRevlogDataFormat(format) => { | |||
|
89 | CommandErrorKind::Abort(Some( | |||
|
90 | utf8_to_local(&format!( | |||
|
91 | "abort: unknow revlog dataformat {:?}\n", | |||
|
92 | format | |||
|
93 | )) | |||
|
94 | .into(), | |||
|
95 | )) | |||
|
96 | } | |||
|
97 | }, | |||
|
98 | } | |||
|
99 | } |
@@ -1,12 +1,13 b'' | |||||
|
1 | pub mod cat; | |||
1 | pub mod debugdata; |
|
2 | pub mod debugdata; | |
2 | pub mod files; |
|
3 | pub mod files; | |
3 | pub mod root; |
|
4 | pub mod root; | |
4 | use crate::error::CommandError; |
|
5 | use crate::error::CommandError; | |
5 | use crate::ui::Ui; |
|
6 | use crate::ui::Ui; | |
6 |
|
7 | |||
7 | /// The common trait for rhg commands |
|
8 | /// The common trait for rhg commands | |
8 | /// |
|
9 | /// | |
9 | /// Normalize the interface of the commands provided by rhg |
|
10 | /// Normalize the interface of the commands provided by rhg | |
10 | pub trait Command { |
|
11 | pub trait Command { | |
11 | fn run(&self, ui: &Ui) -> Result<(), CommandError>; |
|
12 | fn run(&self, ui: &Ui) -> Result<(), CommandError>; | |
12 | } |
|
13 | } |
@@ -1,110 +1,113 b'' | |||||
1 | use crate::exitcode; |
|
1 | use crate::exitcode; | |
2 | use crate::ui::UiError; |
|
2 | use crate::ui::UiError; | |
3 | use hg::operations::{FindRootError, FindRootErrorKind}; |
|
3 | use hg::operations::{FindRootError, FindRootErrorKind}; | |
4 | use hg::utils::files::get_bytes_from_path; |
|
4 | use hg::utils::files::get_bytes_from_path; | |
5 | use std::convert::From; |
|
5 | use std::convert::From; | |
6 | use std::path::PathBuf; |
|
6 | use std::path::PathBuf; | |
7 |
|
7 | |||
8 | /// The kind of command error |
|
8 | /// The kind of command error | |
9 | #[derive(Debug)] |
|
9 | #[derive(Debug)] | |
10 | pub enum CommandErrorKind { |
|
10 | pub enum CommandErrorKind { | |
11 | /// The root of the repository cannot be found |
|
11 | /// The root of the repository cannot be found | |
12 | RootNotFound(PathBuf), |
|
12 | RootNotFound(PathBuf), | |
13 | /// The current directory cannot be found |
|
13 | /// The current directory cannot be found | |
14 | CurrentDirNotFound(std::io::Error), |
|
14 | CurrentDirNotFound(std::io::Error), | |
15 | /// The standard output stream cannot be written to |
|
15 | /// The standard output stream cannot be written to | |
16 | StdoutError, |
|
16 | StdoutError, | |
17 | /// The standard error stream cannot be written to |
|
17 | /// The standard error stream cannot be written to | |
18 | StderrError, |
|
18 | StderrError, | |
19 | /// The command aborted |
|
19 | /// The command aborted | |
20 | Abort(Option<Vec<u8>>), |
|
20 | Abort(Option<Vec<u8>>), | |
|
21 | /// A mercurial capability as not been implemented. | |||
|
22 | Unimplemented, | |||
21 | } |
|
23 | } | |
22 |
|
24 | |||
23 | impl CommandErrorKind { |
|
25 | impl CommandErrorKind { | |
24 | pub fn get_exit_code(&self) -> exitcode::ExitCode { |
|
26 | pub fn get_exit_code(&self) -> exitcode::ExitCode { | |
25 | match self { |
|
27 | match self { | |
26 | CommandErrorKind::RootNotFound(_) => exitcode::ABORT, |
|
28 | CommandErrorKind::RootNotFound(_) => exitcode::ABORT, | |
27 | CommandErrorKind::CurrentDirNotFound(_) => exitcode::ABORT, |
|
29 | CommandErrorKind::CurrentDirNotFound(_) => exitcode::ABORT, | |
28 | CommandErrorKind::StdoutError => exitcode::ABORT, |
|
30 | CommandErrorKind::StdoutError => exitcode::ABORT, | |
29 | CommandErrorKind::StderrError => exitcode::ABORT, |
|
31 | CommandErrorKind::StderrError => exitcode::ABORT, | |
30 | CommandErrorKind::Abort(_) => exitcode::ABORT, |
|
32 | CommandErrorKind::Abort(_) => exitcode::ABORT, | |
|
33 | CommandErrorKind::Unimplemented => exitcode::UNIMPLEMENTED_COMMAND, | |||
31 | } |
|
34 | } | |
32 | } |
|
35 | } | |
33 |
|
36 | |||
34 | /// Return the message corresponding to the error kind if any |
|
37 | /// Return the message corresponding to the error kind if any | |
35 | pub fn get_error_message_bytes(&self) -> Option<Vec<u8>> { |
|
38 | pub fn get_error_message_bytes(&self) -> Option<Vec<u8>> { | |
36 | match self { |
|
39 | match self { | |
37 | // TODO use formating macro |
|
40 | // TODO use formating macro | |
38 | CommandErrorKind::RootNotFound(path) => { |
|
41 | CommandErrorKind::RootNotFound(path) => { | |
39 | let bytes = get_bytes_from_path(path); |
|
42 | let bytes = get_bytes_from_path(path); | |
40 | Some( |
|
43 | Some( | |
41 | [ |
|
44 | [ | |
42 | b"abort: no repository found in '", |
|
45 | b"abort: no repository found in '", | |
43 | bytes.as_slice(), |
|
46 | bytes.as_slice(), | |
44 | b"' (.hg not found)!\n", |
|
47 | b"' (.hg not found)!\n", | |
45 | ] |
|
48 | ] | |
46 | .concat(), |
|
49 | .concat(), | |
47 | ) |
|
50 | ) | |
48 | } |
|
51 | } | |
49 | // TODO use formating macro |
|
52 | // TODO use formating macro | |
50 | CommandErrorKind::CurrentDirNotFound(e) => Some( |
|
53 | CommandErrorKind::CurrentDirNotFound(e) => Some( | |
51 | [ |
|
54 | [ | |
52 | b"abort: error getting current working directory: ", |
|
55 | b"abort: error getting current working directory: ", | |
53 | e.to_string().as_bytes(), |
|
56 | e.to_string().as_bytes(), | |
54 | b"\n", |
|
57 | b"\n", | |
55 | ] |
|
58 | ] | |
56 | .concat(), |
|
59 | .concat(), | |
57 | ), |
|
60 | ), | |
58 | CommandErrorKind::Abort(message) => message.to_owned(), |
|
61 | CommandErrorKind::Abort(message) => message.to_owned(), | |
59 | _ => None, |
|
62 | _ => None, | |
60 | } |
|
63 | } | |
61 | } |
|
64 | } | |
62 | } |
|
65 | } | |
63 |
|
66 | |||
64 | /// The error type for the Command trait |
|
67 | /// The error type for the Command trait | |
65 | #[derive(Debug)] |
|
68 | #[derive(Debug)] | |
66 | pub struct CommandError { |
|
69 | pub struct CommandError { | |
67 | pub kind: CommandErrorKind, |
|
70 | pub kind: CommandErrorKind, | |
68 | } |
|
71 | } | |
69 |
|
72 | |||
70 | impl CommandError { |
|
73 | impl CommandError { | |
71 | /// Exist the process with the corresponding exit code. |
|
74 | /// Exist the process with the corresponding exit code. | |
72 | pub fn exit(&self) { |
|
75 | pub fn exit(&self) { | |
73 | std::process::exit(self.kind.get_exit_code()) |
|
76 | std::process::exit(self.kind.get_exit_code()) | |
74 | } |
|
77 | } | |
75 |
|
78 | |||
76 | /// Return the message corresponding to the command error if any |
|
79 | /// Return the message corresponding to the command error if any | |
77 | pub fn get_error_message_bytes(&self) -> Option<Vec<u8>> { |
|
80 | pub fn get_error_message_bytes(&self) -> Option<Vec<u8>> { | |
78 | self.kind.get_error_message_bytes() |
|
81 | self.kind.get_error_message_bytes() | |
79 | } |
|
82 | } | |
80 | } |
|
83 | } | |
81 |
|
84 | |||
82 | impl From<CommandErrorKind> for CommandError { |
|
85 | impl From<CommandErrorKind> for CommandError { | |
83 | fn from(kind: CommandErrorKind) -> Self { |
|
86 | fn from(kind: CommandErrorKind) -> Self { | |
84 | CommandError { kind } |
|
87 | CommandError { kind } | |
85 | } |
|
88 | } | |
86 | } |
|
89 | } | |
87 |
|
90 | |||
88 | impl From<UiError> for CommandError { |
|
91 | impl From<UiError> for CommandError { | |
89 | fn from(error: UiError) -> Self { |
|
92 | fn from(error: UiError) -> Self { | |
90 | CommandError { |
|
93 | CommandError { | |
91 | kind: match error { |
|
94 | kind: match error { | |
92 | UiError::StdoutError(_) => CommandErrorKind::StdoutError, |
|
95 | UiError::StdoutError(_) => CommandErrorKind::StdoutError, | |
93 | UiError::StderrError(_) => CommandErrorKind::StderrError, |
|
96 | UiError::StderrError(_) => CommandErrorKind::StderrError, | |
94 | }, |
|
97 | }, | |
95 | } |
|
98 | } | |
96 | } |
|
99 | } | |
97 | } |
|
100 | } | |
98 |
|
101 | |||
99 | impl From<FindRootError> for CommandError { |
|
102 | impl From<FindRootError> for CommandError { | |
100 | fn from(err: FindRootError) -> Self { |
|
103 | fn from(err: FindRootError) -> Self { | |
101 | match err.kind { |
|
104 | match err.kind { | |
102 | FindRootErrorKind::RootNotFound(path) => CommandError { |
|
105 | FindRootErrorKind::RootNotFound(path) => CommandError { | |
103 | kind: CommandErrorKind::RootNotFound(path), |
|
106 | kind: CommandErrorKind::RootNotFound(path), | |
104 | }, |
|
107 | }, | |
105 | FindRootErrorKind::GetCurrentDirError(e) => CommandError { |
|
108 | FindRootErrorKind::GetCurrentDirError(e) => CommandError { | |
106 | kind: CommandErrorKind::CurrentDirNotFound(e), |
|
109 | kind: CommandErrorKind::CurrentDirNotFound(e), | |
107 | }, |
|
110 | }, | |
108 | } |
|
111 | } | |
109 | } |
|
112 | } | |
110 | } |
|
113 | } |
@@ -1,141 +1,177 b'' | |||||
1 | extern crate log; |
|
1 | extern crate log; | |
2 | use clap::App; |
|
2 | use clap::App; | |
3 | use clap::AppSettings; |
|
3 | use clap::AppSettings; | |
4 | use clap::Arg; |
|
4 | use clap::Arg; | |
5 | use clap::ArgGroup; |
|
5 | use clap::ArgGroup; | |
6 | use clap::ArgMatches; |
|
6 | use clap::ArgMatches; | |
7 | use clap::SubCommand; |
|
7 | use clap::SubCommand; | |
8 | use hg::operations::DebugDataKind; |
|
8 | use hg::operations::DebugDataKind; | |
9 | use std::convert::TryFrom; |
|
9 | use std::convert::TryFrom; | |
10 |
|
10 | |||
11 | mod commands; |
|
11 | mod commands; | |
12 | mod error; |
|
12 | mod error; | |
13 | mod exitcode; |
|
13 | mod exitcode; | |
14 | mod ui; |
|
14 | mod ui; | |
15 | use commands::Command; |
|
15 | use commands::Command; | |
16 | use error::CommandError; |
|
16 | use error::CommandError; | |
17 |
|
17 | |||
18 | fn main() { |
|
18 | fn main() { | |
19 | env_logger::init(); |
|
19 | env_logger::init(); | |
20 | let app = App::new("rhg") |
|
20 | let app = App::new("rhg") | |
21 | .setting(AppSettings::AllowInvalidUtf8) |
|
21 | .setting(AppSettings::AllowInvalidUtf8) | |
22 | .setting(AppSettings::SubcommandRequired) |
|
22 | .setting(AppSettings::SubcommandRequired) | |
23 | .setting(AppSettings::VersionlessSubcommands) |
|
23 | .setting(AppSettings::VersionlessSubcommands) | |
24 | .version("0.0.1") |
|
24 | .version("0.0.1") | |
25 | .subcommand( |
|
25 | .subcommand( | |
26 | SubCommand::with_name("root").about(commands::root::HELP_TEXT), |
|
26 | SubCommand::with_name("root").about(commands::root::HELP_TEXT), | |
27 | ) |
|
27 | ) | |
28 | .subcommand( |
|
28 | .subcommand( | |
29 | SubCommand::with_name("files") |
|
29 | SubCommand::with_name("files") | |
30 | .arg( |
|
30 | .arg( | |
31 | Arg::with_name("rev") |
|
31 | Arg::with_name("rev") | |
32 | .help("search the repository as it is in REV") |
|
32 | .help("search the repository as it is in REV") | |
33 | .short("-r") |
|
33 | .short("-r") | |
34 | .long("--revision") |
|
34 | .long("--revision") | |
35 | .value_name("REV") |
|
35 | .value_name("REV") | |
36 | .takes_value(true), |
|
36 | .takes_value(true), | |
37 | ) |
|
37 | ) | |
38 | .about(commands::files::HELP_TEXT), |
|
38 | .about(commands::files::HELP_TEXT), | |
39 | ) |
|
39 | ) | |
40 | .subcommand( |
|
40 | .subcommand( | |
|
41 | SubCommand::with_name("cat") | |||
|
42 | .arg( | |||
|
43 | Arg::with_name("rev") | |||
|
44 | .help("search the repository as it is in REV") | |||
|
45 | .short("-r") | |||
|
46 | .long("--revision") | |||
|
47 | .value_name("REV") | |||
|
48 | .takes_value(true), | |||
|
49 | ) | |||
|
50 | .arg( | |||
|
51 | clap::Arg::with_name("files") | |||
|
52 | .required(true) | |||
|
53 | .multiple(true) | |||
|
54 | .empty_values(false) | |||
|
55 | .value_name("FILE") | |||
|
56 | .help("Activity to start: activity@category"), | |||
|
57 | ) | |||
|
58 | .about(commands::cat::HELP_TEXT), | |||
|
59 | ) | |||
|
60 | .subcommand( | |||
41 | SubCommand::with_name("debugdata") |
|
61 | SubCommand::with_name("debugdata") | |
42 | .about(commands::debugdata::HELP_TEXT) |
|
62 | .about(commands::debugdata::HELP_TEXT) | |
43 | .arg( |
|
63 | .arg( | |
44 | Arg::with_name("changelog") |
|
64 | Arg::with_name("changelog") | |
45 | .help("open changelog") |
|
65 | .help("open changelog") | |
46 | .short("-c") |
|
66 | .short("-c") | |
47 | .long("--changelog"), |
|
67 | .long("--changelog"), | |
48 | ) |
|
68 | ) | |
49 | .arg( |
|
69 | .arg( | |
50 | Arg::with_name("manifest") |
|
70 | Arg::with_name("manifest") | |
51 | .help("open manifest") |
|
71 | .help("open manifest") | |
52 | .short("-m") |
|
72 | .short("-m") | |
53 | .long("--manifest"), |
|
73 | .long("--manifest"), | |
54 | ) |
|
74 | ) | |
55 | .group( |
|
75 | .group( | |
56 | ArgGroup::with_name("") |
|
76 | ArgGroup::with_name("") | |
57 | .args(&["changelog", "manifest"]) |
|
77 | .args(&["changelog", "manifest"]) | |
58 | .required(true), |
|
78 | .required(true), | |
59 | ) |
|
79 | ) | |
60 | .arg( |
|
80 | .arg( | |
61 | Arg::with_name("rev") |
|
81 | Arg::with_name("rev") | |
62 | .help("revision") |
|
82 | .help("revision") | |
63 | .required(true) |
|
83 | .required(true) | |
64 | .value_name("REV"), |
|
84 | .value_name("REV"), | |
65 | ), |
|
85 | ), | |
66 | ); |
|
86 | ); | |
67 |
|
87 | |||
68 | let matches = app.clone().get_matches_safe().unwrap_or_else(|err| { |
|
88 | let matches = app.clone().get_matches_safe().unwrap_or_else(|err| { | |
69 | let _ = ui::Ui::new().writeln_stderr_str(&err.message); |
|
89 | let _ = ui::Ui::new().writeln_stderr_str(&err.message); | |
70 | std::process::exit(exitcode::UNIMPLEMENTED_COMMAND) |
|
90 | std::process::exit(exitcode::UNIMPLEMENTED_COMMAND) | |
71 | }); |
|
91 | }); | |
72 |
|
92 | |||
73 | let ui = ui::Ui::new(); |
|
93 | let ui = ui::Ui::new(); | |
74 |
|
94 | |||
75 | let command_result = match_subcommand(matches, &ui); |
|
95 | let command_result = match_subcommand(matches, &ui); | |
76 |
|
96 | |||
77 | match command_result { |
|
97 | match command_result { | |
78 | Ok(_) => std::process::exit(exitcode::OK), |
|
98 | Ok(_) => std::process::exit(exitcode::OK), | |
79 | Err(e) => { |
|
99 | Err(e) => { | |
80 | let message = e.get_error_message_bytes(); |
|
100 | let message = e.get_error_message_bytes(); | |
81 | if let Some(msg) = message { |
|
101 | if let Some(msg) = message { | |
82 | match ui.write_stderr(&msg) { |
|
102 | match ui.write_stderr(&msg) { | |
83 | Ok(_) => (), |
|
103 | Ok(_) => (), | |
84 | Err(_) => std::process::exit(exitcode::ABORT), |
|
104 | Err(_) => std::process::exit(exitcode::ABORT), | |
85 | }; |
|
105 | }; | |
86 | }; |
|
106 | }; | |
87 | e.exit() |
|
107 | e.exit() | |
88 | } |
|
108 | } | |
89 | } |
|
109 | } | |
90 | } |
|
110 | } | |
91 |
|
111 | |||
92 | fn match_subcommand( |
|
112 | fn match_subcommand( | |
93 | matches: ArgMatches, |
|
113 | matches: ArgMatches, | |
94 | ui: &ui::Ui, |
|
114 | ui: &ui::Ui, | |
95 | ) -> Result<(), CommandError> { |
|
115 | ) -> Result<(), CommandError> { | |
96 | match matches.subcommand() { |
|
116 | match matches.subcommand() { | |
97 | ("root", _) => commands::root::RootCommand::new().run(&ui), |
|
117 | ("root", _) => commands::root::RootCommand::new().run(&ui), | |
98 | ("files", Some(matches)) => { |
|
118 | ("files", Some(matches)) => { | |
99 | commands::files::FilesCommand::try_from(matches)?.run(&ui) |
|
119 | commands::files::FilesCommand::try_from(matches)?.run(&ui) | |
100 | } |
|
120 | } | |
|
121 | ("cat", Some(matches)) => { | |||
|
122 | commands::cat::CatCommand::try_from(matches)?.run(&ui) | |||
|
123 | } | |||
101 | ("debugdata", Some(matches)) => { |
|
124 | ("debugdata", Some(matches)) => { | |
102 | commands::debugdata::DebugDataCommand::try_from(matches)?.run(&ui) |
|
125 | commands::debugdata::DebugDataCommand::try_from(matches)?.run(&ui) | |
103 | } |
|
126 | } | |
104 | _ => unreachable!(), // Because of AppSettings::SubcommandRequired, |
|
127 | _ => unreachable!(), // Because of AppSettings::SubcommandRequired, | |
105 | } |
|
128 | } | |
106 | } |
|
129 | } | |
107 |
|
130 | |||
108 | impl<'a> TryFrom<&'a ArgMatches<'_>> for commands::files::FilesCommand<'a> { |
|
131 | impl<'a> TryFrom<&'a ArgMatches<'_>> for commands::files::FilesCommand<'a> { | |
109 | type Error = CommandError; |
|
132 | type Error = CommandError; | |
110 |
|
133 | |||
111 | fn try_from(args: &'a ArgMatches) -> Result<Self, Self::Error> { |
|
134 | fn try_from(args: &'a ArgMatches) -> Result<Self, Self::Error> { | |
112 | let rev = args.value_of("rev"); |
|
135 | let rev = args.value_of("rev"); | |
113 | Ok(commands::files::FilesCommand::new(rev)) |
|
136 | Ok(commands::files::FilesCommand::new(rev)) | |
114 | } |
|
137 | } | |
115 | } |
|
138 | } | |
116 |
|
139 | |||
|
140 | impl<'a> TryFrom<&'a ArgMatches<'_>> for commands::cat::CatCommand<'a> { | |||
|
141 | type Error = CommandError; | |||
|
142 | ||||
|
143 | fn try_from(args: &'a ArgMatches) -> Result<Self, Self::Error> { | |||
|
144 | let rev = args.value_of("rev"); | |||
|
145 | let files = match args.values_of("files") { | |||
|
146 | Some(files) => files.collect(), | |||
|
147 | None => vec![], | |||
|
148 | }; | |||
|
149 | Ok(commands::cat::CatCommand::new(rev, files)) | |||
|
150 | } | |||
|
151 | } | |||
|
152 | ||||
117 | impl<'a> TryFrom<&'a ArgMatches<'_>> |
|
153 | impl<'a> TryFrom<&'a ArgMatches<'_>> | |
118 | for commands::debugdata::DebugDataCommand<'a> |
|
154 | for commands::debugdata::DebugDataCommand<'a> | |
119 | { |
|
155 | { | |
120 | type Error = CommandError; |
|
156 | type Error = CommandError; | |
121 |
|
157 | |||
122 | fn try_from(args: &'a ArgMatches) -> Result<Self, Self::Error> { |
|
158 | fn try_from(args: &'a ArgMatches) -> Result<Self, Self::Error> { | |
123 | let rev = args |
|
159 | let rev = args | |
124 | .value_of("rev") |
|
160 | .value_of("rev") | |
125 | .expect("rev should be a required argument"); |
|
161 | .expect("rev should be a required argument"); | |
126 | let kind = match ( |
|
162 | let kind = match ( | |
127 | args.is_present("changelog"), |
|
163 | args.is_present("changelog"), | |
128 | args.is_present("manifest"), |
|
164 | args.is_present("manifest"), | |
129 | ) { |
|
165 | ) { | |
130 | (true, false) => DebugDataKind::Changelog, |
|
166 | (true, false) => DebugDataKind::Changelog, | |
131 | (false, true) => DebugDataKind::Manifest, |
|
167 | (false, true) => DebugDataKind::Manifest, | |
132 | (true, true) => { |
|
168 | (true, true) => { | |
133 | unreachable!("Should not happen since options are exclusive") |
|
169 | unreachable!("Should not happen since options are exclusive") | |
134 | } |
|
170 | } | |
135 | (false, false) => { |
|
171 | (false, false) => { | |
136 | unreachable!("Should not happen since options are required") |
|
172 | unreachable!("Should not happen since options are required") | |
137 | } |
|
173 | } | |
138 | }; |
|
174 | }; | |
139 | Ok(commands::debugdata::DebugDataCommand::new(rev, kind)) |
|
175 | Ok(commands::debugdata::DebugDataCommand::new(rev, kind)) | |
140 | } |
|
176 | } | |
141 | } |
|
177 | } |
General Comments 0
You need to be logged in to leave comments.
Login now