rust/rhg/src/ui.rs
author Martin von Zweigbergk <martinvonz@google.com>
Wed, 02 Dec 2020 15:15:16 -0800
changeset 46024 d767f71b9158
parent 45999 fada33872b5b
child 46630 21d3b40b4c0e
permissions -rw-r--r--
tests: show that in-memory rebase leaves state when working copy is dirty When in-memory rebase falls back to on-disk rebase, it checks if the working copy is dirty. If it is, it aborts the rebase. However, it leaves the rebase state on disk. I broke it in feffeb18d412 (rebase: teach in-memory rebase to not restart with on-disk rebase on conflict, 2020-09-18). Differential Revision: https://phab.mercurial-scm.org/D9508
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
45999
fada33872b5b rhg: use `format_bytes!` for error messages
Raphaël Gomès <rgomes@octobus.net>
parents: 45534
diff changeset
     1
use format_bytes::format_bytes;
45534
66756b34c06e rhg: add a `DebugData` `Command` to prepare the `rhg debugdata` subcommand
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45451
diff changeset
     2
use std::borrow::Cow;
45050
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
     3
use std::io;
45382
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
     4
use std::io::{ErrorKind, Write};
45050
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
     5
45382
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
     6
#[derive(Debug)]
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
     7
pub struct Ui {
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
     8
    stdout: std::io::Stdout,
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
     9
    stderr: std::io::Stderr,
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    10
}
45050
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    11
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    12
/// The kind of user interface error
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    13
pub enum UiError {
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    14
    /// The standard output stream cannot be written to
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    15
    StdoutError(io::Error),
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    16
    /// The standard error stream cannot be written to
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    17
    StderrError(io::Error),
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    18
}
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    19
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    20
/// The commandline user interface
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    21
impl Ui {
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    22
    pub fn new() -> Self {
45382
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    23
        Ui {
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    24
            stdout: std::io::stdout(),
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    25
            stderr: std::io::stderr(),
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    26
        }
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    27
    }
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    28
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    29
    /// Returns a buffered handle on stdout for faster batch printing
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    30
    /// operations.
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    31
    pub fn stdout_buffer(&self) -> StdoutBuffer<std::io::StdoutLock> {
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    32
        StdoutBuffer::new(self.stdout.lock())
45050
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    33
    }
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    34
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    35
    /// Write bytes to stdout
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    36
    pub fn write_stdout(&self, bytes: &[u8]) -> Result<(), UiError> {
45382
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    37
        let mut stdout = self.stdout.lock();
45050
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    38
45450
fbc373b7cbc3 rhg: fix `clippy` warnings
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45387
diff changeset
    39
        stdout.write_all(bytes).or_else(handle_stdout_error)?;
45050
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    40
45450
fbc373b7cbc3 rhg: fix `clippy` warnings
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45387
diff changeset
    41
        stdout.flush().or_else(handle_stdout_error)
45050
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    42
    }
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    43
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    44
    /// Write bytes to stderr
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    45
    pub fn write_stderr(&self, bytes: &[u8]) -> Result<(), UiError> {
45382
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    46
        let mut stderr = self.stderr.lock();
45050
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    47
45450
fbc373b7cbc3 rhg: fix `clippy` warnings
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45387
diff changeset
    48
        stderr.write_all(bytes).or_else(handle_stderr_error)?;
45050
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    49
45450
fbc373b7cbc3 rhg: fix `clippy` warnings
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45387
diff changeset
    50
        stderr.flush().or_else(handle_stderr_error)
45050
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    51
    }
45451
a6a000ab135b rhg: print error message when argument parsing fails
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45450
diff changeset
    52
a6a000ab135b rhg: print error message when argument parsing fails
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45450
diff changeset
    53
    /// Write string line to stderr
a6a000ab135b rhg: print error message when argument parsing fails
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45450
diff changeset
    54
    pub fn writeln_stderr_str(&self, s: &str) -> Result<(), UiError> {
a6a000ab135b rhg: print error message when argument parsing fails
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45450
diff changeset
    55
        self.write_stderr(&format!("{}\n", s).as_bytes())
a6a000ab135b rhg: print error message when argument parsing fails
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45450
diff changeset
    56
    }
45050
513b3ef277a3 rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`
Antoine Cezar <antoine.cezar@octobus.net>
parents:
diff changeset
    57
}
45382
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    58
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    59
/// A buffered stdout writer for faster batch printing operations.
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    60
pub struct StdoutBuffer<W: Write> {
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    61
    buf: io::BufWriter<W>,
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    62
}
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    63
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    64
impl<W: Write> StdoutBuffer<W> {
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    65
    pub fn new(writer: W) -> Self {
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    66
        let buf = io::BufWriter::new(writer);
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    67
        Self { buf }
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    68
    }
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    69
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    70
    /// Write bytes to stdout buffer
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    71
    pub fn write_all(&mut self, bytes: &[u8]) -> Result<(), UiError> {
45450
fbc373b7cbc3 rhg: fix `clippy` warnings
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45387
diff changeset
    72
        self.buf.write_all(bytes).or_else(handle_stdout_error)
45382
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    73
    }
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    74
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    75
    /// Flush bytes to stdout
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    76
    pub fn flush(&mut self) -> Result<(), UiError> {
45450
fbc373b7cbc3 rhg: fix `clippy` warnings
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45387
diff changeset
    77
        self.buf.flush().or_else(handle_stdout_error)
45382
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    78
    }
eb55274d3650 rhg: add buffered stdout writing possibility
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45050
diff changeset
    79
}
45386
10c36ead86f8 rhg: extract function handle_stdout_error
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45382
diff changeset
    80
10c36ead86f8 rhg: extract function handle_stdout_error
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45382
diff changeset
    81
/// Sometimes writing to stdout is not possible, try writing to stderr to
10c36ead86f8 rhg: extract function handle_stdout_error
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45382
diff changeset
    82
/// signal that failure, otherwise just bail.
10c36ead86f8 rhg: extract function handle_stdout_error
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45382
diff changeset
    83
fn handle_stdout_error(error: io::Error) -> Result<(), UiError> {
10c36ead86f8 rhg: extract function handle_stdout_error
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45382
diff changeset
    84
    if let ErrorKind::BrokenPipe = error.kind() {
10c36ead86f8 rhg: extract function handle_stdout_error
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45382
diff changeset
    85
        // This makes `| head` work for example
10c36ead86f8 rhg: extract function handle_stdout_error
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45382
diff changeset
    86
        return Ok(());
10c36ead86f8 rhg: extract function handle_stdout_error
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45382
diff changeset
    87
    }
10c36ead86f8 rhg: extract function handle_stdout_error
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45382
diff changeset
    88
    let mut stderr = io::stderr();
10c36ead86f8 rhg: extract function handle_stdout_error
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45382
diff changeset
    89
10c36ead86f8 rhg: extract function handle_stdout_error
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45382
diff changeset
    90
    stderr
45999
fada33872b5b rhg: use `format_bytes!` for error messages
Raphaël Gomès <rgomes@octobus.net>
parents: 45534
diff changeset
    91
        .write_all(&format_bytes!(
fada33872b5b rhg: use `format_bytes!` for error messages
Raphaël Gomès <rgomes@octobus.net>
parents: 45534
diff changeset
    92
            b"abort: {}\n",
fada33872b5b rhg: use `format_bytes!` for error messages
Raphaël Gomès <rgomes@octobus.net>
parents: 45534
diff changeset
    93
            error.to_string().as_bytes()
fada33872b5b rhg: use `format_bytes!` for error messages
Raphaël Gomès <rgomes@octobus.net>
parents: 45534
diff changeset
    94
        ))
45450
fbc373b7cbc3 rhg: fix `clippy` warnings
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45387
diff changeset
    95
        .map_err(UiError::StderrError)?;
45386
10c36ead86f8 rhg: extract function handle_stdout_error
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45382
diff changeset
    96
45450
fbc373b7cbc3 rhg: fix `clippy` warnings
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45387
diff changeset
    97
    stderr.flush().map_err(UiError::StderrError)?;
45386
10c36ead86f8 rhg: extract function handle_stdout_error
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45382
diff changeset
    98
10c36ead86f8 rhg: extract function handle_stdout_error
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45382
diff changeset
    99
    Err(UiError::StdoutError(error))
10c36ead86f8 rhg: extract function handle_stdout_error
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45382
diff changeset
   100
}
45387
53af26aa5951 rhg: handle broken pipe error for stderr
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45386
diff changeset
   101
53af26aa5951 rhg: handle broken pipe error for stderr
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45386
diff changeset
   102
/// Sometimes writing to stderr is not possible.
53af26aa5951 rhg: handle broken pipe error for stderr
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45386
diff changeset
   103
fn handle_stderr_error(error: io::Error) -> Result<(), UiError> {
53af26aa5951 rhg: handle broken pipe error for stderr
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45386
diff changeset
   104
    // A broken pipe should not result in a error
53af26aa5951 rhg: handle broken pipe error for stderr
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45386
diff changeset
   105
    // like with `| head` for example
53af26aa5951 rhg: handle broken pipe error for stderr
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45386
diff changeset
   106
    if let ErrorKind::BrokenPipe = error.kind() {
53af26aa5951 rhg: handle broken pipe error for stderr
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45386
diff changeset
   107
        return Ok(());
53af26aa5951 rhg: handle broken pipe error for stderr
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45386
diff changeset
   108
    }
53af26aa5951 rhg: handle broken pipe error for stderr
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45386
diff changeset
   109
    Err(UiError::StdoutError(error))
53af26aa5951 rhg: handle broken pipe error for stderr
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45386
diff changeset
   110
}
45534
66756b34c06e rhg: add a `DebugData` `Command` to prepare the `rhg debugdata` subcommand
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45451
diff changeset
   111
66756b34c06e rhg: add a `DebugData` `Command` to prepare the `rhg debugdata` subcommand
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45451
diff changeset
   112
/// Encode rust strings according to the user system.
66756b34c06e rhg: add a `DebugData` `Command` to prepare the `rhg debugdata` subcommand
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45451
diff changeset
   113
pub fn utf8_to_local(s: &str) -> Cow<[u8]> {
66756b34c06e rhg: add a `DebugData` `Command` to prepare the `rhg debugdata` subcommand
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45451
diff changeset
   114
    // TODO encode for the user's system //
66756b34c06e rhg: add a `DebugData` `Command` to prepare the `rhg debugdata` subcommand
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45451
diff changeset
   115
    let bytes = s.as_bytes();
66756b34c06e rhg: add a `DebugData` `Command` to prepare the `rhg debugdata` subcommand
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45451
diff changeset
   116
    Cow::Borrowed(bytes)
66756b34c06e rhg: add a `DebugData` `Command` to prepare the `rhg debugdata` subcommand
Antoine Cezar <antoine.cezar@octobus.net>
parents: 45451
diff changeset
   117
}