ui.rs
132 lines
| 3.8 KiB
| application/rls-services+xml
|
RustLexer
Raphaël Gomès
|
r46598 | use format_bytes::format_bytes; | ||
Antoine Cezar
|
r46099 | use std::borrow::Cow; | ||
Pulkit Goyal
|
r48990 | use std::env; | ||
Antoine Cezar
|
r45592 | use std::io; | ||
Antoine Cezar
|
r45921 | use std::io::{ErrorKind, Write}; | ||
Antoine Cezar
|
r45592 | |||
Antoine Cezar
|
r45921 | #[derive(Debug)] | ||
pub struct Ui { | ||||
stdout: std::io::Stdout, | ||||
stderr: std::io::Stderr, | ||||
} | ||||
Antoine Cezar
|
r45592 | |||
/// The kind of user interface error | ||||
pub enum UiError { | ||||
/// The standard output stream cannot be written to | ||||
StdoutError(io::Error), | ||||
/// The standard error stream cannot be written to | ||||
StderrError(io::Error), | ||||
} | ||||
/// The commandline user interface | ||||
impl Ui { | ||||
pub fn new() -> Self { | ||||
Antoine Cezar
|
r45921 | Ui { | ||
stdout: std::io::stdout(), | ||||
stderr: std::io::stderr(), | ||||
} | ||||
} | ||||
/// Returns a buffered handle on stdout for faster batch printing | ||||
/// operations. | ||||
pub fn stdout_buffer(&self) -> StdoutBuffer<std::io::StdoutLock> { | ||||
StdoutBuffer::new(self.stdout.lock()) | ||||
Antoine Cezar
|
r45592 | } | ||
/// Write bytes to stdout | ||||
pub fn write_stdout(&self, bytes: &[u8]) -> Result<(), UiError> { | ||||
Antoine Cezar
|
r45921 | let mut stdout = self.stdout.lock(); | ||
Antoine Cezar
|
r45592 | |||
Antoine Cezar
|
r46010 | stdout.write_all(bytes).or_else(handle_stdout_error)?; | ||
Antoine Cezar
|
r45592 | |||
Antoine Cezar
|
r46010 | stdout.flush().or_else(handle_stdout_error) | ||
Antoine Cezar
|
r45592 | } | ||
/// Write bytes to stderr | ||||
pub fn write_stderr(&self, bytes: &[u8]) -> Result<(), UiError> { | ||||
Antoine Cezar
|
r45921 | let mut stderr = self.stderr.lock(); | ||
Antoine Cezar
|
r45592 | |||
Antoine Cezar
|
r46010 | stderr.write_all(bytes).or_else(handle_stderr_error)?; | ||
Antoine Cezar
|
r45592 | |||
Antoine Cezar
|
r46010 | stderr.flush().or_else(handle_stderr_error) | ||
Antoine Cezar
|
r45592 | } | ||
Pulkit Goyal
|
r48990 | |||
Simon Sapin
|
r49344 | /// Return whether plain mode is active. | ||
Pulkit Goyal
|
r48990 | /// | ||
/// Plain mode means that all configuration variables which affect | ||||
/// the behavior and output of Mercurial should be | ||||
/// ignored. Additionally, the output should be stable, | ||||
/// reproducible and suitable for use in scripts or applications. | ||||
/// | ||||
/// The only way to trigger plain mode is by setting either the | ||||
/// `HGPLAIN' or `HGPLAINEXCEPT' environment variables. | ||||
/// | ||||
/// The return value can either be | ||||
/// - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT | ||||
/// - False if feature is disabled by default and not included in HGPLAIN | ||||
/// - True otherwise | ||||
pub fn plain(&self) -> bool { | ||||
// TODO: add support for HGPLAINEXCEPT | ||||
env::var_os("HGPLAIN").is_some() | ||||
} | ||||
Antoine Cezar
|
r45592 | } | ||
Antoine Cezar
|
r45921 | |||
/// A buffered stdout writer for faster batch printing operations. | ||||
pub struct StdoutBuffer<W: Write> { | ||||
buf: io::BufWriter<W>, | ||||
} | ||||
impl<W: Write> StdoutBuffer<W> { | ||||
pub fn new(writer: W) -> Self { | ||||
let buf = io::BufWriter::new(writer); | ||||
Self { buf } | ||||
} | ||||
/// Write bytes to stdout buffer | ||||
pub fn write_all(&mut self, bytes: &[u8]) -> Result<(), UiError> { | ||||
Antoine Cezar
|
r46010 | self.buf.write_all(bytes).or_else(handle_stdout_error) | ||
Antoine Cezar
|
r45921 | } | ||
/// Flush bytes to stdout | ||||
pub fn flush(&mut self) -> Result<(), UiError> { | ||||
Antoine Cezar
|
r46010 | self.buf.flush().or_else(handle_stdout_error) | ||
Antoine Cezar
|
r45921 | } | ||
} | ||||
Antoine Cezar
|
r45925 | |||
/// Sometimes writing to stdout is not possible, try writing to stderr to | ||||
/// signal that failure, otherwise just bail. | ||||
fn handle_stdout_error(error: io::Error) -> Result<(), UiError> { | ||||
if let ErrorKind::BrokenPipe = error.kind() { | ||||
// This makes `| head` work for example | ||||
return Ok(()); | ||||
} | ||||
let mut stderr = io::stderr(); | ||||
stderr | ||||
Raphaël Gomès
|
r46598 | .write_all(&format_bytes!( | ||
b"abort: {}\n", | ||||
error.to_string().as_bytes() | ||||
)) | ||||
Antoine Cezar
|
r46010 | .map_err(UiError::StderrError)?; | ||
Antoine Cezar
|
r45925 | |||
Antoine Cezar
|
r46010 | stderr.flush().map_err(UiError::StderrError)?; | ||
Antoine Cezar
|
r45925 | |||
Err(UiError::StdoutError(error)) | ||||
} | ||||
Antoine Cezar
|
r45926 | |||
/// Sometimes writing to stderr is not possible. | ||||
fn handle_stderr_error(error: io::Error) -> Result<(), UiError> { | ||||
// A broken pipe should not result in a error | ||||
// like with `| head` for example | ||||
if let ErrorKind::BrokenPipe = error.kind() { | ||||
return Ok(()); | ||||
} | ||||
Err(UiError::StdoutError(error)) | ||||
} | ||||
Antoine Cezar
|
r46099 | |||
/// Encode rust strings according to the user system. | ||||
pub fn utf8_to_local(s: &str) -> Cow<[u8]> { | ||||
// TODO encode for the user's system // | ||||
let bytes = s.as_bytes(); | ||||
Cow::Borrowed(bytes) | ||||
} | ||||