1use chrono::{DateTime, Utc};
23use serde::de::{self, SeqAccess, Visitor};
24use serde::{Deserialize, Deserializer, Serialize, Serializer};
25use serde_json::Value;
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct Header {
29 #[serde(rename = "type")]
36 pub file_type: String,
37 pub version: u32,
38 pub created: DateTime<Utc>,
39 #[serde(skip_serializing_if = "Option::is_none")]
40 pub source: Option<String>,
41 pub initial: Value,
42 #[serde(skip_serializing_if = "Option::is_none")]
43 pub metadata: Option<Value>,
44}
45
46impl Header {
47 pub fn new(initial: Value, source: Option<String>) -> Self {
48 Self {
49 file_type: "@peoplesgrocers/json-archive".to_string(),
50 version: 1,
51 created: Utc::now(),
52 source,
53 initial,
54 metadata: None,
55 }
56 }
57}
58
59#[derive(Debug, Clone)]
60pub enum Event {
61 Observe {
62 observation_id: String,
63 timestamp: DateTime<Utc>,
64 change_count: usize,
65 },
66 Add {
67 path: String,
68 value: Value,
69 observation_id: String,
70 },
71 Change {
72 path: String,
73 new_value: Value,
74 observation_id: String,
75 },
76 Remove {
77 path: String,
78 observation_id: String,
79 },
80 Move {
81 path: String,
82 moves: Vec<(usize, usize)>,
83 observation_id: String,
84 },
85 Snapshot {
86 observation_id: String,
87 timestamp: DateTime<Utc>,
88 object: Value,
89 },
90}
91
92
93impl Serialize for Event {
94 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
95 where
96 S: Serializer,
97 {
98 use serde::ser::SerializeSeq;
99
100 match self {
101 Event::Observe {
102 observation_id,
103 timestamp,
104 change_count,
105 } => {
106 let mut seq = serializer.serialize_seq(Some(4))?;
107 seq.serialize_element("observe")?;
108 seq.serialize_element(observation_id)?;
109 seq.serialize_element(timestamp)?;
110 seq.serialize_element(change_count)?;
111 seq.end()
112 }
113 Event::Add {
114 path,
115 value,
116 observation_id,
117 } => {
118 let mut seq = serializer.serialize_seq(Some(4))?;
119 seq.serialize_element("add")?;
120 seq.serialize_element(path)?;
121 seq.serialize_element(value)?;
122 seq.serialize_element(observation_id)?;
123 seq.end()
124 }
125 Event::Change {
126 path,
127 new_value,
128 observation_id,
129 } => {
130 let mut seq = serializer.serialize_seq(Some(4))?;
131 seq.serialize_element("change")?;
132 seq.serialize_element(path)?;
133 seq.serialize_element(new_value)?;
134 seq.serialize_element(observation_id)?;
135 seq.end()
136 }
137 Event::Remove {
138 path,
139 observation_id,
140 } => {
141 let mut seq = serializer.serialize_seq(Some(3))?;
142 seq.serialize_element("remove")?;
143 seq.serialize_element(path)?;
144 seq.serialize_element(observation_id)?;
145 seq.end()
146 }
147 Event::Move {
148 path,
149 moves,
150 observation_id,
151 } => {
152 let mut seq = serializer.serialize_seq(Some(4))?;
153 seq.serialize_element("move")?;
154 seq.serialize_element(path)?;
155 seq.serialize_element(moves)?;
156 seq.serialize_element(observation_id)?;
157 seq.end()
158 }
159 Event::Snapshot {
160 observation_id,
161 timestamp,
162 object,
163 } => {
164 let mut seq = serializer.serialize_seq(Some(4))?;
165 seq.serialize_element("snapshot")?;
166 seq.serialize_element(observation_id)?;
167 seq.serialize_element(timestamp)?;
168 seq.serialize_element(object)?;
169 seq.end()
170 }
171 }
172 }
173}
174
175struct EventVisitor;
176
177impl<'de> Visitor<'de> for EventVisitor {
178 type Value = Event;
179
180 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
181 formatter.write_str("a JSON array representing an Event")
182 }
183
184 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
185 where
186 A: SeqAccess<'de>,
187 {
188 let event_type: String = seq
189 .next_element()?
190 .ok_or_else(|| de::Error::missing_field("event type"))?;
191
192 match event_type.as_str() {
193 "observe" => {
194 let observation_id: String = seq
195 .next_element()?
196 .ok_or_else(|| de::Error::missing_field("observation_id"))?;
197 let timestamp: DateTime<Utc> = seq
198 .next_element()?
199 .ok_or_else(|| de::Error::missing_field("timestamp"))?;
200 let change_count: usize = seq
201 .next_element()?
202 .ok_or_else(|| de::Error::missing_field("change_count"))?;
203 Ok(Event::Observe {
204 observation_id,
205 timestamp,
206 change_count,
207 })
208 }
209 "add" => {
210 let path: String = seq
211 .next_element()?
212 .ok_or_else(|| de::Error::missing_field("path"))?;
213 let value: Value = seq
214 .next_element()?
215 .ok_or_else(|| de::Error::missing_field("value"))?;
216 let observation_id: String = seq
217 .next_element()?
218 .ok_or_else(|| de::Error::missing_field("observation_id"))?;
219 Ok(Event::Add {
220 path,
221 value,
222 observation_id,
223 })
224 }
225 "change" => {
226 let path: String = seq
227 .next_element()?
228 .ok_or_else(|| de::Error::missing_field("path"))?;
229 let new_value: Value = seq
230 .next_element()?
231 .ok_or_else(|| de::Error::missing_field("new_value"))?;
232 let observation_id: String = seq
233 .next_element()?
234 .ok_or_else(|| de::Error::missing_field("observation_id"))?;
235 Ok(Event::Change {
236 path,
237 new_value,
238 observation_id,
239 })
240 }
241 "remove" => {
242 let path: String = seq
243 .next_element()?
244 .ok_or_else(|| de::Error::missing_field("path"))?;
245 let observation_id: String = seq
246 .next_element()?
247 .ok_or_else(|| de::Error::missing_field("observation_id"))?;
248 Ok(Event::Remove {
249 path,
250 observation_id,
251 })
252 }
253 "move" => {
254 let path: String = seq
255 .next_element()?
256 .ok_or_else(|| de::Error::missing_field("path"))?;
257 let moves: Vec<(usize, usize)> = seq
258 .next_element()?
259 .ok_or_else(|| de::Error::missing_field("moves"))?;
260 let observation_id: String = seq
261 .next_element()?
262 .ok_or_else(|| de::Error::missing_field("observation_id"))?;
263 Ok(Event::Move {
264 path,
265 moves,
266 observation_id,
267 })
268 }
269 "snapshot" => {
270 let observation_id: String = seq
271 .next_element()?
272 .ok_or_else(|| de::Error::missing_field("observation_id"))?;
273 let timestamp: DateTime<Utc> = seq
274 .next_element()?
275 .ok_or_else(|| de::Error::missing_field("timestamp"))?;
276 let object: Value = seq
277 .next_element()?
278 .ok_or_else(|| de::Error::missing_field("object"))?;
279 Ok(Event::Snapshot {
280 observation_id,
281 timestamp,
282 object,
283 })
284 }
285 _ => Err(de::Error::unknown_variant(
286 &event_type,
287 &["observe", "add", "change", "remove", "move", "snapshot"],
288 )),
289 }
290 }
291}
292
293impl<'de> Deserialize<'de> for Event {
294 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
295 where
296 D: Deserializer<'de>,
297 {
298 deserializer.deserialize_seq(EventVisitor)
299 }
300}
301
302#[derive(Debug, Clone)]
303pub struct Observation {
304 pub id: String,
305 pub timestamp: DateTime<Utc>,
306 pub events: Vec<Event>,
307}
308
309impl Observation {
310 pub fn new(id: String, timestamp: DateTime<Utc>) -> Self {
311 Self {
312 id,
313 timestamp,
314 events: Vec::new(),
315 }
316 }
317
318 pub fn add_event(&mut self, event: Event) {
319 self.events.push(event);
320 }
321
322 pub fn to_events(self) -> Vec<Event> {
323 let mut result = vec![Event::Observe {
324 observation_id: self.id.clone(),
325 timestamp: self.timestamp,
326 change_count: self.events.len(),
327 }];
328 result.extend(self.events);
329 result
330 }
331}
332
333#[cfg(test)]
334mod tests {
335 use super::*;
336 use serde_json::json;
337
338 #[test]
339 fn test_header_serialization() {
340 let header = Header::new(json!({"test": "value"}), Some("test-source".to_string()));
341 let serialized = serde_json::to_string(&header).unwrap();
342 let deserialized: Header = serde_json::from_str(&serialized).unwrap();
343
344 assert_eq!(header.file_type, deserialized.file_type);
345 assert_eq!(header.version, deserialized.version);
346 assert_eq!(header.initial, deserialized.initial);
347 assert_eq!(header.source, deserialized.source);
348 }
349
350 #[test]
351 fn test_event_serialization() {
352 let timestamp = Utc::now();
353
354 let observe_event = Event::Observe {
356 observation_id: "obs-1".to_string(),
357 timestamp,
358 change_count: 2,
359 };
360 let serialized = serde_json::to_string(&observe_event).unwrap();
361 let expected_array = json!(["observe", "obs-1", timestamp, 2]);
362 assert_eq!(
363 serde_json::from_str::<Value>(&serialized).unwrap(),
364 expected_array
365 );
366
367 let add_event = Event::Add {
369 path: "/test".to_string(),
370 value: json!("value"),
371 observation_id: "obs-1".to_string(),
372 };
373 let serialized = serde_json::to_string(&add_event).unwrap();
374 let expected_array = json!(["add", "/test", "value", "obs-1"]);
375 assert_eq!(
376 serde_json::from_str::<Value>(&serialized).unwrap(),
377 expected_array
378 );
379
380 let events = vec![
382 Event::Observe {
383 observation_id: "obs-1".to_string(),
384 timestamp,
385 change_count: 2,
386 },
387 Event::Add {
388 path: "/test".to_string(),
389 value: json!("value"),
390 observation_id: "obs-1".to_string(),
391 },
392 Event::Change {
393 path: "/test".to_string(),
394 new_value: json!("new"),
395 observation_id: "obs-1".to_string(),
396 },
397 Event::Remove {
398 path: "/test".to_string(),
399 observation_id: "obs-1".to_string(),
400 },
401 Event::Move {
402 path: "/items".to_string(),
403 moves: vec![(0, 1)],
404 observation_id: "obs-1".to_string(),
405 },
406 Event::Snapshot {
407 observation_id: "snap-1".to_string(),
408 timestamp,
409 object: json!({"test": "state"}),
410 },
411 ];
412
413 for event in events {
414 let serialized = serde_json::to_string(&event).unwrap();
415
416 let as_value: Value = serde_json::from_str(&serialized).unwrap();
418 assert!(as_value.is_array(), "Event should serialize to JSON array");
419
420 let deserialized: Event = serde_json::from_str(&serialized).unwrap();
422 let reserialized = serde_json::to_string(&deserialized).unwrap();
423 assert_eq!(
424 serialized, reserialized,
425 "Round-trip serialization should be identical"
426 );
427 }
428 }
429
430 #[test]
431 fn test_observation_to_events() {
432 let mut obs = Observation::new("obs-1".to_string(), Utc::now());
433 obs.add_event(Event::Add {
434 path: "/test".to_string(),
435 value: json!("value"),
436 observation_id: "obs-1".to_string(),
437 });
438 obs.add_event(Event::Change {
439 path: "/test".to_string(),
440 new_value: json!("new"),
441 observation_id: "obs-1".to_string(),
442 });
443
444 let events = obs.to_events();
445 assert_eq!(events.len(), 3); match &events[0] {
448 Event::Observe { change_count, .. } => assert_eq!(*change_count, 2),
449 _ => panic!("First event should be observe"),
450 }
451 }
452}