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			// A map emptied of every key offered none, so nothing sized `seen` for this depth.
584			let seen: &mut [String] = match seen.get_mut(depth) {
585				Some(seen) => &mut seen[..self.seen_len],
586				None => &mut [],
587			};
588			let out = &mut bytes[depth];
589			if self.ordered {
590				seen.sort_unstable();
591			}
592			for key in base.keys() {
593				if seen.binary_search_by(|seen| seen.as_str().cmp(key)).is_err() {
594					if self.entries == 0 {
595						if depth == 0 {
596							out.reserve(*last_capacity);
597						}
598						out.push(b'{');
599					} else {
600						out.push(b',');
601					}
602					serde_json::to_writer(&mut *out, key).map_err(|err| Error(err.to_string()))?;
603					out.extend_from_slice(b":null");
604					self.entries += 1;
605				}
606			}
607		}
608		if self.entries == 0 {
609			if self.differ.baseline.is_object() {
610				return Ok(Node::Same);
611			}
612			self.differ
613				.scratch
614				.borrow_mut()
615				.buffer(self.differ.depth)
616				.extend_from_slice(b"{}");
617			return Ok(Node::Diff);
618		}
619		self.differ.scratch.borrow_mut().buffer(self.differ.depth).push(b'}');
620		Ok(Node::Diff)
621	}
622}
623
624impl SerializeMap for MapDiff<'_> {
625	type Ok = Node;
626	type Error = Error;
627	fn serialize_key<T: Serialize + ?Sized>(&mut self, key: &T) -> Result<(), Error> {
628		let mut scratch = self.differ.scratch.borrow_mut();
629		if scratch.pending.len() <= self.differ.depth {
630			scratch.pending.resize_with(self.differ.depth + 1, String::new);
631		}
632		let pending = &mut scratch.pending[self.differ.depth];
633		pending.clear();
634		key.serialize(KeySer(pending))
635	}
636	fn serialize_value<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
637		let depth = self.differ.depth;
638		let key = std::mem::take(&mut self.differ.scratch.borrow_mut().pending[depth]);
639		let (child, existed) = self.differ.child(&key);
640		let node = value.serialize(child)?;
641		self.entry(&key, existed, node)?;
642		self.differ.scratch.borrow_mut().pending[depth] = key;
643		Ok(())
644	}
645	fn end(self) -> Result<Node, Error> {
646		self.finish()
647	}
648}
649
650impl SerializeStruct for MapDiff<'_> {
651	type Ok = Node;
652	type Error = Error;
653	fn serialize_field<T: Serialize + ?Sized>(&mut self, key: &'static str, value: &T) -> Result<(), Error> {
654		let (child, existed) = self.differ.child(key);
655		let node = value.serialize(child)?;
656		self.entry(key, existed, node)?;
657		Ok(())
658	}
659	// A field skipped via `skip_serializing_if` is simply never offered here, so it stays out of `seen`
660	// and `finish` emits it as a null deletion if the baseline had it (the default `skip_field` suffices).
661	fn end(self) -> Result<Node, Error> {
662		self.finish()
663	}
664}
665
666/// Serializes a map key to its `String`, the only form JSON object keys take. Anything else is an
667/// error, mirroring `serde_json`'s own key handling.
668struct KeySer<'a>(&'a mut String);
669
670impl Serializer for KeySer<'_> {
671	type Ok = ();
672	type Error = Error;
673	type SerializeSeq = Impossible<(), Error>;
674	type SerializeTuple = Impossible<(), Error>;
675	type SerializeTupleStruct = Impossible<(), Error>;
676	type SerializeTupleVariant = Impossible<(), Error>;
677	type SerializeMap = Impossible<(), Error>;
678	type SerializeStruct = Impossible<(), Error>;
679	type SerializeStructVariant = Impossible<(), Error>;
680
681	fn serialize_str(self, v: &str) -> Result<(), Error> {
682		{
683			self.0.push_str(v);
684			Ok(())
685		}
686	}
687	fn serialize_char(self, v: char) -> Result<(), Error> {
688		{
689			use std::fmt::Write;
690			write!(self.0, "{v}").expect("writing to String cannot fail");
691			Ok(())
692		}
693	}
694	fn serialize_bool(self, v: bool) -> Result<(), Error> {
695		{
696			use std::fmt::Write;
697			write!(self.0, "{v}").expect("writing to String cannot fail");
698			Ok(())
699		}
700	}
701	fn serialize_i8(self, v: i8) -> Result<(), Error> {
702		{
703			use std::fmt::Write;
704			write!(self.0, "{v}").expect("writing to String cannot fail");
705			Ok(())
706		}
707	}
708	fn serialize_i16(self, v: i16) -> Result<(), Error> {
709		{
710			use std::fmt::Write;
711			write!(self.0, "{v}").expect("writing to String cannot fail");
712			Ok(())
713		}
714	}
715	fn serialize_i32(self, v: i32) -> Result<(), Error> {
716		{
717			use std::fmt::Write;
718			write!(self.0, "{v}").expect("writing to String cannot fail");
719			Ok(())
720		}
721	}
722	fn serialize_i64(self, v: i64) -> Result<(), Error> {
723		{
724			use std::fmt::Write;
725			write!(self.0, "{v}").expect("writing to String cannot fail");
726			Ok(())
727		}
728	}
729	fn serialize_u8(self, v: u8) -> Result<(), Error> {
730		{
731			use std::fmt::Write;
732			write!(self.0, "{v}").expect("writing to String cannot fail");
733			Ok(())
734		}
735	}
736	fn serialize_u16(self, v: u16) -> Result<(), Error> {
737		{
738			use std::fmt::Write;
739			write!(self.0, "{v}").expect("writing to String cannot fail");
740			Ok(())
741		}
742	}
743	fn serialize_u32(self, v: u32) -> Result<(), Error> {
744		{
745			use std::fmt::Write;
746			write!(self.0, "{v}").expect("writing to String cannot fail");
747			Ok(())
748		}
749	}
750	fn serialize_u64(self, v: u64) -> Result<(), Error> {
751		{
752			use std::fmt::Write;
753			write!(self.0, "{v}").expect("writing to String cannot fail");
754			Ok(())
755		}
756	}
757	fn serialize_unit_variant(self, _name: &'static str, _idx: u32, variant: &'static str) -> Result<(), Error> {
758		{
759			self.0.push_str(variant);
760			Ok(())
761		}
762	}
763	fn serialize_newtype_struct<T: Serialize + ?Sized>(self, _name: &'static str, value: &T) -> Result<(), Error> {
764		value.serialize(self)
765	}
766	fn serialize_some<T: Serialize + ?Sized>(self, value: &T) -> Result<(), Error> {
767		value.serialize(self)
768	}
769	fn serialize_f32(self, _v: f32) -> Result<(), Error> {
770		Err(Error("float map key".into()))
771	}
772	fn serialize_f64(self, _v: f64) -> Result<(), Error> {
773		Err(Error("float map key".into()))
774	}
775	fn serialize_bytes(self, _v: &[u8]) -> Result<(), Error> {
776		Err(Error("bytes map key".into()))
777	}
778	fn serialize_none(self) -> Result<(), Error> {
779		Err(Error("null map key".into()))
780	}
781	fn serialize_unit(self) -> Result<(), Error> {
782		Err(Error("unit map key".into()))
783	}
784	fn serialize_unit_struct(self, _name: &'static str) -> Result<(), Error> {
785		Err(Error("unit struct map key".into()))
786	}
787	fn serialize_newtype_variant<T: Serialize + ?Sized>(
788		self,
789		_name: &'static str,
790		_idx: u32,
791		_variant: &'static str,
792		_value: &T,
793	) -> Result<(), Error> {
794		Err(Error("newtype variant map key".into()))
795	}
796	fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Error> {
797		Err(Error("seq map key".into()))
798	}
799	fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Error> {
800		Err(Error("tuple map key".into()))
801	}
802	fn serialize_tuple_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeTupleStruct, Error> {
803		Err(Error("tuple struct map key".into()))
804	}
805	fn serialize_tuple_variant(
806		self,
807		_name: &'static str,
808		_idx: u32,
809		_variant: &'static str,
810		_len: usize,
811	) -> Result<Self::SerializeTupleVariant, Error> {
812		Err(Error("tuple variant map key".into()))
813	}
814	fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Error> {
815		Err(Error("map map key".into()))
816	}
817	fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct, Error> {
818		Err(Error("struct map key".into()))
819	}
820	fn serialize_struct_variant(
821		self,
822		_name: &'static str,
823		_idx: u32,
824		_variant: &'static str,
825		_len: usize,
826	) -> Result<Self::SerializeStructVariant, Error> {
827		Err(Error("struct variant map key".into()))
828	}
829}
830
831#[cfg(test)]
832mod test {
833	use super::*;
834	use serde_json::json;
835
836	/// A straightforward Value-vs-Value merge-patch diff, used only as a test oracle: the production
837	/// `diff` (the serializer) must agree with it on every case.
838	fn reference(old: &Value, new: &Value) -> Diff {
839		fn objects(
840			old: &Map<String, Value>,
841			new: &Map<String, Value>,
842			patch: &mut Map<String, Value>,
843			forced: &mut bool,
844		) {
845			for key in old.keys() {
846				if !new.contains_key(key) {
847					patch.insert(key.clone(), Value::Null);
848				}
849			}
850			for (key, new_val) in new {
851				let old_val = old.get(key);
852				if old_val == Some(new_val) {
853					continue;
854				}
855				if let (Some(Value::Object(old_obj)), Value::Object(new_obj)) = (old_val, new_val) {
856					let mut sub = Map::new();
857					objects(old_obj, new_obj, &mut sub, forced);
858					if !sub.is_empty() {
859						patch.insert(key.clone(), Value::Object(sub));
860					}
861					continue;
862				}
863				if new_val.is_null() {
864					*forced = true;
865				}
866				patch.insert(key.clone(), new_val.clone());
867			}
868		}
869
870		if let (Value::Object(old_obj), Value::Object(new_obj)) = (old, new) {
871			let mut patch = Map::new();
872			let mut forced = false;
873			objects(old_obj, new_obj, &mut patch, &mut forced);
874			Diff {
875				patch: Value::Object(patch),
876				forced_snapshot: forced,
877			}
878		} else {
879			Diff {
880				patch: new.clone(),
881				forced_snapshot: true,
882			}
883		}
884	}
885
886	/// The serializer must produce the same patch and forced flag as the reference oracle, and (when
887	/// not forced) applying the patch to `old` must reproduce `new`.
888	fn check(old: Value, new: Value) {
889		let want = reference(&old, &new);
890		let got = diff(&old, &new);
891		assert_eq!(got.patch, want.patch, "patch mismatch for {old} -> {new}");
892		assert_eq!(
893			got.forced_snapshot, want.forced_snapshot,
894			"forced mismatch for {old} -> {new}"
895		);
896		if !got.forced_snapshot {
897			let mut applied = old.clone();
898			json_patch::merge(&mut applied, &got.patch);
899			assert_eq!(applied, new, "patch did not roundtrip for {old} -> {new}");
900		}
901	}
902
903	#[test]
904	fn replacing_scalar_with_empty_object_is_a_change() {
905		check(json!({ "value": 1 }), json!({ "value": {} }));
906		check(json!({ "value": null }), json!({ "value": {} }));
907		let result = diff(&json!(1), &json!({}));
908		assert!(result.forced_snapshot);
909		assert_eq!(result.patch, json!({}));
910	}
911
912	#[test]
913	fn duplicate_serialized_map_keys_force_a_snapshot() {
914		struct Duplicate;
915		impl Serialize for Duplicate {
916			fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
917				let mut map = serializer.serialize_map(Some(3))?;
918				map.serialize_entry("key", &1)?;
919				map.serialize_entry("other", &2)?;
920				map.serialize_entry("key", &3)?;
921				map.end()
922			}
923		}
924		assert!(diff(&json!({ "key": 1, "other": 2 }), &Duplicate).forced_snapshot);
925	}
926
927	#[test]
928	fn emptying_a_map_removes_every_key() {
929		// No key is serialized at the emptied map's depth, so nothing has sized the
930		// scratch for it yet.
931		check(json!({ "a": 1 }), json!({}));
932		check(json!({ "a": 1, "b": 2 }), json!({}));
933		check(json!({ "x": { "a": 1 } }), json!({ "x": {} }));
934	}
935
936	#[test]
937	fn changed_scalar() {
938		check(json!({ "a": 1, "b": 2 }), json!({ "a": 1, "b": 3 }));
939	}
940
941	#[test]
942	fn added_key() {
943		let result = diff(&json!({ "a": 1 }), &json!({ "a": 1, "b": 2 }));
944		assert!(!result.forced_snapshot);
945		assert_eq!(result.patch, json!({ "b": 2 }));
946		check(json!({ "a": 1 }), json!({ "a": 1, "b": 2 }));
947	}
948
949	#[test]
950	fn added_null_key_forces_snapshot() {
951		check(json!({ "a": 1 }), json!({ "a": 1, "x": null }));
952		check(
953			json!({ "items": [{ "a": 1 }] }),
954			json!({ "items": [{ "a": 1, "x": null }] }),
955		);
956	}
957
958	#[test]
959	fn removed_key_is_null() {
960		let result = diff(&json!({ "a": 1, "b": 2 }), &json!({ "a": 1 }));
961		assert!(!result.forced_snapshot, "removing a key is a clean delete");
962		assert_eq!(result.patch, json!({ "b": null }));
963		check(json!({ "a": 1, "b": 2 }), json!({ "a": 1 }));
964	}
965
966	#[test]
967	fn nested_object_only_includes_changed_keys() {
968		let result = diff(&json!({ "o": { "x": 1, "y": 2 } }), &json!({ "o": { "x": 1, "y": 9 } }));
969		assert!(!result.forced_snapshot);
970		assert_eq!(result.patch, json!({ "o": { "y": 9 } }));
971		check(json!({ "o": { "x": 1, "y": 2 } }), json!({ "o": { "x": 1, "y": 9 } }));
972	}
973
974	#[test]
975	fn unchanged_object_is_empty_patch() {
976		let result = diff(&json!({ "a": 1, "o": { "x": 1 } }), &json!({ "a": 1, "o": { "x": 1 } }));
977		assert!(!result.forced_snapshot);
978		assert_eq!(result.patch, json!({}));
979	}
980
981	#[test]
982	fn changed_array_is_wholesale_delta() {
983		let result = diff(&json!({ "a": [1, 2] }), &json!({ "a": [1, 2, 3] }));
984		assert!(!result.forced_snapshot);
985		assert_eq!(result.patch, json!({ "a": [1, 2, 3] }));
986		check(json!({ "a": [1, 2] }), json!({ "a": [1, 2, 3] }));
987	}
988
989	#[test]
990	fn unchanged_array_is_pruned() {
991		let result = diff(&json!({ "a": [1, 2, 3], "b": 1 }), &json!({ "a": [1, 2, 3], "b": 2 }));
992		assert_eq!(
993			result.patch,
994			json!({ "b": 2 }),
995			"an unchanged array stays out of the patch"
996		);
997	}
998
999	#[test]
1000	fn added_array_is_delta() {
1001		check(json!({ "a": 1 }), json!({ "a": 1, "b": [1] }));
1002	}
1003
1004	#[test]
1005	fn nested_array_is_delta() {
1006		check(json!({ "o": { "x": 1 } }), json!({ "o": { "x": 1, "list": [1] } }));
1007	}
1008
1009	#[test]
1010	fn array_of_objects_replaces_wholesale() {
1011		check(
1012			json!({ "items": [{ "id": 1, "v": 1 }, { "id": 2, "v": 2 }] }),
1013			json!({ "items": [{ "id": 1, "v": 9 }, { "id": 2, "v": 2 }] }),
1014		);
1015	}
1016
1017	#[test]
1018	fn set_to_null_forces_snapshot() {
1019		// A genuine null value can't be represented: merge patch would delete the key.
1020		let result = diff(&json!({ "a": 1 }), &json!({ "a": null }));
1021		assert!(result.forced_snapshot);
1022		assert!(reference(&json!({ "a": 1 }), &json!({ "a": null })).forced_snapshot);
1023	}
1024
1025	#[test]
1026	fn nested_null_forces_snapshot() {
1027		let old = json!({ "o": { "x": 1 } });
1028		let new = json!({ "o": { "x": null } });
1029		assert!(diff(&old, &new).forced_snapshot);
1030		assert_eq!(diff(&old, &new).forced_snapshot, reference(&old, &new).forced_snapshot);
1031	}
1032
1033	#[test]
1034	fn replacing_object_with_scalar() {
1035		check(json!({ "a": { "x": 1 } }), json!({ "a": 5 }));
1036	}
1037
1038	#[test]
1039	fn replacing_scalar_with_object() {
1040		check(json!({ "a": 5 }), json!({ "a": { "x": 1 } }));
1041	}
1042
1043	#[test]
1044	fn non_object_root_forces_snapshot() {
1045		let result = diff(&json!(1), &json!(2));
1046		assert!(result.forced_snapshot);
1047		assert_eq!(result.patch, json!(2));
1048	}
1049
1050	#[test]
1051	fn array_root_forces_snapshot() {
1052		let result = diff(&json!([1, 2]), &json!([1, 2, 3]));
1053		assert!(result.forced_snapshot);
1054		assert_eq!(result.patch, json!([1, 2, 3]));
1055	}
1056
1057	#[test]
1058	fn unchanged_scalar_root_is_not_forced() {
1059		// An equal non-object root is a no-op (empty patch), matching the producer's dedup.
1060		let result = diff(&json!(7), &json!(7));
1061		assert!(!result.forced_snapshot);
1062		assert_eq!(result.patch, json!({}));
1063	}
1064
1065	#[test]
1066	fn floats_and_bools_and_strings() {
1067		check(
1068			json!({ "f": 1.5, "b": true, "s": "hi" }),
1069			json!({ "f": 2.5, "b": false, "s": "bye" }),
1070		);
1071	}
1072
1073	// ---- Typed structs (the serializer's whole point: diff `T` without building its Value) ----
1074
1075	#[derive(serde::Serialize, serde::Deserialize, Default, PartialEq, Debug)]
1076	struct Doc {
1077		#[serde(skip_serializing_if = "Option::is_none")]
1078		video: Option<String>,
1079		#[serde(skip_serializing_if = "Option::is_none")]
1080		scte35: Option<u32>,
1081		count: u64,
1082		tags: Vec<String>,
1083	}
1084
1085	/// Diff a typed struct against the Value of a prior typed struct; the patch must roundtrip.
1086	fn check_struct(old: &Doc, new: &Doc) {
1087		let old_value = serde_json::to_value(old).unwrap();
1088		let result = diff(&old_value, new);
1089
1090		// Cross-check against the oracle fed the equivalent Values.
1091		let new_value = serde_json::to_value(new).unwrap();
1092		let want = reference(&old_value, &new_value);
1093		assert_eq!(result.patch, want.patch, "struct patch differs from oracle");
1094		assert_eq!(result.forced_snapshot, want.forced_snapshot);
1095
1096		if !result.forced_snapshot {
1097			let mut applied = old_value;
1098			json_patch::merge(&mut applied, &result.patch);
1099			assert_eq!(applied, new_value, "struct patch did not roundtrip");
1100		}
1101	}
1102
1103	#[test]
1104	fn struct_field_change() {
1105		check_struct(
1106			&Doc {
1107				count: 1,
1108				tags: vec!["a".into()],
1109				..Default::default()
1110			},
1111			&Doc {
1112				count: 2,
1113				tags: vec!["a".into()],
1114				..Default::default()
1115			},
1116		);
1117	}
1118
1119	#[test]
1120	fn struct_option_some_to_none_is_deletion() {
1121		// A skipped field (None) must become a null deletion of the previously-present key.
1122		let result = diff(
1123			&serde_json::to_value(Doc {
1124				video: Some("v1".into()),
1125				count: 1,
1126				..Default::default()
1127			})
1128			.unwrap(),
1129			&Doc {
1130				video: None,
1131				count: 1,
1132				..Default::default()
1133			},
1134		);
1135		assert!(!result.forced_snapshot, "deleting a skipped key is clean");
1136		assert_eq!(result.patch, json!({ "video": null }));
1137		check_struct(
1138			&Doc {
1139				video: Some("v1".into()),
1140				count: 1,
1141				..Default::default()
1142			},
1143			&Doc {
1144				video: None,
1145				count: 1,
1146				..Default::default()
1147			},
1148		);
1149	}
1150
1151	#[test]
1152	fn struct_option_none_to_some_is_addition() {
1153		check_struct(
1154			&Doc {
1155				count: 1,
1156				..Default::default()
1157			},
1158			&Doc {
1159				scte35: Some(42),
1160				count: 1,
1161				..Default::default()
1162			},
1163		);
1164	}
1165
1166	#[test]
1167	fn struct_unchanged_is_empty_patch() {
1168		let doc = Doc {
1169			video: Some("v".into()),
1170			scte35: Some(1),
1171			count: 7,
1172			tags: vec!["x".into(), "y".into()],
1173		};
1174		let result = diff(&serde_json::to_value(&doc).unwrap(), &doc);
1175		assert!(!result.forced_snapshot);
1176		assert_eq!(result.patch, json!({}));
1177	}
1178
1179	#[test]
1180	fn struct_vec_changes_wholesale() {
1181		check_struct(
1182			&Doc {
1183				count: 1,
1184				tags: vec!["a".into(), "b".into()],
1185				..Default::default()
1186			},
1187			&Doc {
1188				count: 1,
1189				tags: vec!["a".into(), "c".into()],
1190				..Default::default()
1191			},
1192		);
1193	}
1194
1195	#[derive(serde::Serialize)]
1196	struct Nested {
1197		inner: Inner,
1198		name: String,
1199	}
1200	#[derive(serde::Serialize)]
1201	struct Inner {
1202		a: u32,
1203		b: u32,
1204	}
1205
1206	#[test]
1207	fn nested_struct_only_changed_field() {
1208		let old = serde_json::to_value(Nested {
1209			inner: Inner { a: 1, b: 2 },
1210			name: "n".into(),
1211		})
1212		.unwrap();
1213		let new = Nested {
1214			inner: Inner { a: 1, b: 9 },
1215			name: "n".into(),
1216		};
1217		let result = diff(&old, &new);
1218		assert_eq!(result.patch, json!({ "inner": { "b": 9 } }));
1219		assert!(!result.forced_snapshot);
1220	}
1221
1222	#[derive(serde::Serialize)]
1223	enum Tag {
1224		Active,
1225		Idle,
1226	}
1227
1228	#[derive(serde::Serialize)]
1229	struct Stated {
1230		state: Tag,
1231		seq: u32,
1232	}
1233
1234	#[test]
1235	fn unit_enum_variant_is_string() {
1236		// Externally-tagged unit variants serialize as the variant name string.
1237		let old = serde_json::to_value(Stated {
1238			state: Tag::Active,
1239			seq: 1,
1240		})
1241		.unwrap();
1242		assert_eq!(old, json!({ "state": "Active", "seq": 1 }));
1243		let result = diff(
1244			&old,
1245			&Stated {
1246				state: Tag::Idle,
1247				seq: 1,
1248			},
1249		);
1250		assert!(!result.forced_snapshot);
1251		assert_eq!(result.patch, json!({ "state": "Idle" }));
1252	}
1253
1254	/// Diff a typed value against the Value of a prior value: the patch must match the oracle fed the
1255	/// equivalent Values, and roundtrip.
1256	fn check_typed<T: Serialize>(old: &Value, new: &T) {
1257		let new_value = serde_json::to_value(new).unwrap();
1258		let want = reference(old, &new_value);
1259		let got = diff(old, new);
1260		assert_eq!(got.patch, want.patch, "patch differs from oracle");
1261		assert_eq!(got.forced_snapshot, want.forced_snapshot, "forced differs from oracle");
1262		if !got.forced_snapshot {
1263			let mut applied = old.clone();
1264			json_patch::merge(&mut applied, &got.patch);
1265			assert_eq!(applied, new_value, "patch did not roundtrip");
1266		}
1267	}
1268
1269	#[derive(serde::Serialize)]
1270	enum Payload {
1271		Newtype(u32),
1272		Tuple(u32, String),
1273		Struct { x: u32, y: u32 },
1274	}
1275
1276	#[derive(serde::Serialize)]
1277	struct Holder {
1278		payload: Payload,
1279		seq: u32,
1280	}
1281
1282	#[test]
1283	fn newtype_variant_keeps_its_tag() {
1284		// Regression: a newtype variant must serialize as `{ "Newtype": v }`, not collapse to `v`.
1285		let old = serde_json::to_value(Holder {
1286			payload: Payload::Newtype(1),
1287			seq: 0,
1288		})
1289		.unwrap();
1290		assert_eq!(old, json!({ "payload": { "Newtype": 1 }, "seq": 0 }));
1291		let result = diff(
1292			&old,
1293			&Holder {
1294				payload: Payload::Newtype(2),
1295				seq: 0,
1296			},
1297		);
1298		assert_eq!(result.patch, json!({ "payload": { "Newtype": 2 } }));
1299		check_typed(
1300			&old,
1301			&Holder {
1302				payload: Payload::Newtype(2),
1303				seq: 0,
1304			},
1305		);
1306	}
1307
1308	#[test]
1309	fn tuple_variant_keeps_its_tag() {
1310		let old = serde_json::to_value(Holder {
1311			payload: Payload::Tuple(1, "a".into()),
1312			seq: 0,
1313		})
1314		.unwrap();
1315		assert_eq!(old, json!({ "payload": { "Tuple": [1, "a"] }, "seq": 0 }));
1316		check_typed(
1317			&old,
1318			&Holder {
1319				payload: Payload::Tuple(2, "a".into()),
1320				seq: 0,
1321			},
1322		);
1323	}
1324
1325	#[test]
1326	fn struct_variant_keeps_its_tag() {
1327		let old = serde_json::to_value(Holder {
1328			payload: Payload::Struct { x: 1, y: 2 },
1329			seq: 0,
1330		})
1331		.unwrap();
1332		assert_eq!(old, json!({ "payload": { "Struct": { "x": 1, "y": 2 } }, "seq": 0 }));
1333		check_typed(
1334			&old,
1335			&Holder {
1336				payload: Payload::Struct { x: 1, y: 9 },
1337				seq: 0,
1338			},
1339		);
1340	}
1341
1342	#[derive(serde::Deserialize)]
1343	struct Vector {
1344		name: String,
1345		old: Value,
1346		new: Value,
1347		forced: bool,
1348		patch: Option<Value>,
1349	}
1350
1351	/// Shared cross-impl fixture: the TS suite (js/json) asserts the same vectors so both
1352	/// implementations agree on every snapshot/delta decision and patch shape.
1353	#[test]
1354	fn golden_vectors() {
1355		let vectors: Vec<Vector> = serde_json::from_str(include_str!("../tests/vectors.json")).unwrap();
1356		for case in vectors {
1357			let result = diff(&case.old, &case.new);
1358			assert_eq!(result.forced_snapshot, case.forced, "{}: forced_snapshot", case.name);
1359
1360			if let Some(expected) = case.patch {
1361				assert_eq!(result.patch, expected, "{}: patch", case.name);
1362				let mut applied = case.old.clone();
1363				json_patch::merge(&mut applied, &result.patch);
1364				assert_eq!(applied, case.new, "{}: roundtrip", case.name);
1365			}
1366		}
1367	}
1368
1369	/// Exercise the diff over a sequence of evolving documents, asserting agreement with the oracle
1370	/// and full roundtrip at every step (the way the producer applies deltas).
1371	#[test]
1372	fn evolving_document_matches_oracle() {
1373		let mut docs = Vec::new();
1374		for tick in 0u64..40 {
1375			docs.push(json!({
1376				"id": "device-1",
1377				"static": { "model": "x", "tags": ["a", "b", "c"] },
1378				"counters": { "n": tick, "errors": tick / 10 },
1379				"reading": (tick as f64 * 0.5),
1380				"flags": { "online": tick % 2 == 0, "charging": tick % 3 == 0 },
1381				"list": [tick, tick + 1],
1382			}));
1383		}
1384		for pair in docs.windows(2) {
1385			check(pair[0].clone(), pair[1].clone());
1386		}
1387	}
1388}