agentic_evolve_core/query/
delta.rs1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct VersionedState<T> {
9 data: Vec<T>,
11 version: u64,
13 last_modified: DateTime<Utc>,
15 #[serde(skip)]
17 change_log: Vec<ChangeEntry>,
18}
19
20#[derive(Debug, Clone)]
22struct ChangeEntry {
23 version: u64,
24 change_type: ChangeType,
25 index: usize,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
30pub enum ChangeType {
31 Created,
33 Updated,
35 Deleted,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub enum DeltaResult<T> {
42 Unchanged {
44 version: u64,
46 },
47 Changed {
49 items: Vec<T>,
51 deletions: usize,
53 from_version: u64,
55 to_version: u64,
57 },
58}
59
60impl<T: Clone> VersionedState<T> {
61 pub fn new() -> Self {
63 Self {
64 data: Vec::new(),
65 version: 0,
66 last_modified: Utc::now(),
67 change_log: Vec::new(),
68 }
69 }
70
71 pub fn from_data(data: Vec<T>) -> Self {
73 let len = data.len();
74 let mut state = Self {
75 data,
76 version: 1,
77 last_modified: Utc::now(),
78 change_log: Vec::new(),
79 };
80 for i in 0..len {
81 state.change_log.push(ChangeEntry {
82 version: 1,
83 change_type: ChangeType::Created,
84 index: i,
85 });
86 }
87 state
88 }
89
90 pub fn version(&self) -> u64 {
92 self.version
93 }
94
95 pub fn last_modified(&self) -> DateTime<Utc> {
97 self.last_modified
98 }
99
100 pub fn data(&self) -> &[T] {
102 &self.data
103 }
104
105 pub fn add(&mut self, item: T) {
107 self.version += 1;
108 self.last_modified = Utc::now();
109 self.data.push(item);
110 self.change_log.push(ChangeEntry {
111 version: self.version,
112 change_type: ChangeType::Created,
113 index: self.data.len() - 1,
114 });
115 }
116
117 pub fn update(&mut self, index: usize, item: T) {
119 if index < self.data.len() {
120 self.version += 1;
121 self.last_modified = Utc::now();
122 self.data[index] = item;
123 self.change_log.push(ChangeEntry {
124 version: self.version,
125 change_type: ChangeType::Updated,
126 index,
127 });
128 }
129 }
130
131 pub fn delete(&mut self, index: usize) {
133 if index < self.data.len() {
134 self.version += 1;
135 self.last_modified = Utc::now();
136 self.data.remove(index);
137 self.change_log.push(ChangeEntry {
138 version: self.version,
139 change_type: ChangeType::Deleted,
140 index,
141 });
142 }
143 }
144
145 pub fn changes_since_version(&self, since_version: u64) -> DeltaResult<T> {
150 if since_version >= self.version {
151 return DeltaResult::Unchanged {
152 version: self.version,
153 };
154 }
155
156 let relevant: Vec<&ChangeEntry> = self
157 .change_log
158 .iter()
159 .filter(|e| e.version > since_version)
160 .collect();
161
162 if relevant.is_empty() {
163 return DeltaResult::Unchanged {
164 version: self.version,
165 };
166 }
167
168 let mut items = Vec::new();
169 let mut deletions = 0usize;
170
171 for entry in &relevant {
172 match entry.change_type {
173 ChangeType::Created | ChangeType::Updated => {
174 if entry.index < self.data.len() {
175 items.push(self.data[entry.index].clone());
176 }
177 }
178 ChangeType::Deleted => {
179 deletions += 1;
180 }
181 }
182 }
183
184 DeltaResult::Changed {
185 items,
186 deletions,
187 from_version: since_version,
188 to_version: self.version,
189 }
190 }
191
192 pub fn is_unchanged_since(&self, since_version: u64) -> bool {
194 since_version >= self.version
195 }
196}
197
198impl<T: Clone> Default for VersionedState<T> {
199 fn default() -> Self {
200 Self::new()
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207
208 #[test]
209 fn new_state_version_zero() {
210 let state = VersionedState::<String>::new();
211 assert_eq!(state.version(), 0);
212 assert!(state.data().is_empty());
213 }
214
215 #[test]
216 fn from_data_version_one() {
217 let state = VersionedState::from_data(vec![1, 2, 3]);
218 assert_eq!(state.version(), 1);
219 assert_eq!(state.data().len(), 3);
220 }
221
222 #[test]
223 fn add_increments_version() {
224 let mut state = VersionedState::new();
225 state.add("hello".to_string());
226 assert_eq!(state.version(), 1);
227 state.add("world".to_string());
228 assert_eq!(state.version(), 2);
229 }
230
231 #[test]
232 fn changes_since_returns_unchanged_when_current() {
233 let state = VersionedState::from_data(vec![1, 2, 3]);
234 let result = state.changes_since_version(1);
235 assert!(matches!(result, DeltaResult::Unchanged { .. }));
236 }
237
238 #[test]
239 fn changes_since_returns_changed_items() {
240 let mut state = VersionedState::from_data(vec![1, 2, 3]);
241 state.add(4);
242 let result = state.changes_since_version(1);
243 match result {
244 DeltaResult::Changed {
245 items, to_version, ..
246 } => {
247 assert_eq!(items.len(), 1);
248 assert_eq!(items[0], 4);
249 assert_eq!(to_version, 2);
250 }
251 _ => panic!("Expected Changed"),
252 }
253 }
254
255 #[test]
256 fn is_unchanged_since_current_version() {
257 let state = VersionedState::from_data(vec![1]);
258 assert!(state.is_unchanged_since(1));
259 assert!(state.is_unchanged_since(99));
260 assert!(!state.is_unchanged_since(0));
261 }
262
263 #[test]
264 fn delete_increments_version() {
265 let mut state = VersionedState::from_data(vec![1, 2, 3]);
266 state.delete(0);
267 assert_eq!(state.version(), 2);
268 assert_eq!(state.data().len(), 2);
269 }
270
271 #[test]
272 fn update_tracks_change() {
273 let mut state = VersionedState::from_data(vec!["a".to_string()]);
274 state.update(0, "b".to_string());
275 assert_eq!(state.version(), 2);
276 assert_eq!(state.data()[0], "b");
277 }
278
279 #[test]
280 fn delta_proportional_to_changes() {
281 let mut state = VersionedState::from_data(vec![1, 2, 3, 4, 5]);
282 let baseline = state.version();
283 state.add(6);
284 state.add(7);
285 let result = state.changes_since_version(baseline);
286 match result {
287 DeltaResult::Changed { items, .. } => {
288 assert_eq!(items.len(), 2);
290 }
291 _ => panic!("Expected Changed"),
292 }
293 }
294}