##// END OF EJS Templates
rhg: handle broken pipe error for stderr...
Antoine Cezar -
r45926:53af26aa default
parent child Browse files
Show More
@@ -1,96 +1,106 b''
1 use std::io;
1 use std::io;
2 use std::io::{ErrorKind, Write};
2 use std::io::{ErrorKind, Write};
3
3
4 #[derive(Debug)]
4 #[derive(Debug)]
5 pub struct Ui {
5 pub struct Ui {
6 stdout: std::io::Stdout,
6 stdout: std::io::Stdout,
7 stderr: std::io::Stderr,
7 stderr: std::io::Stderr,
8 }
8 }
9
9
10 /// The kind of user interface error
10 /// The kind of user interface error
11 pub enum UiError {
11 pub enum UiError {
12 /// The standard output stream cannot be written to
12 /// The standard output stream cannot be written to
13 StdoutError(io::Error),
13 StdoutError(io::Error),
14 /// The standard error stream cannot be written to
14 /// The standard error stream cannot be written to
15 StderrError(io::Error),
15 StderrError(io::Error),
16 }
16 }
17
17
18 /// The commandline user interface
18 /// The commandline user interface
19 impl Ui {
19 impl Ui {
20 pub fn new() -> Self {
20 pub fn new() -> Self {
21 Ui {
21 Ui {
22 stdout: std::io::stdout(),
22 stdout: std::io::stdout(),
23 stderr: std::io::stderr(),
23 stderr: std::io::stderr(),
24 }
24 }
25 }
25 }
26
26
27 /// Returns a buffered handle on stdout for faster batch printing
27 /// Returns a buffered handle on stdout for faster batch printing
28 /// operations.
28 /// operations.
29 pub fn stdout_buffer(&self) -> StdoutBuffer<std::io::StdoutLock> {
29 pub fn stdout_buffer(&self) -> StdoutBuffer<std::io::StdoutLock> {
30 StdoutBuffer::new(self.stdout.lock())
30 StdoutBuffer::new(self.stdout.lock())
31 }
31 }
32
32
33 /// Write bytes to stdout
33 /// Write bytes to stdout
34 pub fn write_stdout(&self, bytes: &[u8]) -> Result<(), UiError> {
34 pub fn write_stdout(&self, bytes: &[u8]) -> Result<(), UiError> {
35 let mut stdout = self.stdout.lock();
35 let mut stdout = self.stdout.lock();
36
36
37 stdout
37 stdout
38 .write_all(bytes)
38 .write_all(bytes)
39 .or_else(|e| handle_stdout_error(e))?;
39 .or_else(|e| handle_stdout_error(e))?;
40
40
41 stdout.flush().or_else(|e| handle_stdout_error(e))
41 stdout.flush().or_else(|e| handle_stdout_error(e))
42 }
42 }
43
43
44 /// Write bytes to stderr
44 /// Write bytes to stderr
45 pub fn write_stderr(&self, bytes: &[u8]) -> Result<(), UiError> {
45 pub fn write_stderr(&self, bytes: &[u8]) -> Result<(), UiError> {
46 let mut stderr = self.stderr.lock();
46 let mut stderr = self.stderr.lock();
47
47
48 stderr
48 stderr
49 .write_all(bytes)
49 .write_all(bytes)
50 .or_else(|e| handle_stderr_error(e))?;
50 .or_else(|e| handle_stderr_error(e))?;
51
51
52 stderr.flush().or_else(|e| Err(UiError::StderrError(e)))
52 stderr.flush().or_else(|e| handle_stderr_error(e))
53 }
53 }
54 }
54 }
55
55
56 /// A buffered stdout writer for faster batch printing operations.
56 /// A buffered stdout writer for faster batch printing operations.
57 pub struct StdoutBuffer<W: Write> {
57 pub struct StdoutBuffer<W: Write> {
58 buf: io::BufWriter<W>,
58 buf: io::BufWriter<W>,
59 }
59 }
60
60
61 impl<W: Write> StdoutBuffer<W> {
61 impl<W: Write> StdoutBuffer<W> {
62 pub fn new(writer: W) -> Self {
62 pub fn new(writer: W) -> Self {
63 let buf = io::BufWriter::new(writer);
63 let buf = io::BufWriter::new(writer);
64 Self { buf }
64 Self { buf }
65 }
65 }
66
66
67 /// Write bytes to stdout buffer
67 /// Write bytes to stdout buffer
68 pub fn write_all(&mut self, bytes: &[u8]) -> Result<(), UiError> {
68 pub fn write_all(&mut self, bytes: &[u8]) -> Result<(), UiError> {
69 self.buf
69 self.buf
70 .write_all(bytes)
70 .write_all(bytes)
71 .or_else(|e| handle_stdout_error(e))
71 .or_else(|e| handle_stdout_error(e))
72 }
72 }
73
73
74 /// Flush bytes to stdout
74 /// Flush bytes to stdout
75 pub fn flush(&mut self) -> Result<(), UiError> {
75 pub fn flush(&mut self) -> Result<(), UiError> {
76 self.buf.flush().or_else(|e| handle_stdout_error(e))
76 self.buf.flush().or_else(|e| handle_stdout_error(e))
77 }
77 }
78 }
78 }
79
79
80 /// Sometimes writing to stdout is not possible, try writing to stderr to
80 /// Sometimes writing to stdout is not possible, try writing to stderr to
81 /// signal that failure, otherwise just bail.
81 /// signal that failure, otherwise just bail.
82 fn handle_stdout_error(error: io::Error) -> Result<(), UiError> {
82 fn handle_stdout_error(error: io::Error) -> Result<(), UiError> {
83 if let ErrorKind::BrokenPipe = error.kind() {
83 if let ErrorKind::BrokenPipe = error.kind() {
84 // This makes `| head` work for example
84 // This makes `| head` work for example
85 return Ok(());
85 return Ok(());
86 }
86 }
87 let mut stderr = io::stderr();
87 let mut stderr = io::stderr();
88
88
89 stderr
89 stderr
90 .write_all(&[b"abort: ", error.to_string().as_bytes(), b"\n"].concat())
90 .write_all(&[b"abort: ", error.to_string().as_bytes(), b"\n"].concat())
91 .map_err(|e| UiError::StderrError(e))?;
91 .map_err(|e| UiError::StderrError(e))?;
92
92
93 stderr.flush().map_err(|e| UiError::StderrError(e))?;
93 stderr.flush().map_err(|e| UiError::StderrError(e))?;
94
94
95 Err(UiError::StdoutError(error))
95 Err(UiError::StdoutError(error))
96 }
96 }
97
98 /// Sometimes writing to stderr is not possible.
99 fn handle_stderr_error(error: io::Error) -> Result<(), UiError> {
100 // A broken pipe should not result in a error
101 // like with `| head` for example
102 if let ErrorKind::BrokenPipe = error.kind() {
103 return Ok(());
104 }
105 Err(UiError::StdoutError(error))
106 }
General Comments 0
You need to be logged in to leave comments. Login now