##// END OF EJS Templates
rhg: Move subcommand CLI arguments definitions to respective modules...
Simon Sapin -
r47229:d5896fad default draft
parent child Browse files
Show More
@@ -1,51 +1,73 b''
1 1 use crate::error::CommandError;
2 2 use crate::ui::Ui;
3 use clap::Arg;
3 4 use clap::ArgMatches;
4 5 use hg::config::Config;
5 6 use hg::operations::cat;
6 7 use hg::repo::Repo;
7 8 use hg::utils::hg_path::HgPathBuf;
8 9 use micro_timer::timed;
9 10 use std::convert::TryFrom;
10 11
11 12 pub const HELP_TEXT: &str = "
12 13 Output the current or given revision of files
13 14 ";
14 15
16 pub fn args() -> clap::App<'static, 'static> {
17 clap::SubCommand::with_name("cat")
18 .arg(
19 Arg::with_name("rev")
20 .help("search the repository as it is in REV")
21 .short("-r")
22 .long("--revision")
23 .value_name("REV")
24 .takes_value(true),
25 )
26 .arg(
27 clap::Arg::with_name("files")
28 .required(true)
29 .multiple(true)
30 .empty_values(false)
31 .value_name("FILE")
32 .help("Activity to start: activity@category"),
33 )
34 .about(HELP_TEXT)
35 }
36
15 37 #[timed]
16 38 pub fn run(
17 39 ui: &Ui,
18 40 config: &Config,
19 41 args: &ArgMatches,
20 42 ) -> Result<(), CommandError> {
21 43 let rev = args.value_of("rev");
22 44 let file_args = match args.values_of("files") {
23 45 Some(files) => files.collect(),
24 46 None => vec![],
25 47 };
26 48
27 49 let repo = Repo::find(config)?;
28 50 let cwd = hg::utils::current_dir()?;
29 51
30 52 let mut files = vec![];
31 53 for file in file_args.iter() {
32 54 // TODO: actually normalize `..` path segments etc?
33 55 let normalized = cwd.join(&file);
34 56 let stripped = normalized
35 57 .strip_prefix(&repo.working_directory_path())
36 58 // TODO: error message for path arguments outside of the repo
37 59 .map_err(|_| CommandError::abort(""))?;
38 60 let hg_file = HgPathBuf::try_from(stripped.to_path_buf())
39 61 .map_err(|e| CommandError::abort(e.to_string()))?;
40 62 files.push(hg_file);
41 63 }
42 64
43 65 match rev {
44 66 Some(rev) => {
45 67 let data = cat(&repo, rev, &files).map_err(|e| (e, rev))?;
46 68 ui.write_stdout(&data)?;
47 69 Ok(())
48 70 }
49 71 None => Err(CommandError::Unimplemented.into()),
50 72 }
51 73 }
@@ -1,42 +1,72 b''
1 1 use crate::error::CommandError;
2 2 use crate::ui::Ui;
3 use clap::Arg;
4 use clap::ArgGroup;
3 5 use clap::ArgMatches;
4 6 use hg::config::Config;
5 7 use hg::operations::{debug_data, DebugDataKind};
6 8 use hg::repo::Repo;
7 9 use micro_timer::timed;
8 10
9 11 pub const HELP_TEXT: &str = "
10 12 Dump the contents of a data file revision
11 13 ";
12 14
15 pub fn args() -> clap::App<'static, 'static> {
16 clap::SubCommand::with_name("debugdata")
17 .arg(
18 Arg::with_name("changelog")
19 .help("open changelog")
20 .short("-c")
21 .long("--changelog"),
22 )
23 .arg(
24 Arg::with_name("manifest")
25 .help("open manifest")
26 .short("-m")
27 .long("--manifest"),
28 )
29 .group(
30 ArgGroup::with_name("")
31 .args(&["changelog", "manifest"])
32 .required(true),
33 )
34 .arg(
35 Arg::with_name("rev")
36 .help("revision")
37 .required(true)
38 .value_name("REV"),
39 )
40 .about(HELP_TEXT)
41 }
42
13 43 #[timed]
14 44 pub fn run(
15 45 ui: &Ui,
16 46 config: &Config,
17 47 args: &ArgMatches,
18 48 ) -> Result<(), CommandError> {
19 49 let rev = args
20 50 .value_of("rev")
21 51 .expect("rev should be a required argument");
22 52 let kind =
23 53 match (args.is_present("changelog"), args.is_present("manifest")) {
24 54 (true, false) => DebugDataKind::Changelog,
25 55 (false, true) => DebugDataKind::Manifest,
26 56 (true, true) => {
27 57 unreachable!("Should not happen since options are exclusive")
28 58 }
29 59 (false, false) => {
30 60 unreachable!("Should not happen since options are required")
31 61 }
32 62 };
33 63
34 64 let repo = Repo::find(config)?;
35 65 let data = debug_data(&repo, rev, kind).map_err(|e| (e, rev))?;
36 66
37 67 let mut stdout = ui.stdout_buffer();
38 68 stdout.write_all(&data)?;
39 69 stdout.flush()?;
40 70
41 71 Ok(())
42 72 }
@@ -1,26 +1,30 b''
1 1 use crate::error::CommandError;
2 2 use crate::ui::Ui;
3 3 use clap::ArgMatches;
4 4 use hg::config::Config;
5 5 use hg::repo::Repo;
6 6
7 7 pub const HELP_TEXT: &str = "
8 8 Print the current repo requirements.
9 9 ";
10 10
11 pub fn args() -> clap::App<'static, 'static> {
12 clap::SubCommand::with_name("debugrequirements").about(HELP_TEXT)
13 }
14
11 15 pub fn run(
12 16 ui: &Ui,
13 17 config: &Config,
14 18 _args: &ArgMatches,
15 19 ) -> Result<(), CommandError> {
16 20 let repo = Repo::find(config)?;
17 21 let mut output = String::new();
18 22 let mut requirements: Vec<_> = repo.requirements().iter().collect();
19 23 requirements.sort();
20 24 for req in requirements {
21 25 output.push_str(req);
22 26 output.push('\n');
23 27 }
24 28 ui.write_stdout(output.as_bytes())?;
25 29 Ok(())
26 30 }
@@ -1,55 +1,69 b''
1 1 use crate::error::CommandError;
2 2 use crate::ui::Ui;
3 use clap::Arg;
3 4 use clap::ArgMatches;
4 5 use hg::config::Config;
5 6 use hg::operations::list_rev_tracked_files;
6 7 use hg::operations::Dirstate;
7 8 use hg::repo::Repo;
8 9 use hg::utils::files::{get_bytes_from_path, relativize_path};
9 10 use hg::utils::hg_path::{HgPath, HgPathBuf};
10 11
11 12 pub const HELP_TEXT: &str = "
12 13 List tracked files.
13 14
14 15 Returns 0 on success.
15 16 ";
16 17
18 pub fn args() -> clap::App<'static, 'static> {
19 clap::SubCommand::with_name("files")
20 .arg(
21 Arg::with_name("rev")
22 .help("search the repository as it is in REV")
23 .short("-r")
24 .long("--revision")
25 .value_name("REV")
26 .takes_value(true),
27 )
28 .about(HELP_TEXT)
29 }
30
17 31 pub fn run(
18 32 ui: &Ui,
19 33 config: &Config,
20 34 args: &ArgMatches,
21 35 ) -> Result<(), CommandError> {
22 36 let rev = args.value_of("rev");
23 37
24 38 let repo = Repo::find(config)?;
25 39 if let Some(rev) = rev {
26 40 let files =
27 41 list_rev_tracked_files(&repo, rev).map_err(|e| (e, rev))?;
28 42 display_files(ui, &repo, files.iter())
29 43 } else {
30 44 let distate = Dirstate::new(&repo)?;
31 45 let files = distate.tracked_files()?;
32 46 display_files(ui, &repo, files)
33 47 }
34 48 }
35 49
36 50 fn display_files<'a>(
37 51 ui: &Ui,
38 52 repo: &Repo,
39 53 files: impl IntoIterator<Item = &'a HgPath>,
40 54 ) -> Result<(), CommandError> {
41 55 let cwd = hg::utils::current_dir()?;
42 56 let rooted_cwd = cwd
43 57 .strip_prefix(repo.working_directory_path())
44 58 .expect("cwd was already checked within the repository");
45 59 let rooted_cwd = HgPathBuf::from(get_bytes_from_path(rooted_cwd));
46 60
47 61 let mut stdout = ui.stdout_buffer();
48 62
49 63 for file in files {
50 64 stdout.write_all(relativize_path(file, &rooted_cwd).as_ref())?;
51 65 stdout.write_all(b"\n")?;
52 66 }
53 67 stdout.flush()?;
54 68 Ok(())
55 69 }
@@ -1,24 +1,28 b''
1 1 use crate::error::CommandError;
2 2 use crate::ui::Ui;
3 3 use clap::ArgMatches;
4 4 use format_bytes::format_bytes;
5 5 use hg::config::Config;
6 6 use hg::repo::Repo;
7 7 use hg::utils::files::get_bytes_from_path;
8 8
9 9 pub const HELP_TEXT: &str = "
10 10 Print the root directory of the current repository.
11 11
12 12 Returns 0 on success.
13 13 ";
14 14
15 pub fn args() -> clap::App<'static, 'static> {
16 clap::SubCommand::with_name("root").about(HELP_TEXT)
17 }
18
15 19 pub fn run(
16 20 ui: &Ui,
17 21 config: &Config,
18 22 _args: &ArgMatches,
19 23 ) -> Result<(), CommandError> {
20 24 let repo = Repo::find(config)?;
21 25 let bytes = get_bytes_from_path(repo.working_directory_path());
22 26 ui.write_stdout(&format_bytes!(b"{}\n", bytes.as_slice()))?;
23 27 Ok(())
24 28 }
@@ -1,137 +1,73 b''
1 1 extern crate log;
2 2 use clap::App;
3 3 use clap::AppSettings;
4 use clap::Arg;
5 use clap::ArgGroup;
6 4 use clap::ArgMatches;
7 use clap::SubCommand;
8 5 use format_bytes::format_bytes;
9 6
10 7 mod commands;
11 8 mod error;
12 9 mod exitcode;
13 10 mod ui;
14 11 use error::CommandError;
15 12
16 13 fn main() {
17 14 env_logger::init();
18 15 let app = App::new("rhg")
19 16 .setting(AppSettings::AllowInvalidUtf8)
20 17 .setting(AppSettings::SubcommandRequired)
21 18 .setting(AppSettings::VersionlessSubcommands)
22 19 .version("0.0.1")
23 .subcommand(
24 SubCommand::with_name("root").about(commands::root::HELP_TEXT),
25 )
26 .subcommand(
27 SubCommand::with_name("files")
28 .arg(
29 Arg::with_name("rev")
30 .help("search the repository as it is in REV")
31 .short("-r")
32 .long("--revision")
33 .value_name("REV")
34 .takes_value(true),
35 )
36 .about(commands::files::HELP_TEXT),
37 )
38 .subcommand(
39 SubCommand::with_name("cat")
40 .arg(
41 Arg::with_name("rev")
42 .help("search the repository as it is in REV")
43 .short("-r")
44 .long("--revision")
45 .value_name("REV")
46 .takes_value(true),
47 )
48 .arg(
49 clap::Arg::with_name("files")
50 .required(true)
51 .multiple(true)
52 .empty_values(false)
53 .value_name("FILE")
54 .help("Activity to start: activity@category"),
55 )
56 .about(commands::cat::HELP_TEXT),
57 )
58 .subcommand(
59 SubCommand::with_name("debugdata")
60 .about(commands::debugdata::HELP_TEXT)
61 .arg(
62 Arg::with_name("changelog")
63 .help("open changelog")
64 .short("-c")
65 .long("--changelog"),
66 )
67 .arg(
68 Arg::with_name("manifest")
69 .help("open manifest")
70 .short("-m")
71 .long("--manifest"),
72 )
73 .group(
74 ArgGroup::with_name("")
75 .args(&["changelog", "manifest"])
76 .required(true),
77 )
78 .arg(
79 Arg::with_name("rev")
80 .help("revision")
81 .required(true)
82 .value_name("REV"),
83 ),
84 )
85 .subcommand(
86 SubCommand::with_name("debugrequirements")
87 .about(commands::debugrequirements::HELP_TEXT),
88 );
20 .subcommand(commands::root::args())
21 .subcommand(commands::files::args())
22 .subcommand(commands::cat::args())
23 .subcommand(commands::debugdata::args())
24 .subcommand(commands::debugrequirements::args());
89 25
90 26 let matches = app.clone().get_matches_safe().unwrap_or_else(|err| {
91 27 let _ = ui::Ui::new().writeln_stderr_str(&err.message);
92 28 std::process::exit(exitcode::UNIMPLEMENTED)
93 29 });
94 30
95 31 let ui = ui::Ui::new();
96 32
97 33 let command_result = match_subcommand(matches, &ui);
98 34
99 35 let exit_code = match command_result {
100 36 Ok(_) => exitcode::OK,
101 37
102 38 // Exit with a specific code and no error message to let a potential
103 39 // wrapper script fallback to Python-based Mercurial.
104 40 Err(CommandError::Unimplemented) => exitcode::UNIMPLEMENTED,
105 41
106 42 Err(CommandError::Abort { message }) => {
107 43 if !message.is_empty() {
108 44 // Ignore errors when writing to stderr, we’re already exiting
109 45 // with failure code so there’s not much more we can do.
110 46 let _ =
111 47 ui.write_stderr(&format_bytes!(b"abort: {}\n", message));
112 48 }
113 49 exitcode::ABORT
114 50 }
115 51 };
116 52 std::process::exit(exit_code)
117 53 }
118 54
119 55 fn match_subcommand(
120 56 matches: ArgMatches,
121 57 ui: &ui::Ui,
122 58 ) -> Result<(), CommandError> {
123 59 let config = hg::config::Config::load()?;
124 60
125 61 match matches.subcommand() {
126 62 ("root", Some(matches)) => commands::root::run(ui, &config, matches),
127 63 ("files", Some(matches)) => commands::files::run(ui, &config, matches),
128 64 ("cat", Some(matches)) => commands::cat::run(ui, &config, matches),
129 65 ("debugdata", Some(matches)) => {
130 66 commands::debugdata::run(ui, &config, matches)
131 67 }
132 68 ("debugrequirements", Some(matches)) => {
133 69 commands::debugrequirements::run(ui, &config, matches)
134 70 }
135 71 _ => unreachable!(), // Because of AppSettings::SubcommandRequired,
136 72 }
137 73 }
General Comments 0
You need to be logged in to leave comments. Login now