bevy_ratatui 0.3.0

A Bevy plugin for building terminal user interfaces with Ratatui
Documentation
use bevy::prelude::*;
use crossterm::{
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
    ExecutableCommand,
};
use ratatui::backend::CrosstermBackend;
use std::io::{self, stdout, Stdout};

use crate::error::exit_on_error;

pub struct TerminalPlugin;

impl Plugin for TerminalPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(Startup, setup_terminal.pipe(exit_on_error));
    }
}

pub fn setup_terminal(mut commands: Commands) -> color_eyre::Result<()> {
    let terminal = RatatuiContext::init()?;
    commands.insert_resource(terminal);
    Ok(())
}

/// A wrapper around ratatui::Terminal that automatically enters and leaves the alternate screen.
#[derive(Resource, Deref, DerefMut)]
pub struct RatatuiContext(ratatui::Terminal<CrosstermBackend<Stdout>>);

impl RatatuiContext {
    pub fn init() -> io::Result<Self> {
        stdout().execute(EnterAlternateScreen)?;
        enable_raw_mode()?;
        let backend = CrosstermBackend::new(stdout());
        let terminal = ratatui::Terminal::new(backend)?;
        Ok(RatatuiContext(terminal))
    }

    pub fn restore() -> io::Result<()> {
        stdout().execute(LeaveAlternateScreen)?;
        disable_raw_mode()?;
        Ok(())
    }
}

impl Drop for RatatuiContext {
    fn drop(&mut self) {
        RatatuiContext::restore().unwrap();
    }
}