1use crate::generic_repr::*;
34use std::fmt;
35
36#[derive(Clone, PartialEq, Eq)]
40pub enum Delta<T> {
41 Same,
43 Changed(T),
45}
46
47impl<T: fmt::Debug> fmt::Debug for Delta<T> {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 match self {
50 Delta::Same => write!(f, "Same"),
51 Delta::Changed(v) => write!(f, "Changed({v:?})"),
52 }
53 }
54}
55
56impl<T> Delta<T> {
57 pub fn is_same(&self) -> bool {
59 matches!(self, Delta::Same)
60 }
61
62 pub fn is_changed(&self) -> bool {
64 matches!(self, Delta::Changed(_))
65 }
66}
67
68pub trait Diff: Sized {
74 type Diff: DiffInfo;
76
77 fn diff(old: &Self, new: &Self) -> Self::Diff;
79}
80
81pub trait DiffInfo {
83 fn is_empty(&self) -> bool;
85 fn change_count(&self) -> usize;
87}
88
89pub trait Patch: Diff {
93 fn patch(old: &Self, diff: &Self::Diff) -> Self;
95}
96
97impl<T: Clone + PartialEq> Diff for T
101where
102 T: LeafDiff,
103{
104 type Diff = Delta<T>;
105
106 fn diff(old: &Self, new: &Self) -> Delta<T> {
107 if old == new {
108 Delta::Same
109 } else {
110 Delta::Changed(new.clone())
111 }
112 }
113}
114
115impl<T: Clone + PartialEq> Patch for T
116where
117 T: LeafDiff,
118{
119 fn patch(old: &Self, diff: &Delta<T>) -> Self {
120 match diff {
121 Delta::Same => old.clone(),
122 Delta::Changed(v) => v.clone(),
123 }
124 }
125}
126
127pub trait LeafDiff {}
132
133impl LeafDiff for bool {}
135impl LeafDiff for u8 {}
136impl LeafDiff for u16 {}
137impl LeafDiff for u32 {}
138impl LeafDiff for u64 {}
139impl LeafDiff for u128 {}
140impl LeafDiff for usize {}
141impl LeafDiff for i8 {}
142impl LeafDiff for i16 {}
143impl LeafDiff for i32 {}
144impl LeafDiff for i64 {}
145impl LeafDiff for i128 {}
146impl LeafDiff for isize {}
147impl LeafDiff for f32 {}
148impl LeafDiff for f64 {}
149impl LeafDiff for char {}
150impl LeafDiff for String {}
151impl LeafDiff for &str {}
152
153impl<T> DiffInfo for Delta<T> {
154 fn is_empty(&self) -> bool {
155 self.is_same()
156 }
157 fn change_count(&self) -> usize {
158 if self.is_changed() { 1 } else { 0 }
159 }
160}
161
162#[derive(Clone, Debug, PartialEq, Eq)]
166pub struct ProductDiff<HD, TD> {
167 pub head: HD,
168 pub tail: TD,
169}
170
171impl<HD: DiffInfo, TD: DiffInfo> DiffInfo for ProductDiff<HD, TD> {
172 fn is_empty(&self) -> bool {
173 self.head.is_empty() && self.tail.is_empty()
174 }
175 fn change_count(&self) -> usize {
176 self.head.change_count() + self.tail.change_count()
177 }
178}
179
180impl<H: Diff, T: Diff> Diff for Product<H, T> {
181 type Diff = ProductDiff<H::Diff, T::Diff>;
182
183 fn diff(old: &Self, new: &Self) -> Self::Diff {
184 ProductDiff {
185 head: H::diff(&old.0, &new.0),
186 tail: T::diff(&old.1, &new.1),
187 }
188 }
189}
190
191impl<H: Patch, T: Patch> Patch for Product<H, T> {
192 fn patch(old: &Self, diff: &Self::Diff) -> Self {
193 Product(H::patch(&old.0, &diff.head), T::patch(&old.1, &diff.tail))
194 }
195}
196
197#[derive(Clone, Debug, PartialEq, Eq)]
201pub struct UnitDiff;
202
203impl DiffInfo for UnitDiff {
204 fn is_empty(&self) -> bool {
205 true
206 }
207 fn change_count(&self) -> usize {
208 0
209 }
210}
211
212impl Diff for Unit {
213 type Diff = UnitDiff;
214 fn diff(_old: &Self, _new: &Self) -> UnitDiff {
215 UnitDiff
216 }
217}
218
219impl Patch for Unit {
220 fn patch(_old: &Self, _diff: &UnitDiff) -> Self {
221 Unit
222 }
223}
224
225#[derive(Clone, Debug, PartialEq, Eq)]
230pub enum SumDiff<LD, RD, L, R> {
231 BothLeft(LD),
233 BothRight(RD),
235 LeftToRight(R),
237 RightToLeft(L),
239}
240
241impl<LD: DiffInfo, RD: DiffInfo, L, R> DiffInfo for SumDiff<LD, RD, L, R> {
242 fn is_empty(&self) -> bool {
243 match self {
244 SumDiff::BothLeft(d) => d.is_empty(),
245 SumDiff::BothRight(d) => d.is_empty(),
246 SumDiff::LeftToRight(_) | SumDiff::RightToLeft(_) => false,
247 }
248 }
249 fn change_count(&self) -> usize {
250 match self {
251 SumDiff::BothLeft(d) => d.change_count(),
252 SumDiff::BothRight(d) => d.change_count(),
253 SumDiff::LeftToRight(_) | SumDiff::RightToLeft(_) => 1,
254 }
255 }
256}
257
258impl<L: Diff + Clone, R: Diff + Clone> Diff for Sum<L, R> {
259 type Diff = SumDiff<L::Diff, R::Diff, L, R>;
260
261 fn diff(old: &Self, new: &Self) -> Self::Diff {
262 match (old, new) {
263 (Sum::Left(o), Sum::Left(n)) => SumDiff::BothLeft(L::diff(o, n)),
264 (Sum::Right(o), Sum::Right(n)) => SumDiff::BothRight(R::diff(o, n)),
265 (Sum::Left(_), Sum::Right(n)) => SumDiff::LeftToRight(n.clone()),
266 (Sum::Right(_), Sum::Left(n)) => SumDiff::RightToLeft(n.clone()),
267 }
268 }
269}
270
271impl<L: Patch + Clone, R: Patch + Clone> Patch for Sum<L, R> {
272 fn patch(old: &Self, diff: &Self::Diff) -> Self {
273 match diff {
274 SumDiff::BothLeft(d) => match old {
275 Sum::Left(o) => Sum::Left(L::patch(o, d)),
276 Sum::Right(_) => unreachable!("BothLeft diff applied to Right"),
277 },
278 SumDiff::BothRight(d) => match old {
279 Sum::Right(o) => Sum::Right(R::patch(o, d)),
280 Sum::Left(_) => unreachable!("BothRight diff applied to Left"),
281 },
282 SumDiff::LeftToRight(v) => Sum::Right(v.clone()),
283 SumDiff::RightToLeft(v) => Sum::Left(v.clone()),
284 }
285 }
286}
287
288impl<T: Diff> Diff for Field<T> {
291 type Diff = T::Diff;
292
293 fn diff(old: &Self, new: &Self) -> T::Diff {
294 T::diff(&old.value, &new.value)
295 }
296}
297
298impl<T: Patch> Patch for Field<T> {
299 fn patch(old: &Self, diff: &T::Diff) -> Self {
300 Field {
301 name: old.name,
302 value: T::patch(&old.value, diff),
303 }
304 }
305}
306
307impl<T: Diff> Diff for Variant<T> {
310 type Diff = T::Diff;
311
312 fn diff(old: &Self, new: &Self) -> T::Diff {
313 T::diff(&old.value, &new.value)
314 }
315}
316
317impl<T: Patch> Patch for Variant<T> {
318 fn patch(old: &Self, diff: &T::Diff) -> Self {
319 Variant {
320 name: old.name,
321 value: T::patch(&old.value, diff),
322 }
323 }
324}
325
326#[derive(Clone, Debug, PartialEq, Eq)]
331pub enum VoidDiff {}
332
333impl DiffInfo for VoidDiff {
334 fn is_empty(&self) -> bool {
335 match *self {}
336 }
337 fn change_count(&self) -> usize {
338 match *self {}
339 }
340}
341
342impl Diff for Void {
343 type Diff = VoidDiff;
344 fn diff(old: &Self, _new: &Self) -> VoidDiff {
345 match *old {}
346 }
347}
348
349impl Patch for Void {
350 fn patch(old: &Self, _diff: &VoidDiff) -> Self {
351 match *old {}
352 }
353}
354
355pub fn generic_diff<T>(old: &T, new: &T) -> <T::Repr as Diff>::Diff
362where
363 T: GenericRepr + Clone,
364 T::Repr: Diff,
365{
366 let old_repr = old.clone().into_repr();
367 let new_repr = new.clone().into_repr();
368 T::Repr::diff(&old_repr, &new_repr)
369}
370
371pub fn generic_patch<T>(old: &T, diff: &<T::Repr as Diff>::Diff) -> T
373where
374 T: GenericRepr + Clone,
375 T::Repr: Patch,
376{
377 let old_repr = old.clone().into_repr();
378 let patched_repr = T::Repr::patch(&old_repr, diff);
379 T::from_repr(patched_repr)
380}
381
382#[cfg(test)]
383mod tests {
384 use super::*;
385
386 #[derive(Clone, Debug, PartialEq)]
389 struct Point {
390 x: f64,
391 y: f64,
392 }
393
394 impl GenericRepr for Point {
395 type Repr = Product<Field<f64>, Product<Field<f64>, Unit>>;
396 fn into_repr(self) -> Self::Repr {
397 Product(
398 Field::new("x", self.x),
399 Product(Field::new("y", self.y), Unit),
400 )
401 }
402 fn from_repr(repr: Self::Repr) -> Self {
403 Point {
404 x: repr.0.value,
405 y: repr.1.0.value,
406 }
407 }
408 }
409
410 #[derive(Clone, Debug, PartialEq)]
411 enum Color {
412 Red,
413 Green,
414 Custom(u8, u8, u8),
415 }
416
417 impl GenericRepr for Color {
418 type Repr = Sum<
419 Variant<Unit>,
420 Sum<Variant<Unit>, Sum<Variant<Product<u8, Product<u8, Product<u8, Unit>>>>, Void>>,
421 >;
422
423 fn into_repr(self) -> Self::Repr {
424 match self {
425 Color::Red => Sum::Left(Variant::new("Red", Unit)),
426 Color::Green => Sum::Right(Sum::Left(Variant::new("Green", Unit))),
427 Color::Custom(r, g, b) => Sum::Right(Sum::Right(Sum::Left(Variant::new(
428 "Custom",
429 Product(r, Product(g, Product(b, Unit))),
430 )))),
431 }
432 }
433
434 fn from_repr(repr: Self::Repr) -> Self {
435 match repr {
436 Sum::Left(_) => Color::Red,
437 Sum::Right(Sum::Left(_)) => Color::Green,
438 Sum::Right(Sum::Right(Sum::Left(v))) => {
439 Color::Custom(v.value.0, v.value.1.0, v.value.1.1.0)
440 }
441 Sum::Right(Sum::Right(Sum::Right(v))) => match v {},
442 }
443 }
444 }
445
446 #[test]
449 fn leaf_diff_same() {
450 let d = i32::diff(&42, &42);
451 assert!(d.is_same());
452 assert!(d.is_empty());
453 assert_eq!(d.change_count(), 0);
454 }
455
456 #[test]
457 fn leaf_diff_changed() {
458 let d = i32::diff(&1, &2);
459 assert!(d.is_changed());
460 assert!(!d.is_empty());
461 assert_eq!(d.change_count(), 1);
462 }
463
464 #[test]
465 fn leaf_patch_same() {
466 let d = Delta::Same;
467 assert_eq!(i32::patch(&42, &d), 42);
468 }
469
470 #[test]
471 fn leaf_patch_changed() {
472 let d = Delta::Changed(99);
473 assert_eq!(i32::patch(&42, &d), 99);
474 }
475
476 #[test]
479 fn product_diff_same() {
480 let a = Product(1u32, Product(2u32, Unit));
481 let d = <Product<u32, Product<u32, Unit>>>::diff(&a, &a);
482 assert!(d.is_empty());
483 assert_eq!(d.change_count(), 0);
484 }
485
486 #[test]
487 fn product_diff_one_field() {
488 let a = Product(1u32, Product(2u32, Unit));
489 let b = Product(1u32, Product(3u32, Unit));
490 let d = <Product<u32, Product<u32, Unit>>>::diff(&a, &b);
491 assert!(!d.is_empty());
492 assert_eq!(d.change_count(), 1);
493 assert!(d.head.is_same());
494 assert!(d.tail.head.is_changed());
495 }
496
497 #[test]
498 fn product_patch_roundtrip() {
499 let a = Product(1u32, Product(2u32, Unit));
500 let b = Product(3u32, Product(2u32, Unit));
501 let d = <Product<u32, Product<u32, Unit>>>::diff(&a, &b);
502 let patched = <Product<u32, Product<u32, Unit>>>::patch(&a, &d);
503 assert_eq!(patched, b);
504 }
505
506 #[test]
509 fn sum_diff_same_variant() {
510 let a: Sum<u32, u32> = Sum::Left(42);
511 let b: Sum<u32, u32> = Sum::Left(42);
512 let d = <Sum<u32, u32>>::diff(&a, &b);
513 assert!(d.is_empty());
514 }
515
516 #[test]
517 fn sum_diff_same_variant_different_value() {
518 let a: Sum<u32, u32> = Sum::Left(1);
519 let b: Sum<u32, u32> = Sum::Left(2);
520 let d = <Sum<u32, u32>>::diff(&a, &b);
521 assert!(!d.is_empty());
522 assert_eq!(d.change_count(), 1);
523 }
524
525 #[test]
526 fn sum_diff_variant_changed() {
527 let a: Sum<u32, u32> = Sum::Left(1);
528 let b: Sum<u32, u32> = Sum::Right(2);
529 let d = <Sum<u32, u32>>::diff(&a, &b);
530 assert!(!d.is_empty());
531 assert_eq!(d.change_count(), 1);
532 }
533
534 #[test]
535 fn sum_patch_same_variant() {
536 let a: Sum<u32, u32> = Sum::Left(1);
537 let b: Sum<u32, u32> = Sum::Left(2);
538 let d = <Sum<u32, u32>>::diff(&a, &b);
539 let patched = <Sum<u32, u32>>::patch(&a, &d);
540 assert_eq!(patched, b);
541 }
542
543 #[test]
544 fn sum_patch_variant_change() {
545 let a: Sum<u32, u32> = Sum::Left(1);
546 let b: Sum<u32, u32> = Sum::Right(99);
547 let d = <Sum<u32, u32>>::diff(&a, &b);
548 let patched = <Sum<u32, u32>>::patch(&a, &d);
549 assert_eq!(patched, b);
550 }
551
552 #[test]
555 fn point_diff_same() {
556 let p = Point { x: 1.0, y: 2.0 };
557 let d = generic_diff(&p, &p);
558 assert!(d.is_empty());
559 }
560
561 #[test]
562 fn point_diff_one_field_changed() {
563 let a = Point { x: 1.0, y: 2.0 };
564 let b = Point { x: 1.0, y: 3.0 };
565 let d = generic_diff(&a, &b);
566 assert!(!d.is_empty());
567 assert_eq!(d.change_count(), 1);
568 }
569
570 #[test]
571 fn point_diff_both_fields_changed() {
572 let a = Point { x: 1.0, y: 2.0 };
573 let b = Point { x: 3.0, y: 4.0 };
574 let d = generic_diff(&a, &b);
575 assert_eq!(d.change_count(), 2);
576 }
577
578 #[test]
579 fn point_patch_roundtrip() {
580 let a = Point { x: 1.0, y: 2.0 };
581 let b = Point { x: 3.0, y: 4.0 };
582 let d = generic_diff(&a, &b);
583 let patched = generic_patch(&a, &d);
584 assert_eq!(patched, b);
585 }
586
587 #[test]
588 fn point_patch_identity() {
589 let p = Point { x: 1.0, y: 2.0 };
590 let d = generic_diff(&p, &p);
591 let patched = generic_patch(&p, &d);
592 assert_eq!(patched, p);
593 }
594
595 #[test]
598 fn color_diff_same_unit_variant() {
599 let d = generic_diff(&Color::Red, &Color::Red);
600 assert!(d.is_empty());
601 }
602
603 #[test]
604 fn color_diff_different_unit_variants() {
605 let d = generic_diff(&Color::Red, &Color::Green);
606 assert!(!d.is_empty());
607 }
608
609 #[test]
610 fn color_diff_same_data_variant() {
611 let a = Color::Custom(255, 128, 0);
612 let d = generic_diff(&a, &a);
613 assert!(d.is_empty());
614 }
615
616 #[test]
617 fn color_diff_data_variant_changed() {
618 let a = Color::Custom(255, 128, 0);
619 let b = Color::Custom(255, 0, 0);
620 let d = generic_diff(&a, &b);
621 assert!(!d.is_empty());
622 }
623
624 #[test]
625 fn color_patch_variant_change() {
626 let a = Color::Red;
627 let b = Color::Custom(1, 2, 3);
628 let d = generic_diff(&a, &b);
629 let patched = generic_patch(&a, &d);
630 assert_eq!(patched, b);
631 }
632
633 #[test]
634 fn color_patch_roundtrip_all() {
635 let variants = [Color::Red, Color::Green, Color::Custom(10, 20, 30)];
636 for old in &variants {
637 for new in &variants {
638 let d = generic_diff(old, new);
639 let patched = generic_patch(old, &d);
640 assert_eq!(&patched, new, "failed: {old:?} -> {new:?}");
641 }
642 }
643 }
644
645 #[test]
648 fn delta_debug_same() {
649 let d: Delta<i32> = Delta::Same;
650 assert_eq!(format!("{d:?}"), "Same");
651 }
652
653 #[test]
654 fn delta_debug_changed() {
655 let d = Delta::Changed(42);
656 assert_eq!(format!("{d:?}"), "Changed(42)");
657 }
658
659 #[test]
662 fn product_diff_change_count() {
663 let a = Product(1u32, Product(2u32, Product(3u32, Unit)));
664 let b = Product(1u32, Product(9u32, Product(9u32, Unit)));
665 let d = <Product<u32, Product<u32, Product<u32, Unit>>>>::diff(&a, &b);
666 assert_eq!(d.change_count(), 2);
667 }
668
669 mod proptests {
672 use super::*;
673 use proptest::prelude::*;
674
675 fn arb_point() -> impl Strategy<Value = Point> {
676 (any::<f64>(), any::<f64>()).prop_map(|(x, y)| Point { x, y })
677 }
678
679 fn arb_color() -> impl Strategy<Value = Color> {
680 prop_oneof![
681 Just(Color::Red),
682 Just(Color::Green),
683 (any::<u8>(), any::<u8>(), any::<u8>())
684 .prop_map(|(r, g, b)| Color::Custom(r, g, b)),
685 ]
686 }
687
688 proptest! {
689 #[test]
690 fn point_roundtrip(old in arb_point(), new in arb_point()) {
691 let d = generic_diff(&old, &new);
692 let patched = generic_patch(&old, &d);
693 prop_assert_eq!(patched, new);
694 }
695
696 #[test]
697 fn point_self_diff_is_empty(p in arb_point()) {
698 let d = generic_diff(&p, &p);
699 prop_assert!(d.is_empty());
700 }
701
702 #[test]
703 fn point_self_patch_is_identity(p in arb_point()) {
704 let d = generic_diff(&p, &p);
705 let patched = generic_patch(&p, &d);
706 prop_assert_eq!(patched, p);
707 }
708
709 #[test]
710 fn color_roundtrip(old in arb_color(), new in arb_color()) {
711 let d = generic_diff(&old, &new);
712 let patched = generic_patch(&old, &d);
713 prop_assert_eq!(patched, new);
714 }
715
716 #[test]
717 fn color_self_diff_is_empty(c in arb_color()) {
718 let d = generic_diff(&c, &c);
719 prop_assert!(d.is_empty());
720 }
721
722 #[test]
723 fn color_self_patch_is_identity(c in arb_color()) {
724 let d = generic_diff(&c, &c);
725 let patched = generic_patch(&c, &d);
726 prop_assert_eq!(patched, c);
727 }
728
729 #[test]
730 fn leaf_i32_roundtrip(old in any::<i32>(), new in any::<i32>()) {
731 let d = i32::diff(&old, &new);
732 let patched = i32::patch(&old, &d);
733 prop_assert_eq!(patched, new);
734 }
735
736 #[test]
737 fn leaf_i32_self_diff_empty(v in any::<i32>()) {
738 let d = i32::diff(&v, &v);
739 prop_assert!(d.is_empty());
740 prop_assert_eq!(d.change_count(), 0);
741 }
742
743 #[test]
744 fn product_u32_triple_roundtrip(
745 a0 in any::<u32>(), a1 in any::<u32>(), a2 in any::<u32>(),
746 b0 in any::<u32>(), b1 in any::<u32>(), b2 in any::<u32>(),
747 ) {
748 let old = Product(a0, Product(a1, Product(a2, Unit)));
749 let new = Product(b0, Product(b1, Product(b2, Unit)));
750 let d = <Product<u32, Product<u32, Product<u32, Unit>>>>::diff(&old, &new);
751 let patched = <Product<u32, Product<u32, Product<u32, Unit>>>>::patch(&old, &d);
752 prop_assert_eq!(patched, new);
753 }
754
755 #[test]
756 fn sum_roundtrip(
757 old_left in any::<bool>(), old_val in any::<u32>(),
758 new_left in any::<bool>(), new_val in any::<u32>(),
759 ) {
760 let old: Sum<u32, u32> = if old_left { Sum::Left(old_val) } else { Sum::Right(old_val) };
761 let new: Sum<u32, u32> = if new_left { Sum::Left(new_val) } else { Sum::Right(new_val) };
762 let d = <Sum<u32, u32>>::diff(&old, &new);
763 let patched = <Sum<u32, u32>>::patch(&old, &d);
764 prop_assert_eq!(patched, new);
765 }
766
767 #[test]
768 fn change_count_bounded_by_fields(
769 old in arb_point(), new in arb_point()
770 ) {
771 let d = generic_diff(&old, &new);
772 prop_assert!(d.change_count() <= 2);
773 }
774
775 #[test]
776 fn diff_same_implies_zero_changes(p in arb_point()) {
777 let d = generic_diff(&p, &p);
778 prop_assert_eq!(d.change_count(), 0);
779 }
780 }
781 }
782}