1use crate::buffer::{Buffer, Cell};
4use crate::geometry::{Position, Rect};
5use crate::style::Style;
6use core::fmt;
7
8pub trait Backend {
26 type Error: fmt::Debug + fmt::Display;
28
29 fn size(&self) -> Result<Rect, Self::Error>;
31
32 fn clear(&mut self) -> Result<(), Self::Error>;
34
35 fn clear_region(&mut self, region: Rect) -> Result<(), Self::Error> {
37 for y in region.top()..region.bottom() {
39 for x in region.left()..region.right() {
40 self.draw_cell(x, y, &Cell::default())?;
41 }
42 }
43 Ok(())
44 }
45
46 fn hide_cursor(&mut self) -> Result<(), Self::Error>;
48
49 fn show_cursor(&mut self) -> Result<(), Self::Error>;
51
52 fn get_cursor(&mut self) -> Result<Position, Self::Error>;
54
55 fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), Self::Error>;
57
58 fn draw_cell(&mut self, x: u16, y: u16, cell: &Cell) -> Result<(), Self::Error>;
60
61 fn set_style(&mut self, style: Style) -> Result<(), Self::Error>;
63
64 fn reset_style(&mut self) -> Result<(), Self::Error>;
66
67 fn flush(&mut self) -> Result<(), Self::Error>;
69
70 fn enable_raw_mode(&mut self) -> Result<(), Self::Error>;
72
73 fn disable_raw_mode(&mut self) -> Result<(), Self::Error>;
75
76 fn enter_alternate_screen(&mut self) -> Result<(), Self::Error>;
78
79 fn leave_alternate_screen(&mut self) -> Result<(), Self::Error>;
81
82 #[cfg(feature = "scrolling-regions")]
84 fn set_scroll_region(&mut self, top: u16, bottom: u16) -> Result<(), Self::Error> {
85 let _ = (top, bottom);
86 Ok(())
87 }
88
89 #[cfg(feature = "scrolling-regions")]
91 fn clear_scroll_region(&mut self) -> Result<(), Self::Error> {
92 Ok(())
93 }
94}
95
96pub struct TestBackend {
113 width: u16,
114 height: u16,
115 buffer: Buffer,
116 cursor_visible: bool,
117 cursor_position: Position,
118}
119
120impl TestBackend {
121 #[must_use]
123 pub fn new(width: u16, height: u16) -> Self {
124 Self {
125 width,
126 height,
127 buffer: Buffer::empty(Rect::new(0, 0, width, height)),
128 cursor_visible: true,
129 cursor_position: Position::new(0, 0),
130 }
131 }
132
133 #[must_use]
135 pub fn buffer(&self) -> &Buffer {
136 &self.buffer
137 }
138
139 pub fn buffer_mut(&mut self) -> &mut Buffer {
141 &mut self.buffer
142 }
143
144 pub fn resize(&mut self, width: u16, height: u16) {
146 self.width = width;
147 self.height = height;
148 self.buffer.resize(Rect::new(0, 0, width, height));
149 }
150
151 #[must_use]
153 pub const fn is_cursor_visible(&self) -> bool {
154 self.cursor_visible
155 }
156
157 pub fn assert_buffer_equals(&self, expected: &str) {
163 let actual = format!("{}", self.buffer);
164 assert_eq!(actual.trim(), expected.trim(), "Buffer mismatch");
165 }
166}
167
168impl Backend for TestBackend {
169 type Error = TestBackendError;
170
171 fn size(&self) -> Result<Rect, Self::Error> {
172 Ok(Rect::new(0, 0, self.width, self.height))
173 }
174
175 fn clear(&mut self) -> Result<(), Self::Error> {
176 self.buffer.clear();
177 Ok(())
178 }
179
180 fn clear_region(&mut self, region: Rect) -> Result<(), Self::Error> {
181 self.buffer.clear_region(region);
182 Ok(())
183 }
184
185 fn hide_cursor(&mut self) -> Result<(), Self::Error> {
186 self.cursor_visible = false;
187 Ok(())
188 }
189
190 fn show_cursor(&mut self) -> Result<(), Self::Error> {
191 self.cursor_visible = true;
192 Ok(())
193 }
194
195 fn get_cursor(&mut self) -> Result<Position, Self::Error> {
196 Ok(self.cursor_position)
197 }
198
199 fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), Self::Error> {
200 self.cursor_position = Position::new(x, y);
201 Ok(())
202 }
203
204 fn draw_cell(&mut self, x: u16, y: u16, cell: &Cell) -> Result<(), Self::Error> {
205 self.buffer.set(x, y, cell.symbol.as_str(), cell.style);
206 Ok(())
207 }
208
209 fn set_style(&mut self, _style: Style) -> Result<(), Self::Error> {
210 Ok(())
211 }
212
213 fn reset_style(&mut self) -> Result<(), Self::Error> {
214 Ok(())
215 }
216
217 fn flush(&mut self) -> Result<(), Self::Error> {
218 Ok(())
219 }
220
221 fn enable_raw_mode(&mut self) -> Result<(), Self::Error> {
222 Ok(())
223 }
224
225 fn disable_raw_mode(&mut self) -> Result<(), Self::Error> {
226 Ok(())
227 }
228
229 fn enter_alternate_screen(&mut self) -> Result<(), Self::Error> {
230 Ok(())
231 }
232
233 fn leave_alternate_screen(&mut self) -> Result<(), Self::Error> {
234 Ok(())
235 }
236}
237
238#[derive(Debug, Clone, PartialEq, Eq)]
240pub enum TestBackendError {
241 Generic(alloc::string::String),
243}
244
245impl fmt::Display for TestBackendError {
246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247 match self {
248 Self::Generic(msg) => write!(f, "{msg}"),
249 }
250 }
251}
252
253#[cfg(feature = "std")]
254impl std::error::Error for TestBackendError {}
255
256#[cfg(test)]
257mod tests {
258 use super::*;
259
260 #[test]
261 fn test_backend_size() {
262 let backend = TestBackend::new(80, 24);
263 let size = backend.size().unwrap();
264 assert_eq!(size.width, 80);
265 assert_eq!(size.height, 24);
266 }
267
268 #[test]
269 fn test_backend_cursor() {
270 let mut backend = TestBackend::new(80, 24);
271 backend.set_cursor(10, 5).unwrap();
272 let pos = backend.get_cursor().unwrap();
273 assert_eq!(pos.x, 10);
274 assert_eq!(pos.y, 5);
275 }
276
277 #[test]
278 fn test_backend_clear() {
279 let mut backend = TestBackend::new(10, 5);
280 backend.buffer_mut().set(0, 0, "X", Style::default());
281 backend.clear().unwrap();
282 assert_eq!(backend.buffer().get(0, 0).unwrap().symbol, " ");
283 }
284}