use ratatui::crossterm::{
event::{DisableMouseCapture, EnableMouseCapture},
execute,
terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
};
use ratatui::{Terminal, backend::CrosstermBackend};
use std::io::{self, Stdout};
pub struct ManagedTerminal {
terminal: Terminal<CrosstermBackend<Stdout>>,
}
impl ManagedTerminal {
pub fn new() -> io::Result<Self> {
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let terminal = Terminal::new(backend)?;
Ok(Self { terminal })
}
pub fn terminal_mut(&mut self) -> &mut Terminal<CrosstermBackend<Stdout>> {
&mut self.terminal
}
pub fn size(&self) -> io::Result<ratatui::layout::Rect> {
self.terminal
.size()
.map(|size| ratatui::layout::Rect::new(0, 0, size.width, size.height))
}
pub fn clear(&mut self) -> io::Result<()> {
self.terminal.clear()
}
pub fn draw<F>(&mut self, f: F) -> io::Result<()>
where
F: FnOnce(&mut ratatui::Frame),
{
self.terminal.draw(f)?;
Ok(())
}
}
impl Drop for ManagedTerminal {
fn drop(&mut self) {
let _ = disable_raw_mode();
let _ = execute!(
self.terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
);
let _ = self.terminal.show_cursor();
}
}
pub fn setup_terminal() -> io::Result<ManagedTerminal> {
ManagedTerminal::new()
}
pub fn restore_terminal() -> io::Result<()> {
disable_raw_mode()?;
execute!(std::io::stdout(), LeaveAlternateScreen, DisableMouseCapture)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
#[test]
fn test_managed_terminal_creation_and_cleanup() {
}
#[test]
fn test_size_conversion() {
let width = 80u16;
let height = 24u16;
let rect = ratatui::layout::Rect::new(0, 0, width, height);
assert_eq!(rect.x, 0);
assert_eq!(rect.y, 0);
assert_eq!(rect.width, width);
assert_eq!(rect.height, height);
}
#[test]
fn test_setup_terminal_signature() {
let _setup_fn: fn() -> io::Result<ManagedTerminal> = setup_terminal;
}
#[test]
fn test_restore_terminal_signature() {
let _restore_fn: fn() -> io::Result<()> = restore_terminal;
}
#[test]
fn test_managed_terminal_methods() {
fn _test_methods(mut terminal: ManagedTerminal) -> io::Result<()> {
let _size = terminal.size()?;
terminal.clear()?;
terminal.draw(|_frame| {})?;
let _term_ref = terminal.terminal_mut();
Ok(())
}
}
#[test]
fn test_error_handling() {
let _io_error: io::Error = io::Error::other("test");
fn _test_error_types() {
let _: io::Result<ManagedTerminal> = setup_terminal();
let _: io::Result<()> = restore_terminal();
}
}
#[test]
fn test_thread_safety() {
let counter = Arc::new(Mutex::new(0));
let counter_clone = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter_clone.lock().unwrap();
*num += 1;
let _setup_fn = setup_terminal;
let _restore_fn = restore_terminal;
});
handle.join().unwrap();
assert_eq!(*counter.lock().unwrap(), 1);
}
#[test]
fn test_drop_implementation() {
use std::mem;
assert!(mem::needs_drop::<ManagedTerminal>());
}
#[test]
fn test_performance_characteristics() {
let start = std::time::Instant::now();
for _ in 0..1000 {
let _rect = ratatui::layout::Rect::new(0, 0, 80, 24);
}
let duration = start.elapsed();
assert!(duration < Duration::from_millis(10));
}
#[test]
fn test_memory_usage() {
use std::mem;
let terminal_size = mem::size_of::<ManagedTerminal>();
assert!(terminal_size < 1024);
let rect_size = mem::size_of::<ratatui::layout::Rect>();
assert!(rect_size <= 8); }
}