Mercurial > hg
view rust/rhg/src/ui.rs @ 48409:005ae1a343f8
rhg: add support for narrow clones and sparse checkouts
This adds a minimal support that can be implemented without parsing the narrowspec.
We can parse the narrowspec and add support for more operations later.
The reason we need so few code changes is as follows:
Most operations need no special treatment of sparse because
some of them only read dirstate (`rhg files` without `-r`),
which bakes in the filtering,
some of them only read store (`rhg files -r`, `rhg cat`),
and some of them read no data at all (`rhg root`, `rhg debugrequirements`).
`status` is the command that might care about sparse, so we just disable
rhg on it.
For narrow clones, `rhg files` clearly needs the narrowspec to work
correctly, so we fall back.
`rhg cat` seems to work consistently with `hg cat` if the file exists.
If the file is hidden by narrow spec, the error message is different and confusing, so
that's something that we should improve in follow-up patches.
Differential Revision: https://phab.mercurial-scm.org/D11764
author | Arseniy Alekseyev <aalekseyev@janestreet.com> |
---|---|
date | Tue, 16 Nov 2021 11:53:58 +0000 |
parents | 38deb65d4441 |
children | 47f2a82ae3e4 |
line wrap: on
line source
use format_bytes::format_bytes; use std::borrow::Cow; use std::env; use std::io; use std::io::{ErrorKind, Write}; #[derive(Debug)] pub struct Ui { stdout: std::io::Stdout, stderr: std::io::Stderr, } /// 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 { 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()) } /// Write bytes to stdout pub fn write_stdout(&self, bytes: &[u8]) -> Result<(), UiError> { let mut stdout = self.stdout.lock(); stdout.write_all(bytes).or_else(handle_stdout_error)?; stdout.flush().or_else(handle_stdout_error) } /// Write bytes to stderr pub fn write_stderr(&self, bytes: &[u8]) -> Result<(), UiError> { let mut stderr = self.stderr.lock(); stderr.write_all(bytes).or_else(handle_stderr_error)?; stderr.flush().or_else(handle_stderr_error) } /// is plain mode active /// /// 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() } } /// 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> { self.buf.write_all(bytes).or_else(handle_stdout_error) } /// Flush bytes to stdout pub fn flush(&mut self) -> Result<(), UiError> { self.buf.flush().or_else(handle_stdout_error) } } /// 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 .write_all(&format_bytes!( b"abort: {}\n", error.to_string().as_bytes() )) .map_err(UiError::StderrError)?; stderr.flush().map_err(UiError::StderrError)?; Err(UiError::StdoutError(error)) } /// 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)) } /// 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) }