traceviewer 0.1.3

Run a command and inspect its logs in a scrollable terminal viewer
use std::{cell::RefCell, io};

use anyhow::Result;
use crossterm::{
    cursor::{Hide, Show},
    execute,
    terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
};

pub(crate) struct TerminalGuard {
    stdout: RefCell<Option<io::Stdout>>,
}

impl TerminalGuard {
    pub(crate) fn enter() -> Result<Self> {
        enable_raw_mode()?;
        let mut stdout = io::stdout();
        execute!(stdout, EnterAlternateScreen, Hide)?;
        Ok(Self {
            stdout: RefCell::new(Some(stdout)),
        })
    }

    pub(crate) fn stdout(&self) -> impl std::ops::DerefMut<Target = io::Stdout> + '_ {
        std::cell::RefMut::map(self.stdout.borrow_mut(), |stdout| {
            stdout.as_mut().expect("terminal already left")
        })
    }

    pub(crate) fn leave(self) -> Result<()> {
        if let Some(mut stdout) = self.stdout.borrow_mut().take() {
            execute!(stdout, Show, LeaveAlternateScreen)?;
        }
        disable_raw_mode()?;
        Ok(())
    }
}

impl Drop for TerminalGuard {
    fn drop(&mut self) {
        if let Some(stdout) = self.stdout.get_mut() {
            let _ = execute!(stdout, Show, LeaveAlternateScreen);
        }
        let _ = disable_raw_mode();
    }
}