reratui_runtime/
terminal.rs1use 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!(
91 std::io::stdout(),
92 LeaveAlternateScreen,
93 DisableMouseCapture,
94 crossterm::cursor::Show
95 )?;
96
97 Ok(())
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103 use std::sync::{Arc, Mutex};
104 use std::thread;
105 use std::time::Duration;
106
107 #[test]
109 fn test_managed_terminal_creation_and_cleanup() {
110 }
118
119 #[test]
121 fn test_size_conversion() {
122 let width = 80u16;
124 let height = 24u16;
125
126 let rect = ratatui::layout::Rect::new(0, 0, width, height);
127
128 assert_eq!(rect.x, 0);
129 assert_eq!(rect.y, 0);
130 assert_eq!(rect.width, width);
131 assert_eq!(rect.height, height);
132 }
133
134 #[test]
136 fn test_setup_terminal_signature() {
137 let _setup_fn: fn() -> io::Result<ManagedTerminal> = setup_terminal;
139 }
140
141 #[test]
143 fn test_restore_terminal_signature() {
144 let _restore_fn: fn() -> io::Result<()> = restore_terminal;
146 }
147
148 #[test]
150 fn test_managed_terminal_methods() {
151 fn _test_methods(mut terminal: ManagedTerminal) -> io::Result<()> {
157 let _size = terminal.size()?;
158 terminal.clear()?;
159 terminal.draw(|_frame| {})?;
160 let _term_ref = terminal.terminal_mut();
161 Ok(())
162 }
163
164 }
166
167 #[test]
169 fn test_error_handling() {
170 let _io_error: io::Error = io::Error::other("test");
172
173 fn _test_error_types() {
175 let _: io::Result<ManagedTerminal> = setup_terminal();
176 let _: io::Result<()> = restore_terminal();
177 }
178 }
179
180 #[test]
182 fn test_thread_safety() {
183 let counter = Arc::new(Mutex::new(0));
185 let counter_clone = Arc::clone(&counter);
186
187 let handle = thread::spawn(move || {
188 let mut num = counter_clone.lock().unwrap();
189 *num += 1;
190
191 let _setup_fn = setup_terminal;
193 let _restore_fn = restore_terminal;
194 });
195
196 handle.join().unwrap();
197 assert_eq!(*counter.lock().unwrap(), 1);
198 }
199
200 #[test]
202 fn test_drop_implementation() {
203 use std::mem;
206
207 assert!(mem::needs_drop::<ManagedTerminal>());
209
210 }
213
214 #[test]
216 fn test_performance_characteristics() {
217 let start = std::time::Instant::now();
219
220 for _ in 0..1000 {
222 let _rect = ratatui::layout::Rect::new(0, 0, 80, 24);
223 }
224
225 let duration = start.elapsed();
226
227 assert!(duration < Duration::from_millis(10));
229 }
230
231 #[test]
233 fn test_memory_usage() {
234 use std::mem;
236
237 let terminal_size = mem::size_of::<ManagedTerminal>();
239
240 assert!(terminal_size < 1024);
242
243 let rect_size = mem::size_of::<ratatui::layout::Rect>();
245 assert!(rect_size <= 8); }
247}