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