##// END OF EJS Templates
rhg: replace command structs with functions...
Simon Sapin -
r47250:184e4655 default
parent child Browse files
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 use crate::error::CommandError;
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 use crate::error::CommandError;
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 let mut stdout = ui.stdout_buffer();
37 let mut stdout = ui.stdout_buffer();
32 stdout.write_all(&data)?;
38 stdout.write_all(&data)?;
33 stdout.flush()?;
39 stdout.flush()?;
34
40
35 Ok(())
41 Ok(())
36 }
37 }
42 }
@@ -1,6 +1,6 b''
1 use crate::commands::Command;
2 use crate::error::CommandError;
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 use crate::error::CommandError;
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 use crate::error::CommandError;
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", _) => commands::root::RootCommand::new().run(&ui, &config),
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 ("debugdata", Some(matches)) => {
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