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.map(|position| *position).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
106 .keys()
107 .last()
108 .and_then(|position| Some(*position))
109 }
110}
111
112pub(crate) struct StateIter<'a> {
114 state: &'a State,
115 positions: Vec<Position>,
116 index: usize,
117}
118
119impl StateIter<'_> {
120 fn new(state: &State, positions: Vec<Position>) -> StateIter {
122 StateIter {
123 state,
124 positions,
125 index: 0,
126 }
127 }
128}
129
130impl<'a> Iterator for StateIter<'_> {
131 type Item = (Position, Option<Cell>);
132
133 fn next(&mut self) -> Option<Self::Item> {
134 if self.index < self.positions.len() {
135 let position = self.positions[self.index];
136 let cell = self
137 .state
138 .cells
139 .get(&position)
140 .and_then(|cell| Some(cell.clone()));
141
142 self.index += 1;
143 Some((position, cell))
144 } else {
145 None
146 }
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use crate::{pos, Color, Position, Style};
153
154 use super::{Cell, State};
155
156 #[test]
157 fn state_set_text() {
158 let mut state = State::new();
159
160 state.set_text(pos!(0, 0), "A");
161 state.set_text(pos!(2, 0), "B");
162 state.set_text(pos!(1, 1), "C");
163
164 assert_eq!(3, state.cells.len());
165 assert_eq!(
166 Cell {
167 grapheme: "A".to_string(),
168 style: None
169 },
170 state.cells[&pos!(0, 0)]
171 );
172 assert_eq!(
173 Cell {
174 grapheme: "B".to_string(),
175 style: None
176 },
177 state.cells[&pos!(2, 0)]
178 );
179 assert_eq!(
180 Cell {
181 grapheme: "C".to_string(),
182 style: None
183 },
184 state.cells[&pos!(1, 1)]
185 );
186
187 let dirty_positions: Vec<_> = state.dirty.clone().into_iter().collect();
188 assert_eq!(3, dirty_positions.len());
189 assert_eq!(pos!(0, 0), dirty_positions[0]);
190 assert_eq!(pos!(2, 0), dirty_positions[1]);
191 assert_eq!(pos!(1, 1), dirty_positions[2]);
192 }
193
194 #[test]
195 fn state_set_styled_text() {
196 let mut state = State::new();
197
198 state.set_styled_text(pos!(0, 0), "X", Style::new().set_bold(true));
199 state.set_styled_text(pos!(1, 3), "Y", Style::new().set_italic(true));
200 state.set_styled_text(pos!(2, 2), "Z", Style::new().set_foreground(Color::Blue));
201
202 assert_eq!(3, state.cells.len());
203 assert_eq!(
204 Cell {
205 grapheme: "X".to_string(),
206 style: Some(Style::new().set_bold(true)),
207 },
208 state.cells[&pos!(0, 0)],
209 );
210 assert_eq!(
211 Cell {
212 grapheme: "Y".to_string(),
213 style: Some(Style::new().set_italic(true)),
214 },
215 state.cells[&pos!(1, 3)],
216 );
217 assert_eq!(
218 Cell {
219 grapheme: "Z".to_string(),
220 style: Some(Style::new().set_foreground(Color::Blue)),
221 },
222 state.cells[&pos!(2, 2)],
223 );
224
225 let dirty_positions: Vec<_> = state.dirty.clone().into_iter().collect();
226 assert_eq!(3, dirty_positions.len());
227 assert_eq!(pos!(0, 0), dirty_positions[0]);
228 assert_eq!(pos!(2, 2), dirty_positions[1]);
229 assert_eq!(pos!(1, 3), dirty_positions[2]);
230 }
231
232 #[test]
233 fn state_clear_line() {
234 let mut state = State::new();
235
236 state.set_text(pos!(0, 0), "A");
237 state.set_text(pos!(2, 0), "B");
238 state.set_text(pos!(1, 1), "C");
239 state.set_text(pos!(3, 1), "D");
240 state.clear_dirty();
241
242 assert_eq!(4, state.cells.len());
243 assert_eq!(
244 Cell {
245 grapheme: "A".to_string(),
246 style: None
247 },
248 state.cells[&pos!(0, 0)]
249 );
250 assert_eq!(
251 Cell {
252 grapheme: "B".to_string(),
253 style: None
254 },
255 state.cells[&pos!(2, 0)]
256 );
257 assert_eq!(
258 Cell {
259 grapheme: "C".to_string(),
260 style: None
261 },
262 state.cells[&pos!(1, 1)]
263 );
264 assert_eq!(
265 Cell {
266 grapheme: "D".to_string(),
267 style: None
268 },
269 state.cells[&pos!(3, 1)]
270 );
271
272 state.clear_line(1);
273
274 let dirty_positions: Vec<_> = state.dirty.clone().into_iter().collect();
275 assert_eq!(2, dirty_positions.len());
276 assert_eq!(pos!(1, 1), dirty_positions[0]);
277 assert_eq!(pos!(3, 1), dirty_positions[1]);
278
279 let line_two_cell_count = state.cells.keys().filter(|pos| pos.y() == 1).count();
280 assert_eq!(0, line_two_cell_count);
281 }
282
283 #[test]
284 fn state_clear_dirty() {
285 let mut state = State::new();
286
287 state.set_text(pos!(0, 0), "A");
288 state.set_text(pos!(2, 0), "B");
289 state.set_text(pos!(1, 1), "C");
290
291 assert_eq!(3, state.cells.len());
292 assert_eq!(
293 Cell {
294 grapheme: "A".to_string(),
295 style: None
296 },
297 state.cells[&pos!(0, 0)]
298 );
299 assert_eq!(
300 Cell {
301 grapheme: "B".to_string(),
302 style: None
303 },
304 state.cells[&pos!(2, 0)]
305 );
306 assert_eq!(
307 Cell {
308 grapheme: "C".to_string(),
309 style: None
310 },
311 state.cells[&pos!(1, 1)]
312 );
313 }
314
315 #[test]
316 fn state_clear_rest_of_line() {
317 let mut state = State::new();
318
319 let content = ["ABC", "DEF", "GHI"];
320
321 for row in 0..content.len() {
322 let text = content[row];
323 for column in 0..text.len() {
324 state.set_text(
325 pos!(column as u16, row as u16),
326 text.get(column..column + 1).unwrap(),
327 );
328 }
329 }
330
331 state.clear_dirty();
332
333 assert_eq!(9, state.cells.len());
334
335 state.clear_rest_of_line(pos!(1, 1));
336
337 assert_eq!(7, state.cells.len());
338
339 let dirty_positions: Vec<_> = state.dirty.clone().into_iter().collect();
340 assert_eq!(2, dirty_positions.len());
341 assert_eq!(pos!(1, 1), dirty_positions[0]);
342 assert_eq!(pos!(2, 1), dirty_positions[1]);
343
344 let line_two_cell_count = state.cells.keys().filter(|pos| pos.y() == 1).count();
345 assert_eq!(1, line_two_cell_count);
346 }
347
348 #[test]
349 fn state_clear_rest_of_interface() {
350 let mut state = State::new();
351
352 let content = ["ABC", "DEF", "GHI"];
353
354 for row in 0..content.len() {
355 let text = content[row];
356 for column in 0..text.len() {
357 state.set_text(
358 pos!(column as u16, row as u16),
359 text.get(column..column + 1).unwrap(),
360 );
361 }
362 }
363
364 state.clear_dirty();
365
366 assert_eq!(9, state.cells.len());
367
368 state.clear_rest_of_interface(pos!(1, 1));
369
370 assert_eq!(4, state.cells.len());
371
372 let dirty_positions: Vec<_> = state.dirty.clone().into_iter().collect();
373 assert_eq!(5, dirty_positions.len());
374 assert_eq!(pos!(1, 1), dirty_positions[0]);
375 assert_eq!(pos!(2, 1), dirty_positions[1]);
376 assert_eq!(pos!(0, 2), dirty_positions[2]);
377 assert_eq!(pos!(1, 2), dirty_positions[3]);
378 assert_eq!(pos!(2, 2), dirty_positions[4]);
379 }
380
381 #[test]
382 fn state_dirty_iter() {
383 let mut state = State::new();
384
385 state.set_text(pos!(0, 0), "A");
386 state.clear_dirty();
387
388 state.set_text(pos!(2, 0), "B");
389 state.set_text(pos!(1, 1), "C");
390 state.set_text(pos!(0, 2), "D");
391 state.clear_line(1);
392
393 let mut iter = state.dirty_iter();
394 assert_eq!(
395 Some((
396 pos!(2, 0),
397 Some(Cell {
398 grapheme: "B".to_string(),
399 style: None
400 })
401 )),
402 iter.next()
403 );
404 assert_eq!(Some((pos!(1, 1), None,)), iter.next());
405 assert_eq!(
406 Some((
407 pos!(0, 2),
408 Some(Cell {
409 grapheme: "D".to_string(),
410 style: None
411 })
412 )),
413 iter.next()
414 );
415 assert_eq!(None, iter.next());
416 }
417
418 #[test]
419 fn state_get_last_position() {
420 let mut state = State::new();
421
422 state.set_text(pos!(3, 1), "D");
423 state.set_text(pos!(1, 1), "C");
424 state.set_text(pos!(0, 0), "A");
425 state.set_text(pos!(2, 0), "B");
426
427 assert_eq!(pos!(3, 1), state.get_last_position().unwrap());
428 }
429}