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