1use std::collections::{BTreeMap, BTreeSet};
2
3use crate::{Position, Style};
4
5#[derive(Debug, Clone, Eq, PartialEq)]
7pub(crate) struct Cell {
8 grapheme: String,
9 style: Option<Style>,
10}
11
12impl Cell {
13 pub(crate) fn grapheme(&self) -> &str {
15 &self.grapheme
16 }
17
18 pub(crate) fn style(&self) -> Option<&Style> {
20 self.style.as_ref()
21 }
22}
23
24#[derive(Clone)]
26pub(crate) struct State {
27 cells: BTreeMap<Position, Cell>,
28 dirty: BTreeSet<Position>,
29}
30
31impl State {
32 pub(crate) fn new() -> State {
34 State {
35 cells: BTreeMap::new(),
36 dirty: BTreeSet::new(),
37 }
38 }
39
40 pub(crate) fn set_text(&mut self, position: Position, grapheme: &str) {
42 self.handle_cell_update(position, grapheme, None);
43 }
44
45 pub(crate) fn set_styled_text(&mut self, position: Position, grapheme: &str, style: Style) {
47 self.handle_cell_update(position, grapheme, Some(style));
48 }
49
50 fn handle_cell_update(&mut self, position: Position, grapheme: &str, style: Option<Style>) {
52 let new_cell = Cell {
53 grapheme: grapheme.to_string(),
54 style,
55 };
56
57 if Some(&new_cell) == self.cells.get(&position) {
59 return;
60 }
61
62 self.dirty.insert(position);
63 self.cells.insert(position, new_cell);
64 }
65
66 pub(crate) fn clear_line(&mut self, line: u16) {
68 self.handle_cell_clears(|position| position.y() == line);
69 }
70
71 pub(crate) fn clear_rest_of_line(&mut self, from: Position) {
73 self.handle_cell_clears(|position| position.y() == from.y() && position.x() >= from.x());
74 }
75
76 pub(crate) fn clear_rest_of_interface(&mut self, from: Position) {
78 self.handle_cell_clears(|position| *position >= &from);
79 }
80
81 fn handle_cell_clears<P: FnMut(&&Position) -> bool>(&mut self, filter_predicate: P) {
83 let cells = self.cells.keys();
84 let deleted_cells = cells.filter(filter_predicate);
85 let cell_positions: Vec<Position> = deleted_cells.copied().collect();
86
87 for position in cell_positions {
88 self.cells.remove(&position);
89 self.dirty.insert(position);
90 }
91 }
92
93 pub(crate) fn clear_dirty(&mut self) {
95 self.dirty.clear()
96 }
97
98 pub(crate) fn dirty_iter(&self) -> StateIter<'_> {
100 StateIter::new(self, self.dirty.clone().into_iter().collect())
101 }
102
103 pub(crate) fn get_last_position(&self) -> Option<Position> {
105 self.cells.keys().last().copied()
106 }
107}
108
109pub(crate) struct StateIter<'a> {
111 state: &'a State,
112 positions: Vec<Position>,
113 index: usize,
114}
115
116impl StateIter<'_> {
117 fn new(state: &State, positions: Vec<Position>) -> StateIter<'_> {
119 StateIter {
120 state,
121 positions,
122 index: 0,
123 }
124 }
125}
126
127impl Iterator for StateIter<'_> {
128 type Item = (Position, Option<Cell>);
129
130 fn next(&mut self) -> Option<Self::Item> {
131 if self.index < self.positions.len() {
132 let position = self.positions[self.index];
133 let cell = self.state.cells.get(&position).cloned();
134
135 self.index += 1;
136 Some((position, cell))
137 } else {
138 None
139 }
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use crate::{Color, Position, Style, pos};
146
147 use super::{Cell, State};
148
149 #[test]
150 fn state_set_text() {
151 let mut state = State::new();
152
153 state.set_text(pos!(0, 0), "A");
154 state.set_text(pos!(2, 0), "B");
155 state.set_text(pos!(1, 1), "C");
156
157 assert_eq!(3, state.cells.len());
158 assert_eq!(
159 Cell {
160 grapheme: "A".to_string(),
161 style: None
162 },
163 state.cells[&pos!(0, 0)]
164 );
165 assert_eq!(
166 Cell {
167 grapheme: "B".to_string(),
168 style: None
169 },
170 state.cells[&pos!(2, 0)]
171 );
172 assert_eq!(
173 Cell {
174 grapheme: "C".to_string(),
175 style: None
176 },
177 state.cells[&pos!(1, 1)]
178 );
179
180 let dirty_positions: Vec<_> = state.dirty.clone().into_iter().collect();
181 assert_eq!(3, dirty_positions.len());
182 assert_eq!(pos!(0, 0), dirty_positions[0]);
183 assert_eq!(pos!(2, 0), dirty_positions[1]);
184 assert_eq!(pos!(1, 1), dirty_positions[2]);
185 }
186
187 #[test]
188 fn state_set_styled_text() {
189 let mut state = State::new();
190
191 state.set_styled_text(pos!(0, 0), "X", Style::new().set_bold(true));
192 state.set_styled_text(pos!(1, 3), "Y", Style::new().set_italic(true));
193 state.set_styled_text(pos!(2, 2), "Z", Style::new().set_foreground(Color::Blue));
194
195 assert_eq!(3, state.cells.len());
196 assert_eq!(
197 Cell {
198 grapheme: "X".to_string(),
199 style: Some(Style::new().set_bold(true)),
200 },
201 state.cells[&pos!(0, 0)],
202 );
203 assert_eq!(
204 Cell {
205 grapheme: "Y".to_string(),
206 style: Some(Style::new().set_italic(true)),
207 },
208 state.cells[&pos!(1, 3)],
209 );
210 assert_eq!(
211 Cell {
212 grapheme: "Z".to_string(),
213 style: Some(Style::new().set_foreground(Color::Blue)),
214 },
215 state.cells[&pos!(2, 2)],
216 );
217
218 let dirty_positions: Vec<_> = state.dirty.clone().into_iter().collect();
219 assert_eq!(3, dirty_positions.len());
220 assert_eq!(pos!(0, 0), dirty_positions[0]);
221 assert_eq!(pos!(2, 2), dirty_positions[1]);
222 assert_eq!(pos!(1, 3), dirty_positions[2]);
223 }
224
225 #[test]
226 fn state_clear_line() {
227 let mut state = State::new();
228
229 state.set_text(pos!(0, 0), "A");
230 state.set_text(pos!(2, 0), "B");
231 state.set_text(pos!(1, 1), "C");
232 state.set_text(pos!(3, 1), "D");
233 state.clear_dirty();
234
235 assert_eq!(4, state.cells.len());
236 assert_eq!(
237 Cell {
238 grapheme: "A".to_string(),
239 style: None
240 },
241 state.cells[&pos!(0, 0)]
242 );
243 assert_eq!(
244 Cell {
245 grapheme: "B".to_string(),
246 style: None
247 },
248 state.cells[&pos!(2, 0)]
249 );
250 assert_eq!(
251 Cell {
252 grapheme: "C".to_string(),
253 style: None
254 },
255 state.cells[&pos!(1, 1)]
256 );
257 assert_eq!(
258 Cell {
259 grapheme: "D".to_string(),
260 style: None
261 },
262 state.cells[&pos!(3, 1)]
263 );
264
265 state.clear_line(1);
266
267 let dirty_positions: Vec<_> = state.dirty.clone().into_iter().collect();
268 assert_eq!(2, dirty_positions.len());
269 assert_eq!(pos!(1, 1), dirty_positions[0]);
270 assert_eq!(pos!(3, 1), dirty_positions[1]);
271
272 let line_two_cell_count = state.cells.keys().filter(|pos| pos.y() == 1).count();
273 assert_eq!(0, line_two_cell_count);
274 }
275
276 #[test]
277 fn state_clear_dirty() {
278 let mut state = State::new();
279
280 state.set_text(pos!(0, 0), "A");
281 state.set_text(pos!(2, 0), "B");
282 state.set_text(pos!(1, 1), "C");
283
284 assert_eq!(3, state.cells.len());
285 assert_eq!(
286 Cell {
287 grapheme: "A".to_string(),
288 style: None
289 },
290 state.cells[&pos!(0, 0)]
291 );
292 assert_eq!(
293 Cell {
294 grapheme: "B".to_string(),
295 style: None
296 },
297 state.cells[&pos!(2, 0)]
298 );
299 assert_eq!(
300 Cell {
301 grapheme: "C".to_string(),
302 style: None
303 },
304 state.cells[&pos!(1, 1)]
305 );
306 }
307
308 #[test]
309 fn state_clear_rest_of_line() {
310 let mut state = State::new();
311
312 let content = ["ABC", "DEF", "GHI"];
313
314 for (row, text) in content.iter().enumerate() {
315 for column in 0..text.len() {
316 state.set_text(
317 pos!(column as u16, row as u16),
318 text.get(column..column + 1).unwrap(),
319 );
320 }
321 }
322
323 state.clear_dirty();
324
325 assert_eq!(9, state.cells.len());
326
327 state.clear_rest_of_line(pos!(1, 1));
328
329 assert_eq!(7, state.cells.len());
330
331 let dirty_positions: Vec<_> = state.dirty.clone().into_iter().collect();
332 assert_eq!(2, dirty_positions.len());
333 assert_eq!(pos!(1, 1), dirty_positions[0]);
334 assert_eq!(pos!(2, 1), dirty_positions[1]);
335
336 let line_two_cell_count = state.cells.keys().filter(|pos| pos.y() == 1).count();
337 assert_eq!(1, line_two_cell_count);
338 }
339
340 #[test]
341 fn state_clear_rest_of_interface() {
342 let mut state = State::new();
343
344 let content = ["ABC", "DEF", "GHI"];
345
346 for (row, text) in content.iter().enumerate() {
347 for column in 0..text.len() {
348 state.set_text(
349 pos!(column as u16, row as u16),
350 text.get(column..column + 1).unwrap(),
351 );
352 }
353 }
354
355 state.clear_dirty();
356
357 assert_eq!(9, state.cells.len());
358
359 state.clear_rest_of_interface(pos!(1, 1));
360
361 assert_eq!(4, state.cells.len());
362
363 let dirty_positions: Vec<_> = state.dirty.clone().into_iter().collect();
364 assert_eq!(5, dirty_positions.len());
365 assert_eq!(pos!(1, 1), dirty_positions[0]);
366 assert_eq!(pos!(2, 1), dirty_positions[1]);
367 assert_eq!(pos!(0, 2), dirty_positions[2]);
368 assert_eq!(pos!(1, 2), dirty_positions[3]);
369 assert_eq!(pos!(2, 2), dirty_positions[4]);
370 }
371
372 #[test]
373 fn state_dirty_iter() {
374 let mut state = State::new();
375
376 state.set_text(pos!(0, 0), "A");
377 state.clear_dirty();
378
379 state.set_text(pos!(2, 0), "B");
380 state.set_text(pos!(1, 1), "C");
381 state.set_text(pos!(0, 2), "D");
382 state.clear_line(1);
383
384 let mut iter = state.dirty_iter();
385 assert_eq!(
386 Some((
387 pos!(2, 0),
388 Some(Cell {
389 grapheme: "B".to_string(),
390 style: None
391 })
392 )),
393 iter.next()
394 );
395 assert_eq!(Some((pos!(1, 1), None,)), iter.next());
396 assert_eq!(
397 Some((
398 pos!(0, 2),
399 Some(Cell {
400 grapheme: "D".to_string(),
401 style: None
402 })
403 )),
404 iter.next()
405 );
406 assert_eq!(None, iter.next());
407 }
408
409 #[test]
410 fn state_get_last_position() {
411 let mut state = State::new();
412
413 state.set_text(pos!(3, 1), "D");
414 state.set_text(pos!(1, 1), "C");
415 state.set_text(pos!(0, 0), "A");
416 state.set_text(pos!(2, 0), "B");
417
418 assert_eq!(pos!(3, 1), state.get_last_position().unwrap());
419 }
420}