##// END OF EJS Templates
rhg: print error message when argument parsing fails...
Antoine Cezar -
r46011:a6a000ab default
parent child Browse files
Show More
@@ -1,57 +1,58 b''
1 1 use clap::App;
2 2 use clap::AppSettings;
3 3 use clap::SubCommand;
4 4
5 5 mod commands;
6 6 mod error;
7 7 mod exitcode;
8 8 mod ui;
9 9 use commands::Command;
10 10
11 11 fn main() {
12 12 let mut app = App::new("rhg")
13 13 .setting(AppSettings::AllowInvalidUtf8)
14 14 .setting(AppSettings::SubcommandRequired)
15 15 .setting(AppSettings::VersionlessSubcommands)
16 16 .version("0.0.1")
17 17 .subcommand(
18 18 SubCommand::with_name("root").about(commands::root::HELP_TEXT),
19 19 )
20 20 .subcommand(
21 21 SubCommand::with_name("files").about(commands::files::HELP_TEXT),
22 22 );
23 23
24 let matches = app.clone().get_matches_safe().unwrap_or_else(|_| {
24 let matches = app.clone().get_matches_safe().unwrap_or_else(|err| {
25 let _ = ui::Ui::new().writeln_stderr_str(&err.message);
25 26 std::process::exit(exitcode::UNIMPLEMENTED_COMMAND)
26 27 });
27 28
28 29 let ui = ui::Ui::new();
29 30
30 31 let command_result = match matches.subcommand_name() {
31 32 Some(name) => match name {
32 33 "root" => commands::root::RootCommand::new().run(&ui),
33 34 "files" => commands::files::FilesCommand::new().run(&ui),
34 35 _ => std::process::exit(exitcode::UNIMPLEMENTED_COMMAND),
35 36 },
36 37 _ => {
37 38 match app.print_help() {
38 39 Ok(_) => std::process::exit(exitcode::OK),
39 40 Err(_) => std::process::exit(exitcode::ABORT),
40 41 };
41 42 }
42 43 };
43 44
44 45 match command_result {
45 46 Ok(_) => std::process::exit(exitcode::OK),
46 47 Err(e) => {
47 48 let message = e.get_error_message_bytes();
48 49 if let Some(msg) = message {
49 50 match ui.write_stderr(&msg) {
50 51 Ok(_) => (),
51 52 Err(_) => std::process::exit(exitcode::ABORT),
52 53 };
53 54 };
54 55 e.exit()
55 56 }
56 57 }
57 58 }
@@ -1,100 +1,105 b''
1 1 use std::io;
2 2 use std::io::{ErrorKind, Write};
3 3
4 4 #[derive(Debug)]
5 5 pub struct Ui {
6 6 stdout: std::io::Stdout,
7 7 stderr: std::io::Stderr,
8 8 }
9 9
10 10 /// The kind of user interface error
11 11 pub enum UiError {
12 12 /// The standard output stream cannot be written to
13 13 StdoutError(io::Error),
14 14 /// The standard error stream cannot be written to
15 15 StderrError(io::Error),
16 16 }
17 17
18 18 /// The commandline user interface
19 19 impl Ui {
20 20 pub fn new() -> Self {
21 21 Ui {
22 22 stdout: std::io::stdout(),
23 23 stderr: std::io::stderr(),
24 24 }
25 25 }
26 26
27 27 /// Returns a buffered handle on stdout for faster batch printing
28 28 /// operations.
29 29 pub fn stdout_buffer(&self) -> StdoutBuffer<std::io::StdoutLock> {
30 30 StdoutBuffer::new(self.stdout.lock())
31 31 }
32 32
33 33 /// Write bytes to stdout
34 34 pub fn write_stdout(&self, bytes: &[u8]) -> Result<(), UiError> {
35 35 let mut stdout = self.stdout.lock();
36 36
37 37 stdout.write_all(bytes).or_else(handle_stdout_error)?;
38 38
39 39 stdout.flush().or_else(handle_stdout_error)
40 40 }
41 41
42 42 /// Write bytes to stderr
43 43 pub fn write_stderr(&self, bytes: &[u8]) -> Result<(), UiError> {
44 44 let mut stderr = self.stderr.lock();
45 45
46 46 stderr.write_all(bytes).or_else(handle_stderr_error)?;
47 47
48 48 stderr.flush().or_else(handle_stderr_error)
49 49 }
50
51 /// Write string line to stderr
52 pub fn writeln_stderr_str(&self, s: &str) -> Result<(), UiError> {
53 self.write_stderr(&format!("{}\n", s).as_bytes())
54 }
50 55 }
51 56
52 57 /// A buffered stdout writer for faster batch printing operations.
53 58 pub struct StdoutBuffer<W: Write> {
54 59 buf: io::BufWriter<W>,
55 60 }
56 61
57 62 impl<W: Write> StdoutBuffer<W> {
58 63 pub fn new(writer: W) -> Self {
59 64 let buf = io::BufWriter::new(writer);
60 65 Self { buf }
61 66 }
62 67
63 68 /// Write bytes to stdout buffer
64 69 pub fn write_all(&mut self, bytes: &[u8]) -> Result<(), UiError> {
65 70 self.buf.write_all(bytes).or_else(handle_stdout_error)
66 71 }
67 72
68 73 /// Flush bytes to stdout
69 74 pub fn flush(&mut self) -> Result<(), UiError> {
70 75 self.buf.flush().or_else(handle_stdout_error)
71 76 }
72 77 }
73 78
74 79 /// Sometimes writing to stdout is not possible, try writing to stderr to
75 80 /// signal that failure, otherwise just bail.
76 81 fn handle_stdout_error(error: io::Error) -> Result<(), UiError> {
77 82 if let ErrorKind::BrokenPipe = error.kind() {
78 83 // This makes `| head` work for example
79 84 return Ok(());
80 85 }
81 86 let mut stderr = io::stderr();
82 87
83 88 stderr
84 89 .write_all(&[b"abort: ", error.to_string().as_bytes(), b"\n"].concat())
85 90 .map_err(UiError::StderrError)?;
86 91
87 92 stderr.flush().map_err(UiError::StderrError)?;
88 93
89 94 Err(UiError::StdoutError(error))
90 95 }
91 96
92 97 /// Sometimes writing to stderr is not possible.
93 98 fn handle_stderr_error(error: io::Error) -> Result<(), UiError> {
94 99 // A broken pipe should not result in a error
95 100 // like with `| head` for example
96 101 if let ErrorKind::BrokenPipe = error.kind() {
97 102 return Ok(());
98 103 }
99 104 Err(UiError::StdoutError(error))
100 105 }
@@ -1,64 +1,70 b''
1 1 #require rust
2 2
3 3 Define an rhg function that will only run if rhg exists
4 4 $ rhg() {
5 5 > if [ -f "$RUNTESTDIR/../rust/target/debug/rhg" ]; then
6 6 > "$RUNTESTDIR/../rust/target/debug/rhg" "$@"
7 7 > else
8 8 > echo "skipped: Cannot find rhg. Try to run cargo build in rust/rhg."
9 9 > exit 80
10 10 > fi
11 11 > }
12 12
13 13 Unimplemented command
14 14 $ rhg unimplemented-command
15 error: Found argument 'unimplemented-command' which wasn't expected, or isn't valid in this context
16
17 USAGE:
18 rhg <SUBCOMMAND>
19
20 For more information try --help
15 21 [252]
16 22
17 23 Finding root
18 24 $ rhg root
19 25 abort: no repository found in '$TESTTMP' (.hg not found)!
20 26 [255]
21 27
22 28 $ hg init repository
23 29 $ cd repository
24 30 $ rhg root
25 31 $TESTTMP/repository
26 32
27 33 Unwritable file descriptor
28 34 $ rhg root > /dev/full
29 35 abort: No space left on device (os error 28)
30 36 [255]
31 37
32 38 Deleted repository
33 39 $ rm -rf `pwd`
34 40 $ rhg root
35 41 abort: error getting current working directory: $ENOENT$
36 42 [255]
37 43
38 44 Listing tracked files
39 45 $ cd $TESTTMP
40 46 $ hg init repository
41 47 $ cd repository
42 48 $ for i in 1 2 3; do
43 49 > echo $i >> file$i
44 50 > hg add file$i
45 51 > done
46 52 > hg commit -m "commit $i" -q
47 53
48 54 Listing tracked files from root
49 55 $ rhg files
50 56 file1
51 57 file2
52 58 file3
53 59
54 60 Listing tracked files from subdirectory
55 61 $ mkdir -p path/to/directory
56 62 $ cd path/to/directory
57 63 $ rhg files
58 64 ../../../file1
59 65 ../../../file2
60 66 ../../../file3
61 67
62 68 Listing tracked files through broken pipe
63 69 $ rhg files | head -n 1
64 70 ../../../file1
General Comments 0
You need to be logged in to leave comments. Login now