Show More
@@ -1,69 +1,136 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::utf8_to_local; |
|
3 | use crate::ui::utf8_to_local; | |
4 | use crate::ui::Ui; |
|
4 | use crate::ui::Ui; | |
5 | use hg::operations::FindRoot; |
|
5 | use hg::operations::FindRoot; | |
6 | use hg::operations::{ |
|
6 | use hg::operations::{ | |
7 | ListDirstateTrackedFiles, ListDirstateTrackedFilesError, |
|
7 | ListDirstateTrackedFiles, ListDirstateTrackedFilesError, | |
8 | ListDirstateTrackedFilesErrorKind, |
|
8 | ListDirstateTrackedFilesErrorKind, | |
9 | }; |
|
9 | }; | |
|
10 | use hg::operations::{ | |||
|
11 | ListRevTrackedFiles, ListRevTrackedFilesError, | |||
|
12 | ListRevTrackedFilesErrorKind, | |||
|
13 | }; | |||
10 | use hg::utils::files::{get_bytes_from_path, relativize_path}; |
|
14 | use hg::utils::files::{get_bytes_from_path, relativize_path}; | |
11 | use hg::utils::hg_path::HgPathBuf; |
|
15 | use hg::utils::hg_path::{HgPath, HgPathBuf}; | |
|
16 | use std::path::PathBuf; | |||
12 |
|
17 | |||
13 | pub const HELP_TEXT: &str = " |
|
18 | pub const HELP_TEXT: &str = " | |
14 | List tracked files. |
|
19 | List tracked files. | |
15 |
|
20 | |||
16 | Returns 0 on success. |
|
21 | Returns 0 on success. | |
17 | "; |
|
22 | "; | |
18 |
|
23 | |||
19 |
pub struct FilesCommand { |
|
24 | pub struct FilesCommand<'a> { | |
20 |
|
25 | rev: Option<&'a str>, | ||
21 | impl FilesCommand { |
|
|||
22 | pub fn new() -> Self { |
|
|||
23 | FilesCommand {} |
|
|||
24 | } |
|
|||
25 | } |
|
26 | } | |
26 |
|
27 | |||
27 |
impl |
|
28 | impl<'a> FilesCommand<'a> { | |
28 | fn run(&self, ui: &Ui) -> Result<(), CommandError> { |
|
29 | pub fn new(rev: Option<&'a str>) -> Self { | |
29 | let root = FindRoot::new().run()?; |
|
30 | FilesCommand { rev } | |
30 | let mut operation = ListDirstateTrackedFiles::new(&root) |
|
31 | } | |
31 | .map_err(map_dirstate_error)?; |
|
|||
32 | let files = operation.run().map_err(map_dirstate_error)?; |
|
|||
33 |
|
32 | |||
|
33 | fn display_files( | |||
|
34 | &self, | |||
|
35 | ui: &Ui, | |||
|
36 | root: &PathBuf, | |||
|
37 | files: impl IntoIterator<Item = &'a HgPath>, | |||
|
38 | ) -> Result<(), CommandError> { | |||
34 | let cwd = std::env::current_dir() |
|
39 | let cwd = std::env::current_dir() | |
35 | .or_else(|e| Err(CommandErrorKind::CurrentDirNotFound(e)))?; |
|
40 | .or_else(|e| Err(CommandErrorKind::CurrentDirNotFound(e)))?; | |
36 | let rooted_cwd = cwd |
|
41 | let rooted_cwd = cwd | |
37 | .strip_prefix(&root) |
|
42 | .strip_prefix(&root) | |
38 | .expect("cwd was already checked within the repository"); |
|
43 | .expect("cwd was already checked within the repository"); | |
39 | let rooted_cwd = HgPathBuf::from(get_bytes_from_path(rooted_cwd)); |
|
44 | let rooted_cwd = HgPathBuf::from(get_bytes_from_path(rooted_cwd)); | |
40 |
|
45 | |||
41 | let mut stdout = ui.stdout_buffer(); |
|
46 | let mut stdout = ui.stdout_buffer(); | |
42 |
|
47 | |||
43 | for file in files { |
|
48 | for file in files { | |
44 | stdout.write_all(relativize_path(file, &rooted_cwd).as_ref())?; |
|
49 | stdout.write_all(relativize_path(file, &rooted_cwd).as_ref())?; | |
45 | stdout.write_all(b"\n")?; |
|
50 | stdout.write_all(b"\n")?; | |
46 | } |
|
51 | } | |
47 | stdout.flush()?; |
|
52 | stdout.flush()?; | |
48 | Ok(()) |
|
53 | Ok(()) | |
49 | } |
|
54 | } | |
50 | } |
|
55 | } | |
51 |
|
56 | |||
52 | /// Convert operation errors to command errors |
|
57 | impl<'a> Command for FilesCommand<'a> { | |
|
58 | fn run(&self, ui: &Ui) -> Result<(), CommandError> { | |||
|
59 | let root = FindRoot::new().run()?; | |||
|
60 | if let Some(rev) = self.rev { | |||
|
61 | let mut operation = ListRevTrackedFiles::new(&root, rev) | |||
|
62 | .map_err(|e| map_rev_error(rev, e))?; | |||
|
63 | let files = operation.run().map_err(|e| map_rev_error(rev, e))?; | |||
|
64 | self.display_files(ui, &root, files) | |||
|
65 | } else { | |||
|
66 | let mut operation = ListDirstateTrackedFiles::new(&root) | |||
|
67 | .map_err(map_dirstate_error)?; | |||
|
68 | let files = operation.run().map_err(map_dirstate_error)?; | |||
|
69 | self.display_files(ui, &root, files) | |||
|
70 | } | |||
|
71 | } | |||
|
72 | } | |||
|
73 | ||||
|
74 | /// Convert `ListRevTrackedFilesErrorKind` to `CommandError` | |||
|
75 | fn map_rev_error(rev: &str, err: ListRevTrackedFilesError) -> CommandError { | |||
|
76 | CommandError { | |||
|
77 | kind: match err.kind { | |||
|
78 | ListRevTrackedFilesErrorKind::IoError(err) => { | |||
|
79 | CommandErrorKind::Abort(Some( | |||
|
80 | utf8_to_local(&format!("abort: {}\n", err)).into(), | |||
|
81 | )) | |||
|
82 | } | |||
|
83 | ListRevTrackedFilesErrorKind::InvalidRevision => { | |||
|
84 | CommandErrorKind::Abort(Some( | |||
|
85 | utf8_to_local(&format!( | |||
|
86 | "abort: invalid revision identifier{}\n", | |||
|
87 | rev | |||
|
88 | )) | |||
|
89 | .into(), | |||
|
90 | )) | |||
|
91 | } | |||
|
92 | ListRevTrackedFilesErrorKind::UnsuportedRevlogVersion(version) => { | |||
|
93 | CommandErrorKind::Abort(Some( | |||
|
94 | utf8_to_local(&format!( | |||
|
95 | "abort: unsupported revlog version {}\n", | |||
|
96 | version | |||
|
97 | )) | |||
|
98 | .into(), | |||
|
99 | )) | |||
|
100 | } | |||
|
101 | ListRevTrackedFilesErrorKind::CorruptedRevlog => { | |||
|
102 | CommandErrorKind::Abort(Some( | |||
|
103 | "abort: corrupted revlog\n".into(), | |||
|
104 | )) | |||
|
105 | } | |||
|
106 | ListRevTrackedFilesErrorKind::UnknowRevlogDataFormat(format) => { | |||
|
107 | CommandErrorKind::Abort(Some( | |||
|
108 | utf8_to_local(&format!( | |||
|
109 | "abort: unknow revlog dataformat {:?}\n", | |||
|
110 | format | |||
|
111 | )) | |||
|
112 | .into(), | |||
|
113 | )) | |||
|
114 | } | |||
|
115 | }, | |||
|
116 | } | |||
|
117 | } | |||
|
118 | ||||
|
119 | /// Convert `ListDirstateTrackedFilesError` to `CommandError` | |||
53 | fn map_dirstate_error(err: ListDirstateTrackedFilesError) -> CommandError { |
|
120 | fn map_dirstate_error(err: ListDirstateTrackedFilesError) -> CommandError { | |
54 | CommandError { |
|
121 | CommandError { | |
55 | kind: match err.kind { |
|
122 | kind: match err.kind { | |
56 | ListDirstateTrackedFilesErrorKind::IoError(err) => { |
|
123 | ListDirstateTrackedFilesErrorKind::IoError(err) => { | |
57 | CommandErrorKind::Abort(Some( |
|
124 | CommandErrorKind::Abort(Some( | |
58 | utf8_to_local(&format!("abort: {}\n", err)).into(), |
|
125 | utf8_to_local(&format!("abort: {}\n", err)).into(), | |
59 | )) |
|
126 | )) | |
60 | } |
|
127 | } | |
61 | ListDirstateTrackedFilesErrorKind::ParseError(_) => { |
|
128 | ListDirstateTrackedFilesErrorKind::ParseError(_) => { | |
62 | CommandErrorKind::Abort(Some( |
|
129 | CommandErrorKind::Abort(Some( | |
63 | // TODO find a better error message |
|
130 | // TODO find a better error message | |
64 | b"abort: parse error\n".to_vec(), |
|
131 | b"abort: parse error\n".to_vec(), | |
65 | )) |
|
132 | )) | |
66 | } |
|
133 | } | |
67 | }, |
|
134 | }, | |
68 | } |
|
135 | } | |
69 | } |
|
136 | } |
@@ -1,121 +1,141 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( | |||
|
31 | Arg::with_name("rev") | |||
|
32 | .help("search the repository as it is in REV") | |||
|
33 | .short("-r") | |||
|
34 | .long("--revision") | |||
|
35 | .value_name("REV") | |||
|
36 | .takes_value(true), | |||
|
37 | ) | |||
|
38 | .about(commands::files::HELP_TEXT), | |||
30 | ) |
|
39 | ) | |
31 | .subcommand( |
|
40 | .subcommand( | |
32 | SubCommand::with_name("debugdata") |
|
41 | SubCommand::with_name("debugdata") | |
33 | .about(commands::debugdata::HELP_TEXT) |
|
42 | .about(commands::debugdata::HELP_TEXT) | |
34 | .arg( |
|
43 | .arg( | |
35 | Arg::with_name("changelog") |
|
44 | Arg::with_name("changelog") | |
36 | .help("open changelog") |
|
45 | .help("open changelog") | |
37 | .short("-c") |
|
46 | .short("-c") | |
38 | .long("--changelog"), |
|
47 | .long("--changelog"), | |
39 | ) |
|
48 | ) | |
40 | .arg( |
|
49 | .arg( | |
41 | Arg::with_name("manifest") |
|
50 | Arg::with_name("manifest") | |
42 | .help("open manifest") |
|
51 | .help("open manifest") | |
43 | .short("-m") |
|
52 | .short("-m") | |
44 | .long("--manifest"), |
|
53 | .long("--manifest"), | |
45 | ) |
|
54 | ) | |
46 | .group( |
|
55 | .group( | |
47 | ArgGroup::with_name("") |
|
56 | ArgGroup::with_name("") | |
48 | .args(&["changelog", "manifest"]) |
|
57 | .args(&["changelog", "manifest"]) | |
49 | .required(true), |
|
58 | .required(true), | |
50 | ) |
|
59 | ) | |
51 | .arg( |
|
60 | .arg( | |
52 | Arg::with_name("rev") |
|
61 | Arg::with_name("rev") | |
53 | .help("revision") |
|
62 | .help("revision") | |
54 | .required(true) |
|
63 | .required(true) | |
55 | .value_name("REV"), |
|
64 | .value_name("REV"), | |
56 | ), |
|
65 | ), | |
57 | ); |
|
66 | ); | |
58 |
|
67 | |||
59 | let matches = app.clone().get_matches_safe().unwrap_or_else(|err| { |
|
68 | let matches = app.clone().get_matches_safe().unwrap_or_else(|err| { | |
60 | let _ = ui::Ui::new().writeln_stderr_str(&err.message); |
|
69 | let _ = ui::Ui::new().writeln_stderr_str(&err.message); | |
61 | std::process::exit(exitcode::UNIMPLEMENTED_COMMAND) |
|
70 | std::process::exit(exitcode::UNIMPLEMENTED_COMMAND) | |
62 | }); |
|
71 | }); | |
63 |
|
72 | |||
64 | let ui = ui::Ui::new(); |
|
73 | let ui = ui::Ui::new(); | |
65 |
|
74 | |||
66 | let command_result = match_subcommand(matches, &ui); |
|
75 | let command_result = match_subcommand(matches, &ui); | |
67 |
|
76 | |||
68 | match command_result { |
|
77 | match command_result { | |
69 | Ok(_) => std::process::exit(exitcode::OK), |
|
78 | Ok(_) => std::process::exit(exitcode::OK), | |
70 | Err(e) => { |
|
79 | Err(e) => { | |
71 | let message = e.get_error_message_bytes(); |
|
80 | let message = e.get_error_message_bytes(); | |
72 | if let Some(msg) = message { |
|
81 | if let Some(msg) = message { | |
73 | match ui.write_stderr(&msg) { |
|
82 | match ui.write_stderr(&msg) { | |
74 | Ok(_) => (), |
|
83 | Ok(_) => (), | |
75 | Err(_) => std::process::exit(exitcode::ABORT), |
|
84 | Err(_) => std::process::exit(exitcode::ABORT), | |
76 | }; |
|
85 | }; | |
77 | }; |
|
86 | }; | |
78 | e.exit() |
|
87 | e.exit() | |
79 | } |
|
88 | } | |
80 | } |
|
89 | } | |
81 | } |
|
90 | } | |
82 |
|
91 | |||
83 | fn match_subcommand( |
|
92 | fn match_subcommand( | |
84 | matches: ArgMatches, |
|
93 | matches: ArgMatches, | |
85 | ui: &ui::Ui, |
|
94 | ui: &ui::Ui, | |
86 | ) -> Result<(), CommandError> { |
|
95 | ) -> Result<(), CommandError> { | |
87 | match matches.subcommand() { |
|
96 | match matches.subcommand() { | |
88 | ("root", _) => commands::root::RootCommand::new().run(&ui), |
|
97 | ("root", _) => commands::root::RootCommand::new().run(&ui), | |
89 | ("files", _) => commands::files::FilesCommand::new().run(&ui), |
|
98 | ("files", Some(matches)) => { | |
|
99 | commands::files::FilesCommand::try_from(matches)?.run(&ui) | |||
|
100 | } | |||
90 | ("debugdata", Some(matches)) => { |
|
101 | ("debugdata", Some(matches)) => { | |
91 | commands::debugdata::DebugDataCommand::try_from(matches)?.run(&ui) |
|
102 | commands::debugdata::DebugDataCommand::try_from(matches)?.run(&ui) | |
92 | } |
|
103 | } | |
93 | _ => unreachable!(), // Because of AppSettings::SubcommandRequired, |
|
104 | _ => unreachable!(), // Because of AppSettings::SubcommandRequired, | |
94 | } |
|
105 | } | |
95 | } |
|
106 | } | |
96 |
|
107 | |||
|
108 | impl<'a> TryFrom<&'a ArgMatches<'_>> for commands::files::FilesCommand<'a> { | |||
|
109 | type Error = CommandError; | |||
|
110 | ||||
|
111 | fn try_from(args: &'a ArgMatches) -> Result<Self, Self::Error> { | |||
|
112 | let rev = args.value_of("rev"); | |||
|
113 | Ok(commands::files::FilesCommand::new(rev)) | |||
|
114 | } | |||
|
115 | } | |||
|
116 | ||||
97 | impl<'a> TryFrom<&'a ArgMatches<'_>> |
|
117 | impl<'a> TryFrom<&'a ArgMatches<'_>> | |
98 | for commands::debugdata::DebugDataCommand<'a> |
|
118 | for commands::debugdata::DebugDataCommand<'a> | |
99 | { |
|
119 | { | |
100 | type Error = CommandError; |
|
120 | type Error = CommandError; | |
101 |
|
121 | |||
102 | fn try_from(args: &'a ArgMatches) -> Result<Self, Self::Error> { |
|
122 | fn try_from(args: &'a ArgMatches) -> Result<Self, Self::Error> { | |
103 | let rev = args |
|
123 | let rev = args | |
104 | .value_of("rev") |
|
124 | .value_of("rev") | |
105 | .expect("rev should be a required argument"); |
|
125 | .expect("rev should be a required argument"); | |
106 | let kind = match ( |
|
126 | let kind = match ( | |
107 | args.is_present("changelog"), |
|
127 | args.is_present("changelog"), | |
108 | args.is_present("manifest"), |
|
128 | args.is_present("manifest"), | |
109 | ) { |
|
129 | ) { | |
110 | (true, false) => DebugDataKind::Changelog, |
|
130 | (true, false) => DebugDataKind::Changelog, | |
111 | (false, true) => DebugDataKind::Manifest, |
|
131 | (false, true) => DebugDataKind::Manifest, | |
112 | (true, true) => { |
|
132 | (true, true) => { | |
113 | unreachable!("Should not happen since options are exclusive") |
|
133 | unreachable!("Should not happen since options are exclusive") | |
114 | } |
|
134 | } | |
115 | (false, false) => { |
|
135 | (false, false) => { | |
116 | unreachable!("Should not happen since options are required") |
|
136 | unreachable!("Should not happen since options are required") | |
117 | } |
|
137 | } | |
118 | }; |
|
138 | }; | |
119 | Ok(commands::debugdata::DebugDataCommand::new(rev, kind)) |
|
139 | Ok(commands::debugdata::DebugDataCommand::new(rev, kind)) | |
120 | } |
|
140 | } | |
121 | } |
|
141 | } |
General Comments 0
You need to be logged in to leave comments.
Login now