reratui_runtime/
managed_terminal.rs1use ratatui::crossterm::{
7 event::{DisableMouseCapture, EnableMouseCapture},
8 execute,
9 terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
10};
11use ratatui::{Terminal, backend::CrosstermBackend};
12use std::io::{self, Stdout};
13
14pub struct ManagedTerminal {
16 terminal: Terminal<CrosstermBackend<Stdout>>,
17}
18
19impl ManagedTerminal {
20 pub fn new() -> io::Result<Self> {
22 enable_raw_mode()?;
24
25 let mut stdout = io::stdout();
27
28 execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
30
31 let backend = CrosstermBackend::new(stdout);
33 let terminal = Terminal::new(backend)?;
34
35 Ok(Self { terminal })
36 }
37
38 pub fn terminal_mut(&mut self) -> &mut Terminal<CrosstermBackend<Stdout>> {
40 &mut self.terminal
41 }
42
43 pub fn size(&self) -> io::Result<ratatui::layout::Rect> {
45 self.terminal
46 .size()
47 .map(|size| ratatui::layout::Rect::new(0, 0, size.width, size.height))
48 }
49
50 pub fn clear(&mut self) -> io::Result<()> {
52 self.terminal.clear()
53 }
54
55 pub fn draw<F>(&mut self, f: F) -> io::Result<()>
57 where
58 F: FnOnce(&mut ratatui::Frame),
59 {
60 self.terminal.draw(f)?;
61 Ok(())
62 }
63}
64
65impl Drop for ManagedTerminal {
66 fn drop(&mut self) {
68 let _ = disable_raw_mode();
70 let _ = execute!(
71 self.terminal.backend_mut(),
72 LeaveAlternateScreen,
73 DisableMouseCapture
74 );
75 let _ = self.terminal.show_cursor();
76 }
77}
78
79pub fn setup_terminal() -> io::Result<ManagedTerminal> {
81 ManagedTerminal::new()
82}
83
84pub fn restore_terminal() -> io::Result<()> {
86 disable_raw_mode()?;
88
89 execute!(std::io::stdout(), LeaveAlternateScreen, DisableMouseCapture)?;
91
92 Ok(())
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use std::sync::{Arc, Mutex};
99 use std::thread;
100 use std::time::Duration;
101
102 #[test]
104 fn test_managed_terminal_creation_and_cleanup() {
105 }
113
114 #[test]
116 fn test_size_conversion() {
117 let width = 80u16;
119 let height = 24u16;
120
121 let rect = ratatui::layout::Rect::new(0, 0, width, height);
122
123 assert_eq!(rect.x, 0);
124 assert_eq!(rect.y, 0);
125 assert_eq!(rect.width, width);
126 assert_eq!(rect.height, height);
127 }
128
129 #[test]
131 fn test_setup_terminal_signature() {
132 let _setup_fn: fn() -> io::Result<ManagedTerminal> = setup_terminal;
134 }
135
136 #[test]
138 fn test_restore_terminal_signature() {
139 let _restore_fn: fn() -> io::Result<()> = restore_terminal;
141 }
142
143 #[test]
145 fn test_managed_terminal_methods() {
146 fn _test_methods(mut terminal: ManagedTerminal) -> io::Result<()> {
152 let _size = terminal.size()?;
153 terminal.clear()?;
154 terminal.draw(|_frame| {})?;
155 let _term_ref = terminal.terminal_mut();
156 Ok(())
157 }
158
159 }
161
162 #[test]
164 fn test_error_handling() {
165 let _io_error: io::Error = io::Error::other("test");
167
168 fn _test_error_types() {
170 let _: io::Result<ManagedTerminal> = setup_terminal();
171 let _: io::Result<()> = restore_terminal();
172 }
173 }
174
175 #[test]
177 fn test_thread_safety() {
178 let counter = Arc::new(Mutex::new(0));
180 let counter_clone = Arc::clone(&counter);
181
182 let handle = thread::spawn(move || {
183 let mut num = counter_clone.lock().unwrap();
184 *num += 1;
185
186 let _setup_fn = setup_terminal;
188 let _restore_fn = restore_terminal;
189 });
190
191 handle.join().unwrap();
192 assert_eq!(*counter.lock().unwrap(), 1);
193 }
194
195 #[test]
197 fn test_drop_implementation() {
198 use std::mem;
201
202 assert!(mem::needs_drop::<ManagedTerminal>());
204
205 }
208
209 #[test]
211 fn test_performance_characteristics() {
212 let start = std::time::Instant::now();
214
215 for _ in 0..1000 {
217 let _rect = ratatui::layout::Rect::new(0, 0, 80, 24);
218 }
219
220 let duration = start.elapsed();
221
222 assert!(duration < Duration::from_millis(10));
224 }
225
226 #[test]
228 fn test_memory_usage() {
229 use std::mem;
231
232 let terminal_size = mem::size_of::<ManagedTerminal>();
234
235 assert!(terminal_size < 1024);
237
238 let rect_size = mem::size_of::<ratatui::layout::Rect>();
240 assert!(rect_size <= 8); }
242}