Like in other "unsupported" cases that return a specific exit code
Details
Details
Diff Detail
Diff Detail
- Repository
- rHG Mercurial
- Branch
- default
- Lint
No Linters Available - Unit
No Unit Test Coverage
( )
Like in other "unsupported" cases that return a specific exit code
| No Linters Available |
| No Unit Test Coverage |
| Path | Packages | |||
|---|---|---|---|---|
| M | rust/rhg/src/error.rs (8 lines) | |||
| M | rust/rhg/src/main.rs (29 lines) | |||
| M | rust/rhg/src/ui.rs (5 lines) | |||
| M | tests/test-rhg.t (6 lines) |
| Commit | Parents | Author | Summary | Date |
|---|---|---|---|---|
| 210b228178e8 | c82d6363bc9e | Simon Sapin | Feb 12 2021, 10:54 AM |
| Status | Author | Revision | |
|---|---|---|---|
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin |
| // TODO: bytes-based (instead of Unicode-based) formatting | // TODO: bytes-based (instead of Unicode-based) formatting | ||||
| // of error messages to handle non-UTF-8 filenames etc: | // of error messages to handle non-UTF-8 filenames etc: | ||||
| // https://www.mercurial-scm.org/wiki/EncodingStrategy#Mixing_output | // https://www.mercurial-scm.org/wiki/EncodingStrategy#Mixing_output | ||||
| message: utf8_to_local(message.as_ref()).into(), | message: utf8_to_local(message.as_ref()).into(), | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /// For now we don’t differenciate between invalid CLI args and valid for `hg` | |||||
| /// but not supported yet by `rhg`. | |||||
| impl From<clap::Error> for CommandError { | |||||
| fn from(_: clap::Error) -> Self { | |||||
| CommandError::Unimplemented | |||||
| } | |||||
| } | |||||
| impl From<HgError> for CommandError { | impl From<HgError> for CommandError { | ||||
| fn from(error: HgError) -> Self { | fn from(error: HgError) -> Self { | ||||
| match error { | match error { | ||||
| HgError::UnsupportedFeature(_) => CommandError::Unimplemented, | HgError::UnsupportedFeature(_) => CommandError::Unimplemented, | ||||
| _ => CommandError::abort(error.to_string()), | _ => CommandError::abort(error.to_string()), | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| .takes_value(true) | .takes_value(true) | ||||
| // Ok: `--config section.key1=val --config section.key2=val2` | // Ok: `--config section.key1=val --config section.key2=val2` | ||||
| .multiple(true) | .multiple(true) | ||||
| // Not ok: `--config section.key1=val section.key2=val2` | // Not ok: `--config section.key1=val section.key2=val2` | ||||
| .number_of_values(1), | .number_of_values(1), | ||||
| ) | ) | ||||
| } | } | ||||
| fn main() { | fn main_with_result(ui: &ui::Ui) -> Result<(), CommandError> { | ||||
| env_logger::init(); | env_logger::init(); | ||||
| let app = App::new("rhg") | let app = App::new("rhg") | ||||
| .setting(AppSettings::AllowInvalidUtf8) | .setting(AppSettings::AllowInvalidUtf8) | ||||
| .setting(AppSettings::SubcommandRequired) | .setting(AppSettings::SubcommandRequired) | ||||
| .setting(AppSettings::VersionlessSubcommands) | .setting(AppSettings::VersionlessSubcommands) | ||||
| .version("0.0.1"); | .version("0.0.1"); | ||||
| let app = add_global_args(app); | let app = add_global_args(app); | ||||
| let app = add_subcommand_args(app); | let app = add_subcommand_args(app); | ||||
| let ui = ui::Ui::new(); | let matches = app.clone().get_matches_safe()?; | ||||
| let matches = app.clone().get_matches_safe().unwrap_or_else(|err| { | |||||
| let _ = ui.writeln_stderr_str(&err.message); | |||||
| std::process::exit(exitcode::UNIMPLEMENTED) | |||||
| }); | |||||
| let (subcommand_name, subcommand_matches) = matches.subcommand(); | let (subcommand_name, subcommand_matches) = matches.subcommand(); | ||||
| let run = subcommand_run_fn(subcommand_name) | let run = subcommand_run_fn(subcommand_name) | ||||
| .expect("unknown subcommand name from clap despite AppSettings::SubcommandRequired"); | .expect("unknown subcommand name from clap despite AppSettings::SubcommandRequired"); | ||||
| let args = subcommand_matches | let args = subcommand_matches | ||||
| .expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired"); | .expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired"); | ||||
| // Global arguments can be in either based on e.g. `hg -R ./foo log` v.s. | // Global arguments can be in either based on e.g. `hg -R ./foo log` v.s. | ||||
| // `hg log -R ./foo` | // `hg log -R ./foo` | ||||
| let value_of_global_arg = | let value_of_global_arg = | ||||
| |name| args.value_of_os(name).or_else(|| matches.value_of_os(name)); | |name| args.value_of_os(name).or_else(|| matches.value_of_os(name)); | ||||
| // For arguments where multiple occurences are allowed, return a | // For arguments where multiple occurences are allowed, return a | ||||
| // possibly-iterator of all values. | // possibly-iterator of all values. | ||||
| let values_of_global_arg = |name: &str| { | let values_of_global_arg = |name: &str| { | ||||
| let a = matches.values_of_os(name).into_iter().flatten(); | let a = matches.values_of_os(name).into_iter().flatten(); | ||||
| let b = args.values_of_os(name).into_iter().flatten(); | let b = args.values_of_os(name).into_iter().flatten(); | ||||
| a.chain(b) | a.chain(b) | ||||
| }; | }; | ||||
| let repo_path = value_of_global_arg("repository").map(Path::new); | let repo_path = value_of_global_arg("repository").map(Path::new); | ||||
| let result = (|| -> Result<(), CommandError> { | |||||
| let config_args = values_of_global_arg("config") | let config_args = values_of_global_arg("config") | ||||
| // `get_bytes_from_path` works for OsStr the same as for Path | // `get_bytes_from_path` works for OsStr the same as for Path | ||||
| .map(hg::utils::files::get_bytes_from_path); | .map(hg::utils::files::get_bytes_from_path); | ||||
| let config = hg::config::Config::load(config_args)?; | let config = hg::config::Config::load(config_args)?; | ||||
| run(&ui, &config, repo_path, args) | run(&ui, &config, repo_path, args) | ||||
| })(); | } | ||||
| fn main() { | |||||
| let ui = ui::Ui::new(); | |||||
| let exit_code = match result { | let exit_code = match main_with_result(&ui) { | ||||
| Ok(_) => exitcode::OK, | Ok(()) => exitcode::OK, | ||||
| // Exit with a specific code and no error message to let a potential | // Exit with a specific code and no error message to let a potential | ||||
| // wrapper script fallback to Python-based Mercurial. | // wrapper script fallback to Python-based Mercurial. | ||||
| Err(CommandError::Unimplemented) => exitcode::UNIMPLEMENTED, | Err(CommandError::Unimplemented) => exitcode::UNIMPLEMENTED, | ||||
| Err(CommandError::Abort { message }) => { | Err(CommandError::Abort { message }) => { | ||||
| if !message.is_empty() { | if !message.is_empty() { | ||||
| // Ignore errors when writing to stderr, we’re already exiting | // Ignore errors when writing to stderr, we’re already exiting | ||||
| /// Write bytes to stderr | /// Write bytes to stderr | ||||
| pub fn write_stderr(&self, bytes: &[u8]) -> Result<(), UiError> { | pub fn write_stderr(&self, bytes: &[u8]) -> Result<(), UiError> { | ||||
| let mut stderr = self.stderr.lock(); | let mut stderr = self.stderr.lock(); | ||||
| stderr.write_all(bytes).or_else(handle_stderr_error)?; | stderr.write_all(bytes).or_else(handle_stderr_error)?; | ||||
| stderr.flush().or_else(handle_stderr_error) | stderr.flush().or_else(handle_stderr_error) | ||||
| } | } | ||||
| /// Write string line to stderr | |||||
| pub fn writeln_stderr_str(&self, s: &str) -> Result<(), UiError> { | |||||
| self.write_stderr(&format!("{}\n", s).as_bytes()) | |||||
| } | |||||
| } | } | ||||
| /// A buffered stdout writer for faster batch printing operations. | /// A buffered stdout writer for faster batch printing operations. | ||||
| pub struct StdoutBuffer<W: Write> { | pub struct StdoutBuffer<W: Write> { | ||||
| buf: io::BufWriter<W>, | buf: io::BufWriter<W>, | ||||
| } | } | ||||
| impl<W: Write> StdoutBuffer<W> { | impl<W: Write> StdoutBuffer<W> { | ||||
| #require rust | #require rust | ||||
| Define an rhg function that will only run if rhg exists | Define an rhg function that will only run if rhg exists | ||||
| $ rhg() { | $ rhg() { | ||||
| > if [ -f "$RUNTESTDIR/../rust/target/release/rhg" ]; then | > if [ -f "$RUNTESTDIR/../rust/target/release/rhg" ]; then | ||||
| > "$RUNTESTDIR/../rust/target/release/rhg" "$@" | > "$RUNTESTDIR/../rust/target/release/rhg" "$@" | ||||
| > else | > else | ||||
| > echo "skipped: Cannot find rhg. Try to run cargo build in rust/rhg." | > echo "skipped: Cannot find rhg. Try to run cargo build in rust/rhg." | ||||
| > exit 80 | > exit 80 | ||||
| > fi | > fi | ||||
| > } | > } | ||||
| Unimplemented command | Unimplemented command | ||||
| $ rhg unimplemented-command | $ rhg unimplemented-command | ||||
| error: Found argument 'unimplemented-command' which wasn't expected, or isn't valid in this context | |||||
| USAGE: | |||||
| rhg [OPTIONS] <SUBCOMMAND> | |||||
| For more information try --help | |||||
| [252] | [252] | ||||
| Finding root | Finding root | ||||
| $ rhg root | $ rhg root | ||||
| abort: no repository found in '$TESTTMP' (.hg not found)! | abort: no repository found in '$TESTTMP' (.hg not found)! | ||||
| [255] | [255] | ||||
| $ hg init repository | $ hg init repository | ||||