tirea_state/
delta_tracked.rs1#[derive(Debug, Clone)]
10pub struct DeltaTracked<T> {
11 items: Vec<T>,
12 cursor: usize,
13 initial_len: usize,
16}
17
18impl<T> DeltaTracked<T> {
19 pub fn new(items: Vec<T>) -> Self {
21 let len = items.len();
22 Self {
23 items,
24 cursor: len,
25 initial_len: len,
26 }
27 }
28
29 pub fn empty() -> Self {
31 Self {
32 items: Vec::new(),
33 cursor: 0,
34 initial_len: 0,
35 }
36 }
37
38 pub fn push(&mut self, item: T) {
40 self.items.push(item);
41 }
42
43 pub fn extend(&mut self, iter: impl IntoIterator<Item = T>) {
45 self.items.extend(iter);
46 }
47
48 pub fn as_slice(&self) -> &[T] {
50 &self.items
51 }
52
53 pub fn len(&self) -> usize {
55 self.items.len()
56 }
57
58 pub fn is_empty(&self) -> bool {
60 self.items.is_empty()
61 }
62
63 pub fn into_items(self) -> Vec<T> {
65 self.items
66 }
67
68 pub fn initial_count(&self) -> usize {
71 self.initial_len
72 }
73
74 pub fn has_delta(&self) -> bool {
76 self.cursor < self.items.len()
77 }
78}
79
80impl<T: Clone> DeltaTracked<T> {
81 pub fn take_delta(&mut self) -> Vec<T> {
83 let delta = self.items[self.cursor..].to_vec();
84 self.cursor = self.items.len();
85 delta
86 }
87}
88
89impl<T> Default for DeltaTracked<T> {
90 fn default() -> Self {
91 Self::empty()
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn new_has_no_delta() {
101 let mut dt = DeltaTracked::new(vec![1, 2, 3]);
102 assert!(!dt.has_delta());
103 assert_eq!(dt.take_delta(), Vec::<i32>::new());
104 assert_eq!(dt.as_slice(), &[1, 2, 3]);
105 }
106
107 #[test]
108 fn empty_has_no_delta() {
109 let dt = DeltaTracked::<i32>::empty();
110 assert!(!dt.has_delta());
111 assert!(dt.is_empty());
112 }
113
114 #[test]
115 fn push_creates_delta() {
116 let mut dt = DeltaTracked::new(vec![1, 2]);
117 dt.push(3);
118 dt.push(4);
119 assert!(dt.has_delta());
120 assert_eq!(dt.take_delta(), vec![3, 4]);
121 assert!(!dt.has_delta());
122 assert_eq!(dt.as_slice(), &[1, 2, 3, 4]);
123 }
124
125 #[test]
126 fn extend_creates_delta() {
127 let mut dt = DeltaTracked::new(vec![1]);
128 dt.extend(vec![2, 3]);
129 assert_eq!(dt.take_delta(), vec![2, 3]);
130 }
131
132 #[test]
133 fn empty_then_push() {
134 let mut dt = DeltaTracked::empty();
135 dt.push(1);
136 dt.push(2);
137 assert!(dt.has_delta());
138 assert_eq!(dt.take_delta(), vec![1, 2]);
139 assert!(!dt.has_delta());
140 }
141
142 #[test]
143 fn multiple_takes() {
144 let mut dt = DeltaTracked::empty();
145 dt.push(1);
146 assert_eq!(dt.take_delta(), vec![1]);
147 dt.push(2);
148 dt.push(3);
149 assert_eq!(dt.take_delta(), vec![2, 3]);
150 assert_eq!(dt.take_delta(), Vec::<i32>::new());
151 }
152
153 #[test]
154 fn into_items() {
155 let mut dt = DeltaTracked::new(vec![1, 2]);
156 dt.push(3);
157 assert_eq!(dt.into_items(), vec![1, 2, 3]);
158 }
159
160 #[test]
161 fn len_and_is_empty() {
162 let mut dt = DeltaTracked::empty();
163 assert!(dt.is_empty());
164 assert_eq!(dt.len(), 0);
165 dt.push(1);
166 assert!(!dt.is_empty());
167 assert_eq!(dt.len(), 1);
168 }
169
170 #[test]
171 fn default_is_empty() {
172 let dt = DeltaTracked::<String>::default();
173 assert!(dt.is_empty());
174 assert!(!dt.has_delta());
175 }
176
177 #[test]
178 fn initial_count_stable_after_take_delta() {
179 let mut dt = DeltaTracked::new(vec![1, 2, 3]);
180 assert_eq!(dt.initial_count(), 3);
181
182 dt.push(4);
183 dt.push(5);
184 assert_eq!(dt.initial_count(), 3, "push must not change initial_count");
185
186 dt.take_delta();
187 assert_eq!(
188 dt.initial_count(),
189 3,
190 "take_delta must not change initial_count"
191 );
192
193 dt.push(6);
194 dt.take_delta();
195 assert_eq!(
196 dt.initial_count(),
197 3,
198 "multiple take_delta cycles must not change initial_count"
199 );
200 }
201
202 #[test]
203 fn initial_count_zero_for_empty() {
204 let mut dt = DeltaTracked::empty();
205 assert_eq!(dt.initial_count(), 0);
206 dt.push(1);
207 assert_eq!(dt.initial_count(), 0);
208 dt.take_delta();
209 assert_eq!(dt.initial_count(), 0);
210 }
211}