virtualizer/
emitter.rs

1use crate::Range;
2
3/// Helper to build correct `range_extractor` implementations without allocations.
4///
5/// It enforces the extractor contract:
6/// - Out-of-bounds indexes are ignored (and debug-asserted).
7/// - Duplicates are ignored.
8/// - Out-of-order indexes are ignored (and debug-asserted).
9///
10/// # Example
11///
12/// ```no_run
13/// use virtualizer::{IndexEmitter, Range};
14///
15/// let extractor = |range: Range, emit: &mut dyn FnMut(usize)| {
16///     let mut e = IndexEmitter::new(range, emit);
17///     e.emit_pinned(0);
18///     e.emit_overscanned();
19/// };
20/// ```
21pub struct IndexEmitter<'a> {
22    range: Range,
23    last: Option<usize>,
24    emit: &'a mut dyn FnMut(usize),
25}
26
27impl<'a> IndexEmitter<'a> {
28    pub fn new(range: Range, emit: &'a mut dyn FnMut(usize)) -> Self {
29        Self {
30            range,
31            last: None,
32            emit,
33        }
34    }
35
36    pub fn range(&self) -> Range {
37        self.range
38    }
39
40    pub fn emit(&mut self, index: usize) {
41        if index >= self.range.count {
42            vwarn!(
43                index,
44                count = self.range.count,
45                "IndexEmitter: out-of-bounds index"
46            );
47            debug_assert!(
48                index < self.range.count,
49                "IndexEmitter: out-of-bounds index (i={index}, count={})",
50                self.range.count
51            );
52            return;
53        }
54
55        if let Some(prev) = self.last {
56            if index == prev {
57                return;
58            }
59            if index < prev {
60                vwarn!(
61                    prev,
62                    next = index,
63                    "IndexEmitter: indexes must be emitted in ascending order"
64                );
65                debug_assert!(
66                    index > prev,
67                    "IndexEmitter: indexes must be emitted in ascending order (prev={prev}, next={index})"
68                );
69                return;
70            }
71        }
72
73        self.last = Some(index);
74        (self.emit)(index);
75    }
76
77    pub fn emit_pinned(&mut self, index: usize) {
78        self.emit(index);
79    }
80
81    pub fn emit_range(&mut self, start_index: usize, end_index: usize) {
82        let end = end_index.min(self.range.count);
83        for i in start_index..end {
84            self.emit(i);
85        }
86    }
87
88    pub fn emit_visible(&mut self) {
89        self.emit_range(self.range.start_index, self.range.end_index);
90    }
91
92    pub fn emit_overscanned(&mut self) {
93        let start = self.range.start_index.saturating_sub(self.range.overscan);
94        let end = self
95            .range
96            .end_index
97            .saturating_add(self.range.overscan)
98            .min(self.range.count);
99        self.emit_range(start, end);
100    }
101}