Show More
@@ -3,13 +3,3 b' pub mod debugdata;' | |||||
3 | pub mod debugrequirements; |
|
3 | pub mod debugrequirements; | |
4 | pub mod files; |
|
4 | pub mod files; | |
5 | pub mod root; |
|
5 | pub mod root; | |
6 | use crate::error::CommandError; |
|
|||
7 | use crate::ui::Ui; |
|
|||
8 | use hg::config::Config; |
|
|||
9 |
|
||||
10 | /// The common trait for rhg commands |
|
|||
11 | /// |
|
|||
12 | /// Normalize the interface of the commands provided by rhg |
|
|||
13 | pub trait Command { |
|
|||
14 | fn run(&self, ui: &Ui, config: &Config) -> Result<(), CommandError>; |
|
|||
15 | } |
|
@@ -1,6 +1,6 b'' | |||||
1 | use crate::commands::Command; |
|
|||
2 |
|
|
1 | use crate::error::CommandError; | |
3 | use crate::ui::Ui; |
|
2 | use crate::ui::Ui; | |
|
3 | use clap::ArgMatches; | |||
4 | use hg::config::Config; |
|
4 | use hg::config::Config; | |
5 | use hg::operations::cat; |
|
5 | use hg::operations::cat; | |
6 | use hg::repo::Repo; |
|
6 | use hg::repo::Repo; | |
@@ -12,47 +12,40 b' pub const HELP_TEXT: &str = "' | |||||
12 | Output the current or given revision of files |
|
12 | Output the current or given revision of files | |
13 | "; |
|
13 | "; | |
14 |
|
14 | |||
15 | pub struct CatCommand<'a> { |
|
15 | #[timed] | |
16 | rev: Option<&'a str>, |
|
16 | pub fn run( | |
17 | files: Vec<&'a str>, |
|
17 | ui: &Ui, | |
18 | } |
|
18 | config: &Config, | |
|
19 | args: &ArgMatches, | |||
|
20 | ) -> Result<(), CommandError> { | |||
|
21 | let rev = args.value_of("rev"); | |||
|
22 | let file_args = match args.values_of("files") { | |||
|
23 | Some(files) => files.collect(), | |||
|
24 | None => vec![], | |||
|
25 | }; | |||
19 |
|
26 | |||
20 | impl<'a> CatCommand<'a> { |
|
27 | let repo = Repo::find(config)?; | |
21 | pub fn new(rev: Option<&'a str>, files: Vec<&'a str>) -> Self { |
|
28 | let cwd = hg::utils::current_dir()?; | |
22 | Self { rev, files } |
|
29 | ||
|
30 | let mut files = vec![]; | |||
|
31 | for file in file_args.iter() { | |||
|
32 | // TODO: actually normalize `..` path segments etc? | |||
|
33 | let normalized = cwd.join(&file); | |||
|
34 | let stripped = normalized | |||
|
35 | .strip_prefix(&repo.working_directory_path()) | |||
|
36 | // TODO: error message for path arguments outside of the repo | |||
|
37 | .map_err(|_| CommandError::abort(""))?; | |||
|
38 | let hg_file = HgPathBuf::try_from(stripped.to_path_buf()) | |||
|
39 | .map_err(|e| CommandError::abort(e.to_string()))?; | |||
|
40 | files.push(hg_file); | |||
23 | } |
|
41 | } | |
24 |
|
42 | |||
25 | fn display(&self, ui: &Ui, data: &[u8]) -> Result<(), CommandError> { |
|
43 | match rev { | |
26 | ui.write_stdout(data)?; |
|
44 | Some(rev) => { | |
27 | Ok(()) |
|
45 | let data = cat(&repo, rev, &files).map_err(|e| (e, rev))?; | |
|
46 | ui.write_stdout(&data)?; | |||
|
47 | Ok(()) | |||
|
48 | } | |||
|
49 | None => Err(CommandError::Unimplemented.into()), | |||
28 | } |
|
50 | } | |
29 | } |
|
51 | } | |
30 |
|
||||
31 | impl<'a> Command for CatCommand<'a> { |
|
|||
32 | #[timed] |
|
|||
33 | fn run(&self, ui: &Ui, config: &Config) -> Result<(), CommandError> { |
|
|||
34 | let repo = Repo::find(config)?; |
|
|||
35 | let cwd = hg::utils::current_dir()?; |
|
|||
36 |
|
||||
37 | let mut files = vec![]; |
|
|||
38 | for file in self.files.iter() { |
|
|||
39 | // TODO: actually normalize `..` path segments etc? |
|
|||
40 | let normalized = cwd.join(&file); |
|
|||
41 | let stripped = normalized |
|
|||
42 | .strip_prefix(&repo.working_directory_path()) |
|
|||
43 | // TODO: error message for path arguments outside of the repo |
|
|||
44 | .map_err(|_| CommandError::abort(""))?; |
|
|||
45 | let hg_file = HgPathBuf::try_from(stripped.to_path_buf()) |
|
|||
46 | .map_err(|e| CommandError::abort(e.to_string()))?; |
|
|||
47 | files.push(hg_file); |
|
|||
48 | } |
|
|||
49 |
|
||||
50 | match self.rev { |
|
|||
51 | Some(rev) => { |
|
|||
52 | let data = cat(&repo, rev, &files).map_err(|e| (e, rev))?; |
|
|||
53 | self.display(ui, &data) |
|
|||
54 | } |
|
|||
55 | None => Err(CommandError::Unimplemented.into()), |
|
|||
56 | } |
|
|||
57 | } |
|
|||
58 | } |
|
@@ -1,6 +1,6 b'' | |||||
1 | use crate::commands::Command; |
|
|||
2 |
|
|
1 | use crate::error::CommandError; | |
3 | use crate::ui::Ui; |
|
2 | use crate::ui::Ui; | |
|
3 | use clap::ArgMatches; | |||
4 | use hg::config::Config; |
|
4 | use hg::config::Config; | |
5 | use hg::operations::{debug_data, DebugDataKind}; |
|
5 | use hg::operations::{debug_data, DebugDataKind}; | |
6 | use hg::repo::Repo; |
|
6 | use hg::repo::Repo; | |
@@ -10,28 +10,33 b' pub const HELP_TEXT: &str = "' | |||||
10 | Dump the contents of a data file revision |
|
10 | Dump the contents of a data file revision | |
11 | "; |
|
11 | "; | |
12 |
|
12 | |||
13 | pub struct DebugDataCommand<'a> { |
|
13 | #[timed] | |
14 | rev: &'a str, |
|
14 | pub fn run( | |
15 | kind: DebugDataKind, |
|
15 | ui: &Ui, | |
16 | } |
|
16 | config: &Config, | |
17 |
|
17 | args: &ArgMatches, | ||
18 | impl<'a> DebugDataCommand<'a> { |
|
18 | ) -> Result<(), CommandError> { | |
19 | pub fn new(rev: &'a str, kind: DebugDataKind) -> Self { |
|
19 | let rev = args | |
20 | DebugDataCommand { rev, kind } |
|
20 | .value_of("rev") | |
21 | } |
|
21 | .expect("rev should be a required argument"); | |
22 | } |
|
22 | let kind = | |
|
23 | match (args.is_present("changelog"), args.is_present("manifest")) { | |||
|
24 | (true, false) => DebugDataKind::Changelog, | |||
|
25 | (false, true) => DebugDataKind::Manifest, | |||
|
26 | (true, true) => { | |||
|
27 | unreachable!("Should not happen since options are exclusive") | |||
|
28 | } | |||
|
29 | (false, false) => { | |||
|
30 | unreachable!("Should not happen since options are required") | |||
|
31 | } | |||
|
32 | }; | |||
23 |
|
33 | |||
24 | impl<'a> Command for DebugDataCommand<'a> { |
|
34 | let repo = Repo::find(config)?; | |
25 | #[timed] |
|
35 | let data = debug_data(&repo, rev, kind).map_err(|e| (e, rev))?; | |
26 | fn run(&self, ui: &Ui, config: &Config) -> Result<(), CommandError> { |
|
|||
27 | let repo = Repo::find(config)?; |
|
|||
28 | let data = debug_data(&repo, self.rev, self.kind) |
|
|||
29 | .map_err(|e| (e, self.rev))?; |
|
|||
30 |
|
36 | |||
31 |
|
|
37 | let mut stdout = ui.stdout_buffer(); | |
32 |
|
|
38 | stdout.write_all(&data)?; | |
33 |
|
|
39 | stdout.flush()?; | |
34 |
|
40 | |||
35 |
|
|
41 | Ok(()) | |
36 | } |
|
|||
37 | } |
|
42 | } |
@@ -1,6 +1,6 b'' | |||||
1 | use crate::commands::Command; |
|
|||
2 |
|
|
1 | use crate::error::CommandError; | |
3 | use crate::ui::Ui; |
|
2 | use crate::ui::Ui; | |
|
3 | use clap::ArgMatches; | |||
4 | use hg::config::Config; |
|
4 | use hg::config::Config; | |
5 | use hg::repo::Repo; |
|
5 | use hg::repo::Repo; | |
6 |
|
6 | |||
@@ -8,25 +8,19 b' pub const HELP_TEXT: &str = "' | |||||
8 | Print the current repo requirements. |
|
8 | Print the current repo requirements. | |
9 | "; |
|
9 | "; | |
10 |
|
10 | |||
11 | pub struct DebugRequirementsCommand {} |
|
11 | pub fn run( | |
12 |
|
12 | ui: &Ui, | ||
13 | impl DebugRequirementsCommand { |
|
13 | config: &Config, | |
14 | pub fn new() -> Self { |
|
14 | _args: &ArgMatches, | |
15 | DebugRequirementsCommand {} |
|
15 | ) -> Result<(), CommandError> { | |
|
16 | let repo = Repo::find(config)?; | |||
|
17 | let mut output = String::new(); | |||
|
18 | let mut requirements: Vec<_> = repo.requirements().iter().collect(); | |||
|
19 | requirements.sort(); | |||
|
20 | for req in requirements { | |||
|
21 | output.push_str(req); | |||
|
22 | output.push('\n'); | |||
16 | } |
|
23 | } | |
|
24 | ui.write_stdout(output.as_bytes())?; | |||
|
25 | Ok(()) | |||
17 | } |
|
26 | } | |
18 |
|
||||
19 | impl Command for DebugRequirementsCommand { |
|
|||
20 | fn run(&self, ui: &Ui, config: &Config) -> Result<(), CommandError> { |
|
|||
21 | let repo = Repo::find(config)?; |
|
|||
22 | let mut output = String::new(); |
|
|||
23 | let mut requirements: Vec<_> = repo.requirements().iter().collect(); |
|
|||
24 | requirements.sort(); |
|
|||
25 | for req in requirements { |
|
|||
26 | output.push_str(req); |
|
|||
27 | output.push('\n'); |
|
|||
28 | } |
|
|||
29 | ui.write_stdout(output.as_bytes())?; |
|
|||
30 | Ok(()) |
|
|||
31 | } |
|
|||
32 | } |
|
@@ -1,6 +1,6 b'' | |||||
1 | use crate::commands::Command; |
|
|||
2 |
|
|
1 | use crate::error::CommandError; | |
3 | use crate::ui::Ui; |
|
2 | use crate::ui::Ui; | |
|
3 | use clap::ArgMatches; | |||
4 | use hg::config::Config; |
|
4 | use hg::config::Config; | |
5 | use hg::operations::list_rev_tracked_files; |
|
5 | use hg::operations::list_rev_tracked_files; | |
6 | use hg::operations::Dirstate; |
|
6 | use hg::operations::Dirstate; | |
@@ -14,49 +14,42 b' List tracked files.' | |||||
14 | Returns 0 on success. |
|
14 | Returns 0 on success. | |
15 | "; |
|
15 | "; | |
16 |
|
16 | |||
17 | pub struct FilesCommand<'a> { |
|
17 | pub fn run( | |
18 | rev: Option<&'a str>, |
|
18 | ui: &Ui, | |
19 | } |
|
19 | config: &Config, | |
20 |
|
20 | args: &ArgMatches, | ||
21 | impl<'a> FilesCommand<'a> { |
|
21 | ) -> Result<(), CommandError> { | |
22 | pub fn new(rev: Option<&'a str>) -> Self { |
|
22 | let rev = args.value_of("rev"); | |
23 | FilesCommand { rev } |
|
|||
24 | } |
|
|||
25 |
|
23 | |||
26 | fn display_files( |
|
24 | let repo = Repo::find(config)?; | |
27 | &self, |
|
25 | if let Some(rev) = rev { | |
28 | ui: &Ui, |
|
26 | let files = | |
29 | repo: &Repo, |
|
27 | list_rev_tracked_files(&repo, rev).map_err(|e| (e, rev))?; | |
30 | files: impl IntoIterator<Item = &'a HgPath>, |
|
28 | display_files(ui, &repo, files.iter()) | |
31 | ) -> Result<(), CommandError> { |
|
29 | } else { | |
32 | let cwd = hg::utils::current_dir()?; |
|
30 | let distate = Dirstate::new(&repo)?; | |
33 | let rooted_cwd = cwd |
|
31 | let files = distate.tracked_files()?; | |
34 | .strip_prefix(repo.working_directory_path()) |
|
32 | display_files(ui, &repo, files) | |
35 | .expect("cwd was already checked within the repository"); |
|
|||
36 | let rooted_cwd = HgPathBuf::from(get_bytes_from_path(rooted_cwd)); |
|
|||
37 |
|
||||
38 | let mut stdout = ui.stdout_buffer(); |
|
|||
39 |
|
||||
40 | for file in files { |
|
|||
41 | stdout.write_all(relativize_path(file, &rooted_cwd).as_ref())?; |
|
|||
42 | stdout.write_all(b"\n")?; |
|
|||
43 | } |
|
|||
44 | stdout.flush()?; |
|
|||
45 | Ok(()) |
|
|||
46 | } |
|
33 | } | |
47 | } |
|
34 | } | |
48 |
|
35 | |||
49 | impl<'a> Command for FilesCommand<'a> { |
|
36 | fn display_files<'a>( | |
50 | fn run(&self, ui: &Ui, config: &Config) -> Result<(), CommandError> { |
|
37 | ui: &Ui, | |
51 | let repo = Repo::find(config)?; |
|
38 | repo: &Repo, | |
52 | if let Some(rev) = self.rev { |
|
39 | files: impl IntoIterator<Item = &'a HgPath>, | |
53 | let files = |
|
40 | ) -> Result<(), CommandError> { | |
54 | list_rev_tracked_files(&repo, rev).map_err(|e| (e, rev))?; |
|
41 | let cwd = hg::utils::current_dir()?; | |
55 | self.display_files(ui, &repo, files.iter()) |
|
42 | let rooted_cwd = cwd | |
56 | } else { |
|
43 | .strip_prefix(repo.working_directory_path()) | |
57 | let distate = Dirstate::new(&repo)?; |
|
44 | .expect("cwd was already checked within the repository"); | |
58 | let files = distate.tracked_files()?; |
|
45 | let rooted_cwd = HgPathBuf::from(get_bytes_from_path(rooted_cwd)); | |
59 | self.display_files(ui, &repo, files) |
|
46 | ||
60 | } |
|
47 | let mut stdout = ui.stdout_buffer(); | |
|
48 | ||||
|
49 | for file in files { | |||
|
50 | stdout.write_all(relativize_path(file, &rooted_cwd).as_ref())?; | |||
|
51 | stdout.write_all(b"\n")?; | |||
61 | } |
|
52 | } | |
|
53 | stdout.flush()?; | |||
|
54 | Ok(()) | |||
62 | } |
|
55 | } |
@@ -1,6 +1,6 b'' | |||||
1 | use crate::commands::Command; |
|
|||
2 |
|
|
1 | use crate::error::CommandError; | |
3 | use crate::ui::Ui; |
|
2 | use crate::ui::Ui; | |
|
3 | use clap::ArgMatches; | |||
4 | use format_bytes::format_bytes; |
|
4 | use format_bytes::format_bytes; | |
5 | use hg::config::Config; |
|
5 | use hg::config::Config; | |
6 | use hg::repo::Repo; |
|
6 | use hg::repo::Repo; | |
@@ -12,19 +12,13 b' Print the root directory of the current ' | |||||
12 | Returns 0 on success. |
|
12 | Returns 0 on success. | |
13 | "; |
|
13 | "; | |
14 |
|
14 | |||
15 | pub struct RootCommand {} |
|
15 | pub fn run( | |
16 |
|
16 | ui: &Ui, | ||
17 | impl RootCommand { |
|
17 | config: &Config, | |
18 | pub fn new() -> Self { |
|
18 | _args: &ArgMatches, | |
19 | RootCommand {} |
|
19 | ) -> Result<(), CommandError> { | |
20 | } |
|
20 | let repo = Repo::find(config)?; | |
|
21 | let bytes = get_bytes_from_path(repo.working_directory_path()); | |||
|
22 | ui.write_stdout(&format_bytes!(b"{}\n", bytes.as_slice()))?; | |||
|
23 | Ok(()) | |||
21 | } |
|
24 | } | |
22 |
|
||||
23 | impl Command for RootCommand { |
|
|||
24 | fn run(&self, ui: &Ui, config: &Config) -> Result<(), CommandError> { |
|
|||
25 | let repo = Repo::find(config)?; |
|
|||
26 | let bytes = get_bytes_from_path(repo.working_directory_path()); |
|
|||
27 | ui.write_stdout(&format_bytes!(b"{}\n", bytes.as_slice()))?; |
|
|||
28 | Ok(()) |
|
|||
29 | } |
|
|||
30 | } |
|
@@ -6,14 +6,11 b' use clap::ArgGroup;' | |||||
6 | use clap::ArgMatches; |
|
6 | use clap::ArgMatches; | |
7 | use clap::SubCommand; |
|
7 | use clap::SubCommand; | |
8 | use format_bytes::format_bytes; |
|
8 | use format_bytes::format_bytes; | |
9 | use hg::operations::DebugDataKind; |
|
|||
10 | use std::convert::TryFrom; |
|
|||
11 |
|
9 | |||
12 | mod commands; |
|
10 | mod commands; | |
13 | mod error; |
|
11 | mod error; | |
14 | mod exitcode; |
|
12 | mod exitcode; | |
15 | mod ui; |
|
13 | mod ui; | |
16 | use commands::Command; |
|
|||
17 | use error::CommandError; |
|
14 | use error::CommandError; | |
18 |
|
15 | |||
19 | fn main() { |
|
16 | fn main() { | |
@@ -126,69 +123,15 b' fn match_subcommand(' | |||||
126 | let config = hg::config::Config::load()?; |
|
123 | let config = hg::config::Config::load()?; | |
127 |
|
124 | |||
128 | match matches.subcommand() { |
|
125 | match matches.subcommand() { | |
129 |
("root", |
|
126 | ("root", Some(matches)) => commands::root::run(ui, &config, matches), | |
130 |
("files", Some(matches)) => |
|
127 | ("files", Some(matches)) => commands::files::run(ui, &config, matches), | |
131 | commands::files::FilesCommand::try_from(matches)?.run(&ui, &config) |
|
128 | ("cat", Some(matches)) => commands::cat::run(ui, &config, matches), | |
132 | } |
|
129 | ("debugdata", Some(matches)) => { | |
133 | ("cat", Some(matches)) => { |
|
130 | commands::debugdata::run(ui, &config, matches) | |
134 | commands::cat::CatCommand::try_from(matches)?.run(&ui, &config) |
|
|||
135 | } |
|
131 | } | |
136 |
("debug |
|
132 | ("debugrequirements", Some(matches)) => { | |
137 | commands::debugdata::DebugDataCommand::try_from(matches)? |
|
133 | commands::debugrequirements::run(ui, &config, matches) | |
138 | .run(&ui, &config) |
|
|||
139 | } |
|
|||
140 | ("debugrequirements", _) => { |
|
|||
141 | commands::debugrequirements::DebugRequirementsCommand::new() |
|
|||
142 | .run(&ui, &config) |
|
|||
143 | } |
|
134 | } | |
144 | _ => unreachable!(), // Because of AppSettings::SubcommandRequired, |
|
135 | _ => unreachable!(), // Because of AppSettings::SubcommandRequired, | |
145 | } |
|
136 | } | |
146 | } |
|
137 | } | |
147 |
|
||||
148 | impl<'a> TryFrom<&'a ArgMatches<'_>> for commands::files::FilesCommand<'a> { |
|
|||
149 | type Error = CommandError; |
|
|||
150 |
|
||||
151 | fn try_from(args: &'a ArgMatches) -> Result<Self, Self::Error> { |
|
|||
152 | let rev = args.value_of("rev"); |
|
|||
153 | Ok(commands::files::FilesCommand::new(rev)) |
|
|||
154 | } |
|
|||
155 | } |
|
|||
156 |
|
||||
157 | impl<'a> TryFrom<&'a ArgMatches<'_>> for commands::cat::CatCommand<'a> { |
|
|||
158 | type Error = CommandError; |
|
|||
159 |
|
||||
160 | fn try_from(args: &'a ArgMatches) -> Result<Self, Self::Error> { |
|
|||
161 | let rev = args.value_of("rev"); |
|
|||
162 | let files = match args.values_of("files") { |
|
|||
163 | Some(files) => files.collect(), |
|
|||
164 | None => vec![], |
|
|||
165 | }; |
|
|||
166 | Ok(commands::cat::CatCommand::new(rev, files)) |
|
|||
167 | } |
|
|||
168 | } |
|
|||
169 |
|
||||
170 | impl<'a> TryFrom<&'a ArgMatches<'_>> |
|
|||
171 | for commands::debugdata::DebugDataCommand<'a> |
|
|||
172 | { |
|
|||
173 | type Error = CommandError; |
|
|||
174 |
|
||||
175 | fn try_from(args: &'a ArgMatches) -> Result<Self, Self::Error> { |
|
|||
176 | let rev = args |
|
|||
177 | .value_of("rev") |
|
|||
178 | .expect("rev should be a required argument"); |
|
|||
179 | let kind = match ( |
|
|||
180 | args.is_present("changelog"), |
|
|||
181 | args.is_present("manifest"), |
|
|||
182 | ) { |
|
|||
183 | (true, false) => DebugDataKind::Changelog, |
|
|||
184 | (false, true) => DebugDataKind::Manifest, |
|
|||
185 | (true, true) => { |
|
|||
186 | unreachable!("Should not happen since options are exclusive") |
|
|||
187 | } |
|
|||
188 | (false, false) => { |
|
|||
189 | unreachable!("Should not happen since options are required") |
|
|||
190 | } |
|
|||
191 | }; |
|
|||
192 | Ok(commands::debugdata::DebugDataCommand::new(rev, kind)) |
|
|||
193 | } |
|
|||
194 | } |
|
General Comments 0
You need to be logged in to leave comments.
Login now