Skip to main content

moq_json/
diff.rs

1//! Generate an [RFC 7396](https://www.rfc-editor.org/rfc/rfc7396.html) JSON Merge Patch directly
2//! from a value, diffing it against the previously published value as it is serialized.
3//!
4//! A serde [`Serializer`] walks the new value and compares each field against the corresponding
5//! node of the old [`Value`], so unchanged scalars and subtrees cost only a comparison (no
6//! allocation) and only changed fields are written into reusable patch buffers. This avoids
7//! materializing a `Value` tree for either the new value or the patch on the encoding path.
8
9use std::cell::{Cell, RefCell};
10
11use serde::Serialize;
12use serde::ser::{Impossible, SerializeMap, SerializeSeq, SerializeStruct, Serializer};
13use serde_json::{Map, Value};
14
15/// The result of diffing a value into an RFC 7396 merge patch.
16pub struct Diff {
17	/// A merge patch that transforms the old value into the new one.
18	pub patch: Value,
19
20	/// Set when the change can't be faithfully expressed as a merge patch, so the caller should
21	/// publish a full snapshot instead. This happens when a value is set to JSON null, which merge
22	/// patch reads as a key deletion, or when the root is not an object. Arrays are fine: merge patch
23	/// replaces them wholesale, which is still typically smaller than a full snapshot.
24	pub forced_snapshot: bool,
25}
26
27/// Generate an RFC 7396 merge patch transforming `old` into `new`.
28///
29/// Only object roots produce a recursive patch; any other root forces a snapshot. A merge patch that
30/// would delete a key it shouldn't (a value genuinely set to null) also forces a snapshot.
31pub fn diff<T: Serialize>(old: &Value, new: &T) -> Diff {
32	let result = bytes(old, new, &RefCell::new(Scratch::default())).unwrap_or(PatchBytes {
33		patch: Vec::new(),
34		forced_snapshot: true,
35	});
36	Diff {
37		patch: if result.patch.is_empty() {
38			Value::Object(Map::new())
39		} else {
40			serde_json::from_slice(&result.patch).expect("the diff serializer emits JSON")
41		},
42		forced_snapshot: result.forced_snapshot,
43	}
44}
45
46#[derive(Default)]
47pub(crate) struct Scratch {
48	seen: Vec<Vec<String>>,
49	pending: Vec<String>,
50	bytes: Vec<Vec<u8>>,
51	last_capacity: usize,
52}
53
54impl Scratch {
55	fn buffer(&mut self, depth: usize) -> &mut Vec<u8> {
56		if self.bytes.len() <= depth {
57			self.bytes.resize_with(depth + 1, Vec::new);
58		}
59		&mut self.bytes[depth]
60	}
61}
62
63pub(crate) struct PatchBytes {
64	pub patch: Vec<u8>,
65	pub forced_snapshot: bool,
66}
67
68/// Diff directly into JSON bytes, reusing child buffers across updates.
69pub(crate) fn bytes<T: Serialize>(old: &Value, new: &T, scratch: &RefCell<Scratch>) -> Result<PatchBytes, String> {
70	let forced = Cell::new(false);
71	let node = new.serialize(Differ {
72		baseline: old,
73		present: true,
74		forced: &forced,
75		scratch,
76		depth: 0,
77	});
78	match node {
79		Ok(Node::Same) => Ok(PatchBytes {
80			patch: Vec::new(),
81			forced_snapshot: forced.get(),
82		}),
83		Ok(Node::Diff) => {
84			let mut scratch = scratch.borrow_mut();
85			let patch = std::mem::take(scratch.buffer(0));
86			scratch.last_capacity = patch.capacity();
87			let forced_snapshot = forced.get() || !patch.starts_with(b"{") || !old.is_object();
88			Ok(PatchBytes { patch, forced_snapshot })
89		}
90		Err(err) => Err(err.0),
91	}
92}
93
94/// One node's verdict from the diffing serializer.
95enum Node {
96	/// Equal to the baseline; nothing to emit.
97	Same,
98	/// Differs; the patch bytes are in this node's scratch buffer.
99	Diff,
100}
101
102const NULL: Value = Value::Null;
103
104/// Serializer that diffs `T` against `baseline` and yields a merge patch. `forced` is set if a
105/// genuine null is emitted (merge patch can't represent it, so the caller must snapshot).
106#[derive(Copy, Clone)]
107struct Differ<'a> {
108	baseline: &'a Value,
109	present: bool,
110	forced: &'a Cell<bool>,
111	scratch: &'a RefCell<Scratch>,
112	depth: usize,
113}
114
115impl<'a> Differ<'a> {
116	/// The baseline child for `key` and whether the baseline actually had that key (a missing key
117	/// means the field is an addition, which `MapDiff` uses to keep deletion detection cheap).
118	fn child(&self, key: &str) -> (Differ<'a>, bool) {
119		let (baseline, existed) = match self.baseline {
120			Value::Object(m) => match m.get(key) {
121				Some(value) => (value, true),
122				None => (&NULL, false),
123			},
124			_ => (&NULL, false),
125		};
126		(
127			Differ {
128				baseline,
129				present: existed,
130				forced: self.forced,
131				scratch: self.scratch,
132				depth: self.depth + 1,
133			},
134			existed,
135		)
136	}
137
138	/// Serialize a changed scalar into its reusable buffer.
139	fn scalar<T: Serialize + ?Sized>(self, value: &T, equal: bool, null: bool) -> Result<Node, Error> {
140		if equal {
141			return Ok(Node::Same);
142		}
143		if null {
144			self.forced.set(true);
145		}
146		let mut scratch = self.scratch.borrow_mut();
147		let capacity = scratch.last_capacity;
148		let bytes = scratch.buffer(self.depth);
149		bytes.clear();
150		if self.depth == 0 {
151			bytes.reserve(capacity);
152		}
153		serde_json::to_writer(bytes, value).map_err(|err| Error(err.to_string()))?;
154		Ok(Node::Diff)
155	}
156}
157
158/// Minimal serde error for the diffing serializer. JSON-shaped data never produces one in practice.
159#[derive(Debug)]
160struct Error(String);
161
162impl std::fmt::Display for Error {
163	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164		f.write_str(&self.0)
165	}
166}
167
168impl std::error::Error for Error {}
169
170impl serde::ser::Error for Error {
171	fn custom<M: std::fmt::Display>(msg: M) -> Self {
172		Error(msg.to_string())
173	}
174}
175
176/// Build a Value with no diffing (used for array elements, which merge patch replaces wholesale).
177fn to_plain<T: Serialize + ?Sized>(value: &T) -> Result<Value, Error> {
178	serde_json::to_value(value).map_err(|e| Error(e.to_string()))
179}
180
181impl<'a> Serializer for Differ<'a> {
182	type Ok = Node;
183	type Error = Error;
184	type SerializeSeq = SeqDiff<'a>;
185	type SerializeTuple = SeqDiff<'a>;
186	type SerializeTupleStruct = SeqDiff<'a>;
187	type SerializeTupleVariant = VariantSeq<'a>;
188	type SerializeMap = MapDiff<'a>;
189	type SerializeStruct = MapDiff<'a>;
190	type SerializeStructVariant = VariantMap<'a>;
191
192	fn serialize_bool(self, v: bool) -> Result<Node, Error> {
193		self.scalar(&v, self.baseline == &Value::Bool(v), false)
194	}
195	fn serialize_i8(self, v: i8) -> Result<Node, Error> {
196		self.scalar(&v, self.baseline == &Value::from(v), false)
197	}
198	fn serialize_i16(self, v: i16) -> Result<Node, Error> {
199		self.scalar(&v, self.baseline == &Value::from(v), false)
200	}
201	fn serialize_i32(self, v: i32) -> Result<Node, Error> {
202		self.scalar(&v, self.baseline == &Value::from(v), false)
203	}
204	fn serialize_i64(self, v: i64) -> Result<Node, Error> {
205		self.scalar(&v, self.baseline == &Value::from(v), false)
206	}
207	fn serialize_i128(self, v: i128) -> Result<Node, Error> {
208		{
209			let plain = to_plain(&v)?;
210			self.scalar(&plain, self.baseline == &plain, false)
211		}
212	}
213	fn serialize_u8(self, v: u8) -> Result<Node, Error> {
214		self.scalar(&v, self.baseline == &Value::from(v), false)
215	}
216	fn serialize_u16(self, v: u16) -> Result<Node, Error> {
217		self.scalar(&v, self.baseline == &Value::from(v), false)
218	}
219	fn serialize_u32(self, v: u32) -> Result<Node, Error> {
220		self.scalar(&v, self.baseline == &Value::from(v), false)
221	}
222	fn serialize_u64(self, v: u64) -> Result<Node, Error> {
223		self.scalar(&v, self.baseline == &Value::from(v), false)
224	}
225	fn serialize_u128(self, v: u128) -> Result<Node, Error> {
226		{
227			let plain = to_plain(&v)?;
228			self.scalar(&plain, self.baseline == &plain, false)
229		}
230	}
231	fn serialize_f32(self, v: f32) -> Result<Node, Error> {
232		self.scalar(&v, self.baseline == &Value::from(v), false)
233	}
234	fn serialize_f64(self, v: f64) -> Result<Node, Error> {
235		self.scalar(&v, self.baseline == &Value::from(v), false)
236	}
237	fn serialize_char(self, v: char) -> Result<Node, Error> {
238		let mut utf8 = [0; 4];
239		self.scalar(&v, self.baseline.as_str() == Some(v.encode_utf8(&mut utf8)), false)
240	}
241	fn serialize_str(self, v: &str) -> Result<Node, Error> {
242		// Strings are the common churn-free field, so compare against the baseline without allocating a
243		// `Value::String` on the unchanged path.
244		if matches!(self.baseline, Value::String(b) if b == v) {
245			Ok(Node::Same)
246		} else {
247			self.scalar(&v, false, false)
248		}
249	}
250	fn serialize_bytes(self, v: &[u8]) -> Result<Node, Error> {
251		let mut seq = self.serialize_seq(Some(v.len()))?;
252		for byte in v {
253			SerializeSeq::serialize_element(&mut seq, byte)?;
254		}
255		SerializeSeq::end(seq)
256	}
257	fn serialize_none(self) -> Result<Node, Error> {
258		self.scalar(&Value::Null, self.present && self.baseline.is_null(), true)
259	}
260	fn serialize_some<T: Serialize + ?Sized>(self, value: &T) -> Result<Node, Error> {
261		value.serialize(self)
262	}
263	fn serialize_unit(self) -> Result<Node, Error> {
264		self.scalar(&Value::Null, self.present && self.baseline.is_null(), true)
265	}
266	fn serialize_unit_struct(self, _name: &'static str) -> Result<Node, Error> {
267		self.scalar(&Value::Null, self.present && self.baseline.is_null(), true)
268	}
269	fn serialize_unit_variant(self, _name: &'static str, _idx: u32, variant: &'static str) -> Result<Node, Error> {
270		self.scalar(&variant, self.baseline.as_str() == Some(variant), false)
271	}
272	fn serialize_newtype_struct<T: Serialize + ?Sized>(self, _name: &'static str, value: &T) -> Result<Node, Error> {
273		value.serialize(self)
274	}
275	fn serialize_newtype_variant<T: Serialize + ?Sized>(
276		self,
277		_name: &'static str,
278		_idx: u32,
279		variant: &'static str,
280		value: &T,
281	) -> Result<Node, Error> {
282		// An externally-tagged newtype variant serializes as `{ "Variant": value }`. Diff that object
283		// against the baseline like any other object, so the tag is preserved and the payload diffs
284		// minimally (a variant switch deletes the old tag and adds the new one).
285		Variant { name: variant, value }.serialize(self)
286	}
287	fn serialize_seq(self, len: Option<usize>) -> Result<SeqDiff<'a>, Error> {
288		let _ = len;
289		self.scratch.borrow_mut().buffer(self.depth).clear();
290		Ok(SeqDiff {
291			differ: self,
292			changed: false,
293			len: 0,
294		})
295	}
296	fn serialize_tuple(self, len: usize) -> Result<SeqDiff<'a>, Error> {
297		self.serialize_seq(Some(len))
298	}
299	fn serialize_tuple_struct(self, _name: &'static str, len: usize) -> Result<SeqDiff<'a>, Error> {
300		self.serialize_seq(Some(len))
301	}
302	fn serialize_tuple_variant(
303		self,
304		_name: &'static str,
305		_idx: u32,
306		variant: &'static str,
307		len: usize,
308	) -> Result<VariantSeq<'a>, Error> {
309		// A tuple variant serializes as `{ "Variant": [..] }`, replaced wholesale.
310		Ok(VariantSeq {
311			differ: self,
312			variant,
313			items: self.child(variant).0.serialize_seq(Some(len))?,
314		})
315	}
316	fn serialize_map(self, _len: Option<usize>) -> Result<MapDiff<'a>, Error> {
317		self.scratch.borrow_mut().buffer(self.depth).clear();
318		Ok(MapDiff {
319			differ: self,
320			entries: 0,
321			seen_len: 0,
322			ordered: true,
323			added_key: false,
324		})
325	}
326	fn serialize_struct(self, _name: &'static str, len: usize) -> Result<MapDiff<'a>, Error> {
327		self.serialize_map(Some(len))
328	}
329	fn serialize_struct_variant(
330		self,
331		_name: &'static str,
332		_idx: u32,
333		variant: &'static str,
334		_len: usize,
335	) -> Result<VariantMap<'a>, Error> {
336		// A struct variant serializes as `{ "Variant": { .. } }`, replaced wholesale.
337		Ok(VariantMap {
338			differ: self,
339			variant,
340			fields: self.child(variant).0.serialize_map(Some(_len))?,
341		})
342	}
343}
344
345/// Serialize an externally tagged newtype without materializing its payload.
346struct Variant<'a, T: ?Sized> {
347	name: &'static str,
348	value: &'a T,
349}
350
351impl<T: Serialize + ?Sized> Serialize for Variant<'_, T> {
352	fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
353		let mut map = serializer.serialize_map(Some(1))?;
354		map.serialize_entry(self.name, self.value)?;
355		map.end()
356	}
357}
358
359/// Finish an externally tagged variant from the already serialized child patch.
360fn finish_variant(differ: Differ<'_>, variant: &'static str, node: Node) -> Result<Node, Error> {
361	let (_, existed) = differ.child(variant);
362	let mut outer = differ.serialize_map(Some(1))?;
363	outer.entry(variant, existed, node)?;
364	SerializeMap::end(outer)
365}
366
367struct VariantSeq<'a> {
368	differ: Differ<'a>,
369	variant: &'static str,
370	items: SeqDiff<'a>,
371}
372
373impl serde::ser::SerializeTupleVariant for VariantSeq<'_> {
374	type Ok = Node;
375	type Error = Error;
376	fn serialize_field<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
377		SerializeSeq::serialize_element(&mut self.items, value)
378	}
379	fn end(self) -> Result<Node, Error> {
380		finish_variant(self.differ, self.variant, SerializeSeq::end(self.items)?)
381	}
382}
383
384struct VariantMap<'a> {
385	differ: Differ<'a>,
386	variant: &'static str,
387	fields: MapDiff<'a>,
388}
389
390impl serde::ser::SerializeStructVariant for VariantMap<'_> {
391	type Ok = Node;
392	type Error = Error;
393	fn serialize_field<T: Serialize + ?Sized>(&mut self, key: &'static str, value: &T) -> Result<(), Error> {
394		SerializeStruct::serialize_field(&mut self.fields, key, value)
395	}
396	fn end(self) -> Result<Node, Error> {
397		finish_variant(self.differ, self.variant, SerializeStruct::end(self.fields)?)
398	}
399}
400
401/// Arrays are replaced wholesale by merge patch, but an unchanged array needs no copy.
402struct SeqDiff<'a> {
403	differ: Differ<'a>,
404	changed: bool,
405	len: usize,
406}
407
408impl SeqDiff<'_> {
409	fn begin(&mut self, old: &[Value]) -> Result<(), Error> {
410		let mut scratch = self.differ.scratch.borrow_mut();
411		let capacity = scratch.last_capacity;
412		let bytes = scratch.buffer(self.differ.depth);
413		bytes.clear();
414		if self.differ.depth == 0 {
415			bytes.reserve(capacity);
416		}
417		bytes.push(b'[');
418		for item in &old[..self.len] {
419			if bytes.len() > 1 {
420				bytes.push(b',');
421			}
422			serde_json::to_writer(&mut *bytes, item).map_err(|err| Error(err.to_string()))?;
423		}
424		self.changed = true;
425		Ok(())
426	}
427}
428
429impl SerializeSeq for SeqDiff<'_> {
430	type Ok = Node;
431	type Error = Error;
432	fn serialize_element<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
433		let old = self.differ.baseline.as_array();
434		if !self.changed {
435			let baseline = old.and_then(|old| old.get(self.len)).unwrap_or(&NULL);
436			// Null inside an array is data, not a merge-patch deletion.
437			let forced = Cell::new(false);
438			let equal = matches!(
439				value.serialize(Differ {
440					baseline,
441					present: true,
442					forced: &forced,
443					scratch: self.differ.scratch,
444					depth: self.differ.depth + 1,
445				})?,
446				Node::Same
447			) && old.is_some_and(|old| self.len < old.len());
448			if !equal {
449				self.begin(old.map(Vec::as_slice).unwrap_or(&[]))?;
450			}
451		}
452		if self.changed {
453			let mut scratch = self.differ.scratch.borrow_mut();
454			let bytes = scratch.buffer(self.differ.depth);
455			if bytes.len() > 1 {
456				bytes.push(b',');
457			}
458			serde_json::to_writer(bytes, value).map_err(|err| Error(err.to_string()))?;
459		}
460		self.len += 1;
461		Ok(())
462	}
463	fn end(mut self) -> Result<Node, Error> {
464		if !self.changed {
465			if let Some(old) = self.differ.baseline.as_array() {
466				if self.len == old.len() {
467					return Ok(Node::Same);
468				}
469				self.begin(old)?;
470			} else {
471				self.begin(&[])?;
472			}
473		}
474		self.differ.scratch.borrow_mut().buffer(self.differ.depth).push(b']');
475		Ok(Node::Diff)
476	}
477}
478
479impl serde::ser::SerializeTuple for SeqDiff<'_> {
480	type Ok = Node;
481	type Error = Error;
482	fn serialize_element<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
483		SerializeSeq::serialize_element(self, value)
484	}
485	fn end(self) -> Result<Node, Error> {
486		SerializeSeq::end(self)
487	}
488}
489
490impl serde::ser::SerializeTupleStruct for SeqDiff<'_> {
491	type Ok = Node;
492	type Error = Error;
493	fn serialize_field<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
494		SerializeSeq::serialize_element(self, value)
495	}
496	fn end(self) -> Result<Node, Error> {
497		SerializeSeq::end(self)
498	}
499}
500
501/// Objects recurse; only changed entries are written into the reusable patch buffer.
502struct MapDiff<'a> {
503	differ: Differ<'a>,
504	entries: usize,
505	seen_len: usize,
506	ordered: bool,
507	added_key: bool,
508}
509
510impl MapDiff<'_> {
511	fn write_entry(&mut self, key: &str, child: Option<usize>) -> Result<(), Error> {
512		let depth = self.differ.depth;
513		let mut scratch = self.differ.scratch.borrow_mut();
514		let capacity = scratch.last_capacity;
515		let bytes = scratch.buffer(depth);
516		if self.entries == 0 {
517			if depth == 0 {
518				bytes.reserve(capacity);
519			}
520			bytes.push(b'{');
521		} else {
522			bytes.push(b',');
523		}
524		serde_json::to_writer(&mut *bytes, key).map_err(|err| Error(err.to_string()))?;
525		bytes.push(b':');
526		if let Some(child) = child {
527			let (parents, children) = scratch.bytes.split_at_mut(child);
528			parents[depth].extend_from_slice(&children[0]);
529		} else {
530			scratch.bytes[depth].extend_from_slice(b"null");
531		}
532		self.entries += 1;
533		Ok(())
534	}
535
536	fn entry(&mut self, key: &str, existed: bool, node: Node) -> Result<(), Error> {
537		self.added_key |= !existed;
538		if let Node::Diff = node {
539			self.write_entry(key, Some(self.differ.depth + 1))?;
540		}
541		let mut scratch = self.differ.scratch.borrow_mut();
542		if scratch.seen.len() <= self.differ.depth {
543			scratch.seen.resize_with(self.differ.depth + 1, Vec::new);
544		}
545		let seen = &mut scratch.seen[self.differ.depth];
546		if self.seen_len > 0 {
547			match seen[self.seen_len - 1].as_str().cmp(key) {
548				std::cmp::Ordering::Equal => return Err(Error("duplicate JSON object key".into())),
549				std::cmp::Ordering::Greater => self.ordered = false,
550				std::cmp::Ordering::Less => {}
551			}
552		}
553		if self.seen_len == seen.len() {
554			seen.push(key.to_owned());
555		} else {
556			seen[self.seen_len].clear();
557			seen[self.seen_len].push_str(key);
558		}
559		self.seen_len += 1;
560		Ok(())
561	}
562
563	fn finish(mut self) -> Result<Node, Error> {
564		if !self.ordered {
565			let mut scratch = self.differ.scratch.borrow_mut();
566			let seen = &mut scratch.seen[self.differ.depth][..self.seen_len];
567			seen.sort_unstable();
568			if seen.windows(2).any(|pair| pair[0] == pair[1]) {
569				return Err(Error("duplicate JSON object key".into()));
570			}
571		}
572		if let Value::Object(base) = self.differ.baseline
573			&& (self.added_key || self.seen_len != base.len())
574		{
575			let depth = self.differ.depth;
576			let mut scratch = self.differ.scratch.borrow_mut();
577			let Scratch {
578				seen,
579				bytes,
580				last_capacity,
581				..
582			} = &mut *scratch;
583			let seen = &mut seen[depth][..self.seen_len];
584			let out = &mut bytes[depth];
585			if self.ordered {
586				seen.sort_unstable();
587			}
588			for key in base.keys() {
589				if seen.binary_search_by(|seen| seen.as_str().cmp(key)).is_err() {
590					if self.entries == 0 {
591						if depth == 0 {
592							out.reserve(*last_capacity);
593						}
594						out.push(b'{');
595					} else {
596						out.push(b',');
597					}
598					serde_json::to_writer(&mut *out, key).map_err(|err| Error(err.to_string()))?;
599					out.extend_from_slice(b":null");
600					self.entries += 1;
601				}
602			}
603		}
604		if self.entries == 0 {
605			if self.differ.baseline.is_object() {
606				return Ok(Node::Same);
607			}
608			self.differ
609				.scratch
610				.borrow_mut()
611				.buffer(self.differ.depth)
612				.extend_from_slice(b"{}");
613			return Ok(Node::Diff);
614		}
615		self.differ.scratch.borrow_mut().buffer(self.differ.depth).push(b'}');
616		Ok(Node::Diff)
617	}
618}
619
620impl SerializeMap for MapDiff<'_> {
621	type Ok = Node;
622	type Error = Error;
623	fn serialize_key<T: Serialize + ?Sized>(&mut self, key: &T) -> Result<(), Error> {
624		let mut scratch = self.differ.scratch.borrow_mut();
625		if scratch.pending.len() <= self.differ.depth {
626			scratch.pending.resize_with(self.differ.depth + 1, String::new);
627		}
628		let pending = &mut scratch.pending[self.differ.depth];
629		pending.clear();
630		key.serialize(KeySer(pending))
631	}
632	fn serialize_value<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
633		let depth = self.differ.depth;
634		let key = std::mem::take(&mut self.differ.scratch.borrow_mut().pending[depth]);
635		let (child, existed) = self.differ.child(&key);
636		let node = value.serialize(child)?;
637		self.entry(&key, existed, node)?;
638		self.differ.scratch.borrow_mut().pending[depth] = key;
639		Ok(())
640	}
641	fn end(self) -> Result<Node, Error> {
642		self.finish()
643	}
644}
645
646impl SerializeStruct for MapDiff<'_> {
647	type Ok = Node;
648	type Error = Error;
649	fn serialize_field<T: Serialize + ?Sized>(&mut self, key: &'static str, value: &T) -> Result<(), Error> {
650		let (child, existed) = self.differ.child(key);
651		let node = value.serialize(child)?;
652		self.entry(key, existed, node)?;
653		Ok(())
654	}
655	// A field skipped via `skip_serializing_if` is simply never offered here, so it stays out of `seen`
656	// and `finish` emits it as a null deletion if the baseline had it (the default `skip_field` suffices).
657	fn end(self) -> Result<Node, Error> {
658		self.finish()
659	}
660}
661
662/// Serializes a map key to its `String`, the only form JSON object keys take. Anything else is an
663/// error, mirroring `serde_json`'s own key handling.
664struct KeySer<'a>(&'a mut String);
665
666impl Serializer for KeySer<'_> {
667	type Ok = ();
668	type Error = Error;
669	type SerializeSeq = Impossible<(), Error>;
670	type SerializeTuple = Impossible<(), Error>;
671	type SerializeTupleStruct = Impossible<(), Error>;
672	type SerializeTupleVariant = Impossible<(), Error>;
673	type SerializeMap = Impossible<(), Error>;
674	type SerializeStruct = Impossible<(), Error>;
675	type SerializeStructVariant = Impossible<(), Error>;
676
677	fn serialize_str(self, v: &str) -> Result<(), Error> {
678		{
679			self.0.push_str(v);
680			Ok(())
681		}
682	}
683	fn serialize_char(self, v: char) -> Result<(), Error> {
684		{
685			use std::fmt::Write;
686			write!(self.0, "{v}").expect("writing to String cannot fail");
687			Ok(())
688		}
689	}
690	fn serialize_bool(self, v: bool) -> Result<(), Error> {
691		{
692			use std::fmt::Write;
693			write!(self.0, "{v}").expect("writing to String cannot fail");
694			Ok(())
695		}
696	}
697	fn serialize_i8(self, v: i8) -> Result<(), Error> {
698		{
699			use std::fmt::Write;
700			write!(self.0, "{v}").expect("writing to String cannot fail");
701			Ok(())
702		}
703	}
704	fn serialize_i16(self, v: i16) -> Result<(), Error> {
705		{
706			use std::fmt::Write;
707			write!(self.0, "{v}").expect("writing to String cannot fail");
708			Ok(())
709		}
710	}
711	fn serialize_i32(self, v: i32) -> Result<(), Error> {
712		{
713			use std::fmt::Write;
714			write!(self.0, "{v}").expect("writing to String cannot fail");
715			Ok(())
716		}
717	}
718	fn serialize_i64(self, v: i64) -> Result<(), Error> {
719		{
720			use std::fmt::Write;
721			write!(self.0, "{v}").expect("writing to String cannot fail");
722			Ok(())
723		}
724	}
725	fn serialize_u8(self, v: u8) -> Result<(), Error> {
726		{
727			use std::fmt::Write;
728			write!(self.0, "{v}").expect("writing to String cannot fail");
729			Ok(())
730		}
731	}
732	fn serialize_u16(self, v: u16) -> Result<(), Error> {
733		{
734			use std::fmt::Write;
735			write!(self.0, "{v}").expect("writing to String cannot fail");
736			Ok(())
737		}
738	}
739	fn serialize_u32(self, v: u32) -> Result<(), Error> {
740		{
741			use std::fmt::Write;
742			write!(self.0, "{v}").expect("writing to String cannot fail");
743			Ok(())
744		}
745	}
746	fn serialize_u64(self, v: u64) -> Result<(), Error> {
747		{
748			use std::fmt::Write;
749			write!(self.0, "{v}").expect("writing to String cannot fail");
750			Ok(())
751		}
752	}
753	fn serialize_unit_variant(self, _name: &'static str, _idx: u32, variant: &'static str) -> Result<(), Error> {
754		{
755			self.0.push_str(variant);
756			Ok(())
757		}
758	}
759	fn serialize_newtype_struct<T: Serialize + ?Sized>(self, _name: &'static str, value: &T) -> Result<(), Error> {
760		value.serialize(self)
761	}
762	fn serialize_some<T: Serialize + ?Sized>(self, value: &T) -> Result<(), Error> {
763		value.serialize(self)
764	}
765	fn serialize_f32(self, _v: f32) -> Result<(), Error> {
766		Err(Error("float map key".into()))
767	}
768	fn serialize_f64(self, _v: f64) -> Result<(), Error> {
769		Err(Error("float map key".into()))
770	}
771	fn serialize_bytes(self, _v: &[u8]) -> Result<(), Error> {
772		Err(Error("bytes map key".into()))
773	}
774	fn serialize_none(self) -> Result<(), Error> {
775		Err(Error("null map key".into()))
776	}
777	fn serialize_unit(self) -> Result<(), Error> {
778		Err(Error("unit map key".into()))
779	}
780	fn serialize_unit_struct(self, _name: &'static str) -> Result<(), Error> {
781		Err(Error("unit struct map key".into()))
782	}
783	fn serialize_newtype_variant<T: Serialize + ?Sized>(
784		self,
785		_name: &'static str,
786		_idx: u32,
787		_variant: &'static str,
788		_value: &T,
789	) -> Result<(), Error> {
790		Err(Error("newtype variant map key".into()))
791	}
792	fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Error> {
793		Err(Error("seq map key".into()))
794	}
795	fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Error> {
796		Err(Error("tuple map key".into()))
797	}
798	fn serialize_tuple_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeTupleStruct, Error> {
799		Err(Error("tuple struct map key".into()))
800	}
801	fn serialize_tuple_variant(
802		self,
803		_name: &'static str,
804		_idx: u32,
805		_variant: &'static str,
806		_len: usize,
807	) -> Result<Self::SerializeTupleVariant, Error> {
808		Err(Error("tuple variant map key".into()))
809	}
810	fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Error> {
811		Err(Error("map map key".into()))
812	}
813	fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct, Error> {
814		Err(Error("struct map key".into()))
815	}
816	fn serialize_struct_variant(
817		self,
818		_name: &'static str,
819		_idx: u32,
820		_variant: &'static str,
821		_len: usize,
822	) -> Result<Self::SerializeStructVariant, Error> {
823		Err(Error("struct variant map key".into()))
824	}
825}
826
827#[cfg(test)]
828mod test {
829	use super::*;
830	use serde_json::json;
831
832	/// A straightforward Value-vs-Value merge-patch diff, used only as a test oracle: the production
833	/// `diff` (the serializer) must agree with it on every case.
834	fn reference(old: &Value, new: &Value) -> Diff {
835		fn objects(
836			old: &Map<String, Value>,
837			new: &Map<String, Value>,
838			patch: &mut Map<String, Value>,
839			forced: &mut bool,
840		) {
841			for key in old.keys() {
842				if !new.contains_key(key) {
843					patch.insert(key.clone(), Value::Null);
844				}
845			}
846			for (key, new_val) in new {
847				let old_val = old.get(key);
848				if old_val == Some(new_val) {
849					continue;
850				}
851				if let (Some(Value::Object(old_obj)), Value::Object(new_obj)) = (old_val, new_val) {
852					let mut sub = Map::new();
853					objects(old_obj, new_obj, &mut sub, forced);
854					if !sub.is_empty() {
855						patch.insert(key.clone(), Value::Object(sub));
856					}
857					continue;
858				}
859				if new_val.is_null() {
860					*forced = true;
861				}
862				patch.insert(key.clone(), new_val.clone());
863			}
864		}
865
866		if let (Value::Object(old_obj), Value::Object(new_obj)) = (old, new) {
867			let mut patch = Map::new();
868			let mut forced = false;
869			objects(old_obj, new_obj, &mut patch, &mut forced);
870			Diff {
871				patch: Value::Object(patch),
872				forced_snapshot: forced,
873			}
874		} else {
875			Diff {
876				patch: new.clone(),
877				forced_snapshot: true,
878			}
879		}
880	}
881
882	/// The serializer must produce the same patch and forced flag as the reference oracle, and (when
883	/// not forced) applying the patch to `old` must reproduce `new`.
884	fn check(old: Value, new: Value) {
885		let want = reference(&old, &new);
886		let got = diff(&old, &new);
887		assert_eq!(got.patch, want.patch, "patch mismatch for {old} -> {new}");
888		assert_eq!(
889			got.forced_snapshot, want.forced_snapshot,
890			"forced mismatch for {old} -> {new}"
891		);
892		if !got.forced_snapshot {
893			let mut applied = old.clone();
894			json_patch::merge(&mut applied, &got.patch);
895			assert_eq!(applied, new, "patch did not roundtrip for {old} -> {new}");
896		}
897	}
898
899	#[test]
900	fn replacing_scalar_with_empty_object_is_a_change() {
901		check(json!({ "value": 1 }), json!({ "value": {} }));
902		check(json!({ "value": null }), json!({ "value": {} }));
903		let result = diff(&json!(1), &json!({}));
904		assert!(result.forced_snapshot);
905		assert_eq!(result.patch, json!({}));
906	}
907
908	#[test]
909	fn duplicate_serialized_map_keys_force_a_snapshot() {
910		struct Duplicate;
911		impl Serialize for Duplicate {
912			fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
913				let mut map = serializer.serialize_map(Some(3))?;
914				map.serialize_entry("key", &1)?;
915				map.serialize_entry("other", &2)?;
916				map.serialize_entry("key", &3)?;
917				map.end()
918			}
919		}
920		assert!(diff(&json!({ "key": 1, "other": 2 }), &Duplicate).forced_snapshot);
921	}
922
923	#[test]
924	fn changed_scalar() {
925		check(json!({ "a": 1, "b": 2 }), json!({ "a": 1, "b": 3 }));
926	}
927
928	#[test]
929	fn added_key() {
930		let result = diff(&json!({ "a": 1 }), &json!({ "a": 1, "b": 2 }));
931		assert!(!result.forced_snapshot);
932		assert_eq!(result.patch, json!({ "b": 2 }));
933		check(json!({ "a": 1 }), json!({ "a": 1, "b": 2 }));
934	}
935
936	#[test]
937	fn added_null_key_forces_snapshot() {
938		check(json!({ "a": 1 }), json!({ "a": 1, "x": null }));
939		check(
940			json!({ "items": [{ "a": 1 }] }),
941			json!({ "items": [{ "a": 1, "x": null }] }),
942		);
943	}
944
945	#[test]
946	fn removed_key_is_null() {
947		let result = diff(&json!({ "a": 1, "b": 2 }), &json!({ "a": 1 }));
948		assert!(!result.forced_snapshot, "removing a key is a clean delete");
949		assert_eq!(result.patch, json!({ "b": null }));
950		check(json!({ "a": 1, "b": 2 }), json!({ "a": 1 }));
951	}
952
953	#[test]
954	fn nested_object_only_includes_changed_keys() {
955		let result = diff(&json!({ "o": { "x": 1, "y": 2 } }), &json!({ "o": { "x": 1, "y": 9 } }));
956		assert!(!result.forced_snapshot);
957		assert_eq!(result.patch, json!({ "o": { "y": 9 } }));
958		check(json!({ "o": { "x": 1, "y": 2 } }), json!({ "o": { "x": 1, "y": 9 } }));
959	}
960
961	#[test]
962	fn unchanged_object_is_empty_patch() {
963		let result = diff(&json!({ "a": 1, "o": { "x": 1 } }), &json!({ "a": 1, "o": { "x": 1 } }));
964		assert!(!result.forced_snapshot);
965		assert_eq!(result.patch, json!({}));
966	}
967
968	#[test]
969	fn changed_array_is_wholesale_delta() {
970		let result = diff(&json!({ "a": [1, 2] }), &json!({ "a": [1, 2, 3] }));
971		assert!(!result.forced_snapshot);
972		assert_eq!(result.patch, json!({ "a": [1, 2, 3] }));
973		check(json!({ "a": [1, 2] }), json!({ "a": [1, 2, 3] }));
974	}
975
976	#[test]
977	fn unchanged_array_is_pruned() {
978		let result = diff(&json!({ "a": [1, 2, 3], "b": 1 }), &json!({ "a": [1, 2, 3], "b": 2 }));
979		assert_eq!(
980			result.patch,
981			json!({ "b": 2 }),
982			"an unchanged array stays out of the patch"
983		);
984	}
985
986	#[test]
987	fn added_array_is_delta() {
988		check(json!({ "a": 1 }), json!({ "a": 1, "b": [1] }));
989	}
990
991	#[test]
992	fn nested_array_is_delta() {
993		check(json!({ "o": { "x": 1 } }), json!({ "o": { "x": 1, "list": [1] } }));
994	}
995
996	#[test]
997	fn array_of_objects_replaces_wholesale() {
998		check(
999			json!({ "items": [{ "id": 1, "v": 1 }, { "id": 2, "v": 2 }] }),
1000			json!({ "items": [{ "id": 1, "v": 9 }, { "id": 2, "v": 2 }] }),
1001		);
1002	}
1003
1004	#[test]
1005	fn set_to_null_forces_snapshot() {
1006		// A genuine null value can't be represented: merge patch would delete the key.
1007		let result = diff(&json!({ "a": 1 }), &json!({ "a": null }));
1008		assert!(result.forced_snapshot);
1009		assert!(reference(&json!({ "a": 1 }), &json!({ "a": null })).forced_snapshot);
1010	}
1011
1012	#[test]
1013	fn nested_null_forces_snapshot() {
1014		let old = json!({ "o": { "x": 1 } });
1015		let new = json!({ "o": { "x": null } });
1016		assert!(diff(&old, &new).forced_snapshot);
1017		assert_eq!(diff(&old, &new).forced_snapshot, reference(&old, &new).forced_snapshot);
1018	}
1019
1020	#[test]
1021	fn replacing_object_with_scalar() {
1022		check(json!({ "a": { "x": 1 } }), json!({ "a": 5 }));
1023	}
1024
1025	#[test]
1026	fn replacing_scalar_with_object() {
1027		check(json!({ "a": 5 }), json!({ "a": { "x": 1 } }));
1028	}
1029
1030	#[test]
1031	fn non_object_root_forces_snapshot() {
1032		let result = diff(&json!(1), &json!(2));
1033		assert!(result.forced_snapshot);
1034		assert_eq!(result.patch, json!(2));
1035	}
1036
1037	#[test]
1038	fn array_root_forces_snapshot() {
1039		let result = diff(&json!([1, 2]), &json!([1, 2, 3]));
1040		assert!(result.forced_snapshot);
1041		assert_eq!(result.patch, json!([1, 2, 3]));
1042	}
1043
1044	#[test]
1045	fn unchanged_scalar_root_is_not_forced() {
1046		// An equal non-object root is a no-op (empty patch), matching the producer's dedup.
1047		let result = diff(&json!(7), &json!(7));
1048		assert!(!result.forced_snapshot);
1049		assert_eq!(result.patch, json!({}));
1050	}
1051
1052	#[test]
1053	fn floats_and_bools_and_strings() {
1054		check(
1055			json!({ "f": 1.5, "b": true, "s": "hi" }),
1056			json!({ "f": 2.5, "b": false, "s": "bye" }),
1057		);
1058	}
1059
1060	// ---- Typed structs (the serializer's whole point: diff `T` without building its Value) ----
1061
1062	#[derive(serde::Serialize, serde::Deserialize, Default, PartialEq, Debug)]
1063	struct Doc {
1064		#[serde(skip_serializing_if = "Option::is_none")]
1065		video: Option<String>,
1066		#[serde(skip_serializing_if = "Option::is_none")]
1067		scte35: Option<u32>,
1068		count: u64,
1069		tags: Vec<String>,
1070	}
1071
1072	/// Diff a typed struct against the Value of a prior typed struct; the patch must roundtrip.
1073	fn check_struct(old: &Doc, new: &Doc) {
1074		let old_value = serde_json::to_value(old).unwrap();
1075		let result = diff(&old_value, new);
1076
1077		// Cross-check against the oracle fed the equivalent Values.
1078		let new_value = serde_json::to_value(new).unwrap();
1079		let want = reference(&old_value, &new_value);
1080		assert_eq!(result.patch, want.patch, "struct patch differs from oracle");
1081		assert_eq!(result.forced_snapshot, want.forced_snapshot);
1082
1083		if !result.forced_snapshot {
1084			let mut applied = old_value;
1085			json_patch::merge(&mut applied, &result.patch);
1086			assert_eq!(applied, new_value, "struct patch did not roundtrip");
1087		}
1088	}
1089
1090	#[test]
1091	fn struct_field_change() {
1092		check_struct(
1093			&Doc {
1094				count: 1,
1095				tags: vec!["a".into()],
1096				..Default::default()
1097			},
1098			&Doc {
1099				count: 2,
1100				tags: vec!["a".into()],
1101				..Default::default()
1102			},
1103		);
1104	}
1105
1106	#[test]
1107	fn struct_option_some_to_none_is_deletion() {
1108		// A skipped field (None) must become a null deletion of the previously-present key.
1109		let result = diff(
1110			&serde_json::to_value(Doc {
1111				video: Some("v1".into()),
1112				count: 1,
1113				..Default::default()
1114			})
1115			.unwrap(),
1116			&Doc {
1117				video: None,
1118				count: 1,
1119				..Default::default()
1120			},
1121		);
1122		assert!(!result.forced_snapshot, "deleting a skipped key is clean");
1123		assert_eq!(result.patch, json!({ "video": null }));
1124		check_struct(
1125			&Doc {
1126				video: Some("v1".into()),
1127				count: 1,
1128				..Default::default()
1129			},
1130			&Doc {
1131				video: None,
1132				count: 1,
1133				..Default::default()
1134			},
1135		);
1136	}
1137
1138	#[test]
1139	fn struct_option_none_to_some_is_addition() {
1140		check_struct(
1141			&Doc {
1142				count: 1,
1143				..Default::default()
1144			},
1145			&Doc {
1146				scte35: Some(42),
1147				count: 1,
1148				..Default::default()
1149			},
1150		);
1151	}
1152
1153	#[test]
1154	fn struct_unchanged_is_empty_patch() {
1155		let doc = Doc {
1156			video: Some("v".into()),
1157			scte35: Some(1),
1158			count: 7,
1159			tags: vec!["x".into(), "y".into()],
1160		};
1161		let result = diff(&serde_json::to_value(&doc).unwrap(), &doc);
1162		assert!(!result.forced_snapshot);
1163		assert_eq!(result.patch, json!({}));
1164	}
1165
1166	#[test]
1167	fn struct_vec_changes_wholesale() {
1168		check_struct(
1169			&Doc {
1170				count: 1,
1171				tags: vec!["a".into(), "b".into()],
1172				..Default::default()
1173			},
1174			&Doc {
1175				count: 1,
1176				tags: vec!["a".into(), "c".into()],
1177				..Default::default()
1178			},
1179		);
1180	}
1181
1182	#[derive(serde::Serialize)]
1183	struct Nested {
1184		inner: Inner,
1185		name: String,
1186	}
1187	#[derive(serde::Serialize)]
1188	struct Inner {
1189		a: u32,
1190		b: u32,
1191	}
1192
1193	#[test]
1194	fn nested_struct_only_changed_field() {
1195		let old = serde_json::to_value(Nested {
1196			inner: Inner { a: 1, b: 2 },
1197			name: "n".into(),
1198		})
1199		.unwrap();
1200		let new = Nested {
1201			inner: Inner { a: 1, b: 9 },
1202			name: "n".into(),
1203		};
1204		let result = diff(&old, &new);
1205		assert_eq!(result.patch, json!({ "inner": { "b": 9 } }));
1206		assert!(!result.forced_snapshot);
1207	}
1208
1209	#[derive(serde::Serialize)]
1210	enum Tag {
1211		Active,
1212		Idle,
1213	}
1214
1215	#[derive(serde::Serialize)]
1216	struct Stated {
1217		state: Tag,
1218		seq: u32,
1219	}
1220
1221	#[test]
1222	fn unit_enum_variant_is_string() {
1223		// Externally-tagged unit variants serialize as the variant name string.
1224		let old = serde_json::to_value(Stated {
1225			state: Tag::Active,
1226			seq: 1,
1227		})
1228		.unwrap();
1229		assert_eq!(old, json!({ "state": "Active", "seq": 1 }));
1230		let result = diff(
1231			&old,
1232			&Stated {
1233				state: Tag::Idle,
1234				seq: 1,
1235			},
1236		);
1237		assert!(!result.forced_snapshot);
1238		assert_eq!(result.patch, json!({ "state": "Idle" }));
1239	}
1240
1241	/// Diff a typed value against the Value of a prior value: the patch must match the oracle fed the
1242	/// equivalent Values, and roundtrip.
1243	fn check_typed<T: Serialize>(old: &Value, new: &T) {
1244		let new_value = serde_json::to_value(new).unwrap();
1245		let want = reference(old, &new_value);
1246		let got = diff(old, new);
1247		assert_eq!(got.patch, want.patch, "patch differs from oracle");
1248		assert_eq!(got.forced_snapshot, want.forced_snapshot, "forced differs from oracle");
1249		if !got.forced_snapshot {
1250			let mut applied = old.clone();
1251			json_patch::merge(&mut applied, &got.patch);
1252			assert_eq!(applied, new_value, "patch did not roundtrip");
1253		}
1254	}
1255
1256	#[derive(serde::Serialize)]
1257	enum Payload {
1258		Newtype(u32),
1259		Tuple(u32, String),
1260		Struct { x: u32, y: u32 },
1261	}
1262
1263	#[derive(serde::Serialize)]
1264	struct Holder {
1265		payload: Payload,
1266		seq: u32,
1267	}
1268
1269	#[test]
1270	fn newtype_variant_keeps_its_tag() {
1271		// Regression: a newtype variant must serialize as `{ "Newtype": v }`, not collapse to `v`.
1272		let old = serde_json::to_value(Holder {
1273			payload: Payload::Newtype(1),
1274			seq: 0,
1275		})
1276		.unwrap();
1277		assert_eq!(old, json!({ "payload": { "Newtype": 1 }, "seq": 0 }));
1278		let result = diff(
1279			&old,
1280			&Holder {
1281				payload: Payload::Newtype(2),
1282				seq: 0,
1283			},
1284		);
1285		assert_eq!(result.patch, json!({ "payload": { "Newtype": 2 } }));
1286		check_typed(
1287			&old,
1288			&Holder {
1289				payload: Payload::Newtype(2),
1290				seq: 0,
1291			},
1292		);
1293	}
1294
1295	#[test]
1296	fn tuple_variant_keeps_its_tag() {
1297		let old = serde_json::to_value(Holder {
1298			payload: Payload::Tuple(1, "a".into()),
1299			seq: 0,
1300		})
1301		.unwrap();
1302		assert_eq!(old, json!({ "payload": { "Tuple": [1, "a"] }, "seq": 0 }));
1303		check_typed(
1304			&old,
1305			&Holder {
1306				payload: Payload::Tuple(2, "a".into()),
1307				seq: 0,
1308			},
1309		);
1310	}
1311
1312	#[test]
1313	fn struct_variant_keeps_its_tag() {
1314		let old = serde_json::to_value(Holder {
1315			payload: Payload::Struct { x: 1, y: 2 },
1316			seq: 0,
1317		})
1318		.unwrap();
1319		assert_eq!(old, json!({ "payload": { "Struct": { "x": 1, "y": 2 } }, "seq": 0 }));
1320		check_typed(
1321			&old,
1322			&Holder {
1323				payload: Payload::Struct { x: 1, y: 9 },
1324				seq: 0,
1325			},
1326		);
1327	}
1328
1329	#[derive(serde::Deserialize)]
1330	struct Vector {
1331		name: String,
1332		old: Value,
1333		new: Value,
1334		forced: bool,
1335		patch: Option<Value>,
1336	}
1337
1338	/// Shared cross-impl fixture: the TS suite (js/json) asserts the same vectors so both
1339	/// implementations agree on every snapshot/delta decision and patch shape.
1340	#[test]
1341	fn golden_vectors() {
1342		let vectors: Vec<Vector> = serde_json::from_str(include_str!("../tests/vectors.json")).unwrap();
1343		for case in vectors {
1344			let result = diff(&case.old, &case.new);
1345			assert_eq!(result.forced_snapshot, case.forced, "{}: forced_snapshot", case.name);
1346
1347			if let Some(expected) = case.patch {
1348				assert_eq!(result.patch, expected, "{}: patch", case.name);
1349				let mut applied = case.old.clone();
1350				json_patch::merge(&mut applied, &result.patch);
1351				assert_eq!(applied, case.new, "{}: roundtrip", case.name);
1352			}
1353		}
1354	}
1355
1356	/// Exercise the diff over a sequence of evolving documents, asserting agreement with the oracle
1357	/// and full roundtrip at every step (the way the producer applies deltas).
1358	#[test]
1359	fn evolving_document_matches_oracle() {
1360		let mut docs = Vec::new();
1361		for tick in 0u64..40 {
1362			docs.push(json!({
1363				"id": "device-1",
1364				"static": { "model": "x", "tags": ["a", "b", "c"] },
1365				"counters": { "n": tick, "errors": tick / 10 },
1366				"reading": (tick as f64 * 0.5),
1367				"flags": { "online": tick % 2 == 0, "charging": tick % 3 == 0 },
1368				"list": [tick, tick + 1],
1369			}));
1370		}
1371		for pair in docs.windows(2) {
1372			check(pair[0].clone(), pair[1].clone());
1373		}
1374	}
1375}