Skip to main content

muster/adapter/tui/
terminal.rs

1use std::io::{self, Stdout};
2
3use crossterm::{
4    execute,
5    terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
6};
7use getset::MutGetters;
8use ratatui::{Terminal, backend::CrosstermBackend};
9
10use crate::error::Result;
11
12/// The concrete ratatui terminal type: a crossterm backend on stdout.
13pub type Tui = Terminal<CrosstermBackend<Stdout>>;
14
15/// RAII guard that enters raw mode + the alternate screen on construction and
16/// restores the original terminal state on drop.
17#[derive(MutGetters)]
18pub struct TerminalGuard {
19    #[getset(get_mut = "pub")]
20    terminal: Tui,
21}
22
23impl TerminalGuard {
24    /// Enters raw mode and the alternate screen, returning a ready terminal.
25    ///
26    /// # Errors
27    /// Returns an error if raw mode cannot be enabled, the alternate screen
28    /// cannot be entered, or the terminal backend fails to initialize.
29    pub fn new() -> Result<Self> {
30        enable_raw_mode()?;
31        let mut stdout = io::stdout();
32        if let Err(error) = execute!(stdout, EnterAlternateScreen) {
33            let _ = disable_raw_mode();
34            return Err(error.into());
35        }
36        match Terminal::new(CrosstermBackend::new(stdout)) {
37            Ok(terminal) => Ok(Self { terminal }),
38            Err(error) => {
39                let _ = Self::restore();
40                Err(error.into())
41            },
42        }
43    }
44
45    /// Restores the terminal to its original cooked state. Safe to call more
46    /// than once; used by both `Drop` and the panic hook.
47    ///
48    /// # Errors
49    /// Returns an error if raw mode cannot be disabled or the alternate screen
50    /// cannot be left.
51    pub fn restore() -> io::Result<()> {
52        let raw = disable_raw_mode();
53        let screen = execute!(io::stdout(), LeaveAlternateScreen);
54        raw.and(screen)
55    }
56}
57
58impl Drop for TerminalGuard {
59    fn drop(&mut self) {
60        let _ = Self::restore();
61    }
62}