git_iblame/extensions/
terminal_raw_mode_scope.rs

1use std::io;
2
3use crossterm::terminal;
4use log::*;
5
6/// Enable or disable the
7/// [terminal raw mode](https://docs.rs/crossterm/latest/crossterm/terminal/index.html#raw-mode)
8/// while its instance is in scope.
9///
10/// While it automatically resets the mode when it's out of scope,
11/// `reset()` should be called explicitly when the mode should be reset
12/// or at the end of the scope
13/// to avoid it being dropped earlier by the compiler.
14/// # Examples
15/// ```no_run
16/// # fn main() -> std::io::Result<()> {
17/// use git_iblame::TerminalRawModeScope;
18///
19/// let mut terminal_raw_mode = TerminalRawModeScope::new(true)?;
20/// // Do the work.
21/// // If it returns early, the terminal raw mode will be reset automatically.
22/// terminal_raw_mode.reset()?
23/// ;
24/// # Ok(())
25/// # }
26/// ```
27pub struct TerminalRawModeScope {
28    is_enabled: bool,
29    is_reset: bool,
30}
31
32impl TerminalRawModeScope {
33    /// Enable the raw mode if `enable` is true,
34    /// or disable it if `enable` is false.
35    pub fn new(enable: bool) -> io::Result<Self> {
36        Self::enable(enable)?;
37        Ok(Self {
38            is_enabled: enable,
39            is_reset: false,
40        })
41    }
42
43    /// Reset the terminal raw mode.
44    /// This should be called when the mode should be reset,
45    /// or at the end of the scope.
46    ///
47    /// Even if the mode should be reset at the end of the scope,
48    /// and that the `Drop` trait should reset the raw mode,
49    /// it should be called to avoid it being dropped earlier by the compiler.
50    pub fn reset(&mut self) -> io::Result<()> {
51        if !self.is_reset {
52            Self::enable(!self.is_enabled)?;
53            self.is_reset = true;
54        }
55        Ok(())
56    }
57
58    fn enable(enable: bool) -> io::Result<()> {
59        debug!("TerminalRawModeScope.enable({enable})");
60        if enable {
61            terminal::enable_raw_mode()
62        } else {
63            terminal::disable_raw_mode()
64        }
65    }
66}
67
68impl Drop for TerminalRawModeScope {
69    /// Calls `reset()` if it's not already reset.
70    /// This is called when the instance goes out of scope.
71    fn drop(&mut self) {
72        if let Err(error) = self.reset() {
73            warn!("Failed to change terminal raw mode, ignored: {error}");
74        }
75    }
76}