schemadoc_diff/
core.rs

1use serde::{Deserialize, Deserializer, Serialize};
2use std::borrow::Cow;
3use std::fmt::Debug;
4use std::marker::PhantomData;
5
6use indexmap::{IndexMap, IndexSet};
7use serde::__private::de::{Content, ContentRefDeserializer};
8use serde_json::Value;
9use std::ops::Deref;
10use std::sync::Arc;
11
12pub trait Prepare {
13    fn prepare(self) -> Self;
14}
15
16pub trait DiffContext {
17    fn removing(&self) -> Self;
18    fn switch_flow(&self) -> Self;
19
20    fn is_direct_flow(&self) -> bool;
21
22    fn add_visited_reference_source(&self, reference: &str) -> Self;
23    fn check_visited_reference_source(&self, reference: &str) -> usize;
24    fn add_visited_reference_target(&self, reference: &str) -> Self;
25    fn check_visited_reference_target(&self, reference: &str) -> usize;
26}
27
28pub trait ComponentContainer<T> {
29    fn deref_source(&self, reference: &str) -> Option<&T>;
30    fn deref_target(&self, reference: &str) -> Option<&T>;
31}
32
33pub trait DiffCache<O> {
34    fn get_diff(&self, reference: &str) -> Option<Arc<DiffResult<O>>>;
35    fn set_diff(&self, reference: &str, component: Arc<DiffResult<O>>);
36}
37
38#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
39#[serde(rename_all = "lowercase", tag = "t", content = "v")]
40pub enum DiffResult<T> {
41    #[serde(rename = "n")]
42    None,
43    #[serde(rename = "=")]
44    Same(T),
45    #[serde(rename = "+")]
46    Added(T),
47    #[serde(rename = "~")]
48    Updated(T, Option<Box<T>>),
49    #[serde(rename = "-")]
50    Removed(T),
51}
52
53impl<T> DiffResult<T> {
54    pub fn is_none(&self) -> bool {
55        matches!(self, DiffResult::None)
56    }
57
58    pub fn is_same(&self) -> bool {
59        matches!(self, DiffResult::Same(_))
60    }
61
62    pub fn is_added(&self) -> bool {
63        matches!(self, DiffResult::Added(_))
64    }
65
66    pub fn is_updated(&self) -> bool {
67        matches!(self, DiffResult::Updated(_, _))
68    }
69
70    pub fn is_upserted(&self) -> bool {
71        self.is_added() || self.is_updated()
72    }
73
74    pub fn is_removed(&self) -> bool {
75        matches!(self, DiffResult::Removed(_))
76    }
77
78    pub fn is_same_or_none(&self) -> bool {
79        self.is_same() || self.is_none()
80    }
81
82    pub fn exists(&self) -> bool {
83        !(self.is_none() || self.is_removed())
84    }
85
86    pub fn new<C: DiffContext>(mut value: DiffResult<T>, context: &C) -> Self {
87        if !context.is_direct_flow() {
88            value = match value {
89                DiffResult::Added(v) => DiffResult::Removed(v),
90                DiffResult::Removed(v) => DiffResult::Added(v),
91
92                DiffResult::Updated(new, Some(old)) => {
93                    DiffResult::Updated(*old, Some(Box::new(new)))
94                }
95                result => result,
96            }
97        }
98
99        value
100    }
101
102    pub fn get(&self) -> Option<&T> {
103        match self {
104            DiffResult::None => None,
105
106            DiffResult::Same(v) => Some(v),
107            DiffResult::Added(v) => Some(v),
108            DiffResult::Removed(v) => Some(v),
109            DiffResult::Updated(v, _) => Some(v),
110        }
111    }
112
113    pub fn take(self) -> Option<T> {
114        match self {
115            DiffResult::None => None,
116
117            DiffResult::Same(v) => Some(v),
118            DiffResult::Added(v) => Some(v),
119            DiffResult::Removed(v) => Some(v),
120            DiffResult::Updated(v, _) => Some(v),
121        }
122    }
123
124    pub fn as_ref(&self) -> DiffResult<&T> {
125        match &self {
126            DiffResult::None => DiffResult::None,
127            DiffResult::Same(v) => DiffResult::Same(v),
128            DiffResult::Added(v) => DiffResult::Added(v),
129            DiffResult::Removed(v) => DiffResult::Removed(v),
130
131            DiffResult::Updated(new, old) => DiffResult::Updated(
132                new,
133                old.as_ref().map(|old| Box::new(&**old)),
134            ),
135        }
136    }
137}
138
139// Marker trait
140pub trait Referencable {}
141
142impl Referencable for Value {}
143
144pub trait Diff<With, Output, Context: DiffContext> {
145    fn diff(
146        &self,
147        new: Option<&With>,
148        context: &Context,
149    ) -> DiffResult<Output>;
150}
151
152pub trait Empty {
153    fn is_empty(&self) -> bool;
154}
155
156pub trait Keyed<C> {
157    fn key(&self, c: C) -> String;
158}
159
160impl<T, O, C: DiffContext> Diff<T, O, C> for Option<T>
161where
162    T: Diff<T, O, C> + Debug,
163{
164    fn diff(&self, new: Option<&T>, context: &C) -> DiffResult<O> {
165        match (self, new) {
166            (None, None) => DiffResult::new(DiffResult::None, context),
167            (Some(v1), Some(v2)) => v1.diff(Some(v2), context),
168            (Some(v1), None) => v1.diff(None, context),
169            (None, Some(v2)) => {
170                if context.is_direct_flow() {
171                    v2.diff(None, &context.switch_flow())
172                } else {
173                    v2.diff(None, context)
174                }
175            }
176        }
177    }
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
181#[serde(untagged)]
182pub enum Either<L, R> {
183    Left(L),
184    Right(Box<R>),
185}
186
187#[derive(Debug, Clone, Serialize, Deserialize)]
188#[serde(rename_all = "lowercase", tag = "t", content = "v")]
189pub enum EitherDiff<LD, RD> {
190    #[serde(rename = "l")]
191    Left(DiffResult<LD>),
192    #[serde(rename = "r")]
193    Right(Box<DiffResult<RD>>),
194
195    #[serde(rename = "tr")]
196    ToRight(Box<DiffResult<RD>>),
197    #[serde(rename = "tl")]
198    ToLeft(Box<DiffResult<LD>>),
199}
200
201impl<LD, RD> Empty for EitherDiff<LD, RD> {
202    fn is_empty(&self) -> bool {
203        match self {
204            EitherDiff::Left(l) => l.is_same_or_none(),
205            EitherDiff::Right(r) => r.is_same_or_none(),
206            _ => false,
207        }
208    }
209}
210
211impl<L, R, LD, RD, C: DiffContext> Diff<Either<L, R>, EitherDiff<LD, RD>, C>
212    for Either<L, R>
213where
214    L: Diff<L, LD, C>,
215    R: Diff<R, RD, C>,
216{
217    fn diff(
218        &self,
219        new: Option<&Either<L, R>>,
220        context: &C,
221    ) -> DiffResult<EitherDiff<LD, RD>> {
222        let diff = match new {
223            None => DiffResult::Removed(match self {
224                Either::Left(l) => {
225                    EitherDiff::Left(l.diff(None, &context.removing()))
226                }
227                Either::Right(r) => EitherDiff::Right(Box::new(
228                    r.diff(None, &context.removing()),
229                )),
230            }),
231            Some(value) => {
232                let diff = match value {
233                    Either::Left(vl) => match self {
234                        Either::Left(l) => {
235                            EitherDiff::Left(l.diff(Option::from(vl), context))
236                        }
237                        Either::Right(_) => EitherDiff::ToLeft(Box::new(
238                            vl.diff(None, context),
239                        )),
240                    },
241                    Either::Right(vr) => match self {
242                        Either::Left(_) => EitherDiff::ToRight(Box::new(
243                            vr.diff(None, context),
244                        )),
245                        Either::Right(r) => EitherDiff::Right(Box::new(
246                            r.diff(Some(vr), context),
247                        )),
248                    },
249                };
250
251                if diff.is_empty() {
252                    DiffResult::Same(diff)
253                } else {
254                    DiffResult::Updated(diff, None)
255                }
256            }
257        };
258        DiffResult::new(diff, context)
259    }
260}
261
262pub trait ReferenceDescriptor {
263    fn reference(&self) -> &str;
264}
265
266#[derive(Debug, Clone, Serialize)]
267#[serde(untagged)]
268pub enum MayBeRefCore<T, R: ReferenceDescriptor> {
269    Ref(R),
270    Value(T),
271}
272
273impl<'de, T, R: ReferenceDescriptor> Deserialize<'de> for MayBeRefCore<T, R>
274where
275    T: Deserialize<'de>,
276    R: Deserialize<'de>,
277{
278    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
279    where
280        D: Deserializer<'de>,
281    {
282        let content = match <Content as Deserialize>::deserialize(deserializer)
283        {
284            Ok(val) => val,
285            Err(err) => {
286                return Err(err);
287            }
288        };
289
290        if let Ok(ok) = Result::map(
291            <R as Deserialize>::deserialize(
292                ContentRefDeserializer::<D::Error>::new(&content),
293            ),
294            MayBeRefCore::Ref,
295        ) {
296            return Ok(ok);
297        }
298
299        let mut track = serde_path_to_error::Track::new();
300        let de = serde_path_to_error::Deserializer::new(
301            ContentRefDeserializer::<D::Error>::new(&content),
302            &mut track,
303        );
304
305        match <T as Deserialize>::deserialize(de) {
306            Ok(t) => Ok(MayBeRefCore::Value(t)),
307            Err(err) => {
308                eprintln!("Err path: {} --- {}", track.path(), &err);
309                Err(err)
310            }
311        }
312    }
313}
314
315impl<T, R: ReferenceDescriptor> MayBeRefCore<T, R> {
316    pub fn is_ref(&self) -> bool {
317        matches!(self, MayBeRefCore::Ref(_))
318    }
319
320    pub fn reference(&self) -> Option<&str> {
321        match self {
322            MayBeRefCore::Ref(rd) => Some(rd.reference()),
323            _ => None,
324        }
325    }
326
327    pub fn value(&self) -> Option<&T> {
328        match self {
329            MayBeRefCore::Value(v) => Some(v),
330            _ => None,
331        }
332    }
333
334    pub fn value_mut(&mut self) -> Option<&mut T> {
335        match self {
336            MayBeRefCore::Value(v) => Some(v),
337            _ => None,
338        }
339    }
340}
341
342#[derive(Debug, Clone, Serialize, Deserialize)]
343#[serde(rename_all = "lowercase", tag = "t", content = "v")]
344pub enum MayBeRefCoreDiff<T: Referencable, R: ReferenceDescriptor> {
345    #[serde(rename = "r")]
346    Ref(R),
347    #[serde(rename = "v")]
348    Value(Arc<DiffResult<T>>),
349}
350
351impl<T, R> Keyed<usize> for MayBeRefCore<T, R>
352where
353    T: Keyed<usize>,
354    R: ReferenceDescriptor,
355{
356    fn key(&self, c: usize) -> String {
357        match self {
358            MayBeRefCore::Value(value) => value.key(c),
359            MayBeRefCore::Ref(value) => value.reference().to_owned(),
360        }
361    }
362}
363
364impl<T, O, R, C: DiffContext>
365    Diff<MayBeRefCore<T, R>, MayBeRefCoreDiff<O, R>, C> for MayBeRefCore<T, R>
366where
367    T: Diff<T, O, C> + Clone + Debug + 'static,
368    O: Referencable + Clone + Debug + 'static,
369    R: ReferenceDescriptor + Clone + Debug + 'static,
370    C: DiffContext + ComponentContainer<T> + DiffCache<O> + ToOwned<Owned = C>,
371{
372    fn diff(
373        &self,
374        new: Option<&MayBeRefCore<T, R>>,
375        context: &C,
376    ) -> DiffResult<MayBeRefCoreDiff<O, R>> {
377        let diff = match new {
378            None => DiffResult::Removed(match self {
379                MayBeRefCore::Ref(value) => {
380                    MayBeRefCoreDiff::Ref(value.clone())
381                }
382                MayBeRefCore::Value(value) => MayBeRefCoreDiff::Value(
383                    Arc::new(value.diff(None, context)),
384                ),
385            }),
386            Some(value) => {
387                let cached_diff = if let (
388                    MayBeRefCore::Ref(old_ref),
389                    MayBeRefCore::Ref(new_ref),
390                ) = (self, value)
391                {
392                    if old_ref.reference() == new_ref.reference() {
393                        context.get_diff(old_ref.reference())
394                    } else {
395                        None
396                    }
397                } else {
398                    None
399                };
400
401                let diff = if let Some(diff) = &cached_diff {
402                    Arc::clone(diff)
403                } else {
404                    let context =
405                        if let MayBeRefCore::Ref(old_ref) = self {
406                            Cow::Owned(context.add_visited_reference_source(
407                                old_ref.reference(),
408                            ))
409                        } else {
410                            Cow::Borrowed(context)
411                        };
412                    let context =
413                        if let MayBeRefCore::Ref(new_ref) = value {
414                            Cow::Owned(context.add_visited_reference_target(
415                                new_ref.reference(),
416                            ))
417                        } else {
418                            context
419                        };
420
421                    let (old_value, new_value) = match (self, value) {
422                        (
423                            MayBeRefCore::Ref(old_ref),
424                            MayBeRefCore::Ref(new_ref),
425                        ) => {
426                            let old_reference = old_ref.reference();
427                            let new_reference = new_ref.reference();
428
429                            let old_visited_count = context
430                                .check_visited_reference_source(old_reference);
431                            let new_visited_count = context
432                                .check_visited_reference_target(new_reference);
433
434                            if old_visited_count > 1 && new_visited_count > 1 {
435                                return DiffResult::None;
436                            }
437
438                            let source = context.deref_source(old_reference);
439                            let target = context.deref_target(new_reference);
440
441                            (source, target)
442                        }
443                        (
444                            MayBeRefCore::Value(old_value),
445                            MayBeRefCore::Value(new_value),
446                        ) => (Some(old_value), Some(new_value)),
447
448                        (
449                            MayBeRefCore::Value(old_value),
450                            MayBeRefCore::Ref(new_ref),
451                        ) => {
452                            let target =
453                                context.deref_target(new_ref.reference());
454                            (Some(old_value), target)
455                        }
456
457                        (
458                            MayBeRefCore::Ref(old_ref),
459                            MayBeRefCore::Value(new_value),
460                        ) => {
461                            let source =
462                                context.deref_source(old_ref.reference());
463                            (source, Some(new_value))
464                        }
465                    };
466
467                    let old_value = if let Some(value) = old_value {
468                        value
469                    } else {
470                        let diff = None.diff(new_value, &*context);
471                        return if diff.is_none() {
472                            DiffResult::new(DiffResult::None, &*context)
473                        } else {
474                            DiffResult::new(
475                                DiffResult::Added(MayBeRefCoreDiff::Value(
476                                    Arc::new(diff),
477                                )),
478                                &*context,
479                            )
480                        };
481                    };
482
483                    let new_value = if let Some(value) = new_value {
484                        value
485                    } else {
486                        // removed schema
487                        return self.diff(None, &*context);
488                    };
489
490                    Arc::new(old_value.diff(Some(new_value), &*context))
491                };
492
493                match (self, value) {
494                    (
495                        MayBeRefCore::Ref(old_ref),
496                        MayBeRefCore::Ref(new_ref),
497                    ) if old_ref.reference() == new_ref.reference() => {
498                        let result = match &*diff {
499                            DiffResult::None => DiffResult::None,
500                            DiffResult::Same(_) => DiffResult::Same(
501                                MayBeRefCoreDiff::Ref(old_ref.clone()),
502                            ),
503                            DiffResult::Added(_) => DiffResult::Added(
504                                MayBeRefCoreDiff::Ref(old_ref.clone()),
505                            ),
506                            DiffResult::Removed(_) => DiffResult::Removed(
507                                MayBeRefCoreDiff::Ref(old_ref.clone()),
508                            ),
509                            DiffResult::Updated(_, _) => DiffResult::Updated(
510                                MayBeRefCoreDiff::Ref(old_ref.clone()),
511                                None,
512                            ),
513                        };
514
515                        if cached_diff.is_none() {
516                            context.set_diff(old_ref.reference(), diff);
517                        }
518
519                        result
520                    }
521                    // Do not track `ref -> value` and `value -> ref`
522                    // since they are not representable changes for us,
523                    // we are trying to track only changes that on UI look different
524                    (_, _) => {
525                        if diff.is_same_or_none() {
526                            DiffResult::Same(MayBeRefCoreDiff::Value(diff))
527                        } else {
528                            DiffResult::Updated(
529                                MayBeRefCoreDiff::Value(diff),
530                                None,
531                            )
532                        }
533                    }
534                }
535            }
536        };
537
538        DiffResult::new(diff, context)
539    }
540}
541
542impl<V> Keyed<usize> for IndexMap<String, V> {
543    fn key(&self, idx: usize) -> String {
544        idx.to_string()
545    }
546}
547
548pub trait PathResolver {
549    /// Object implementing this trait can determine that two different
550    /// keys from src and tgt represents the same object and thus must be mapped
551    fn new<'a, T>(k1: T, k2: T) -> Self
552    where
553        T: Iterator<Item = &'a String>;
554
555    /// k1 is the key from src which must be mapped to tgt (k2) key
556    /// or to be returned not changed
557    fn k1tok2(&self, k1: &String) -> String;
558
559    /// k2 is the key from tgt which must be mapped to src (k1) key
560    /// or to be returned not changed
561    fn k2tok1(&self, k2: &String) -> String;
562}
563
564#[derive(Debug, Clone)]
565pub struct DefaultMapPathResolver;
566
567impl PathResolver for DefaultMapPathResolver {
568    fn new<'a, T>(_k1: T, _k2: T) -> Self
569    where
570        T: Iterator<Item = &'a String>,
571    {
572        Self
573    }
574
575    fn k1tok2(&self, k1: &String) -> String {
576        k1.to_owned()
577    }
578
579    fn k2tok1(&self, k2: &String) -> String {
580        k2.to_owned()
581    }
582}
583
584#[derive(Debug, Default, Clone, Serialize, Deserialize)]
585#[serde(transparent)]
586pub struct MapDiff<V, R = DefaultMapPathResolver>(
587    pub(crate) IndexMap<String, DiffResult<V>>,
588    #[serde(skip)] PhantomData<R>,
589);
590
591impl<V, R> Deref for MapDiff<V, R> {
592    type Target = IndexMap<String, DiffResult<V>>;
593
594    fn deref(&self) -> &Self::Target {
595        &self.0
596    }
597}
598
599impl<V, O, C, R> Diff<IndexMap<String, V>, MapDiff<O, R>, C>
600    for IndexMap<String, V>
601where
602    V: Diff<V, O, C> + Clone + Debug,
603    R: PathResolver,
604    C: DiffContext,
605    O: Debug,
606{
607    fn diff(
608        &self,
609        new: Option<&IndexMap<String, V>>,
610        context: &C,
611    ) -> DiffResult<MapDiff<O, R>> {
612        let diff = match new {
613            None => DiffResult::Removed(MapDiff(
614                self.iter()
615                    .map(|(key, value)| {
616                        (key.to_owned(), value.diff(None, &context.removing()))
617                    })
618                    .collect(),
619                PhantomData,
620            )),
621            Some(other) => {
622                let resolver = R::new(self.keys(), other.keys());
623
624                let mut result: IndexMap<String, DiffResult<O>> =
625                    Default::default();
626
627                for (k1, v1) in self.iter() {
628                    let k2 = resolver.k1tok2(k1);
629
630                    if let Some(v2) = other.get(&k2) {
631                        result.insert(k2, v1.diff(Some(v2), context));
632                    } else {
633                        result.insert(k2, v1.diff(None, context));
634                    }
635                }
636
637                result.extend(other.iter().filter_map(|(k2, value)| {
638                    let k1 = resolver.k2tok1(k2);
639                    if self.contains_key(&k1) {
640                        None
641                    } else {
642                        Some((k1, None.diff(Some(value), context)))
643                    }
644                }));
645
646                let is_same =
647                    result.iter().all(|(_key, value)| value.is_same_or_none());
648
649                let diff = MapDiff(result, PhantomData);
650
651                if is_same {
652                    DiffResult::Same(diff)
653                } else {
654                    DiffResult::Updated(diff, None)
655                }
656            }
657        };
658        DiffResult::new(diff, context)
659    }
660}
661
662pub trait VecDiffTransformer<T> {
663    fn transform(collection: T) -> T;
664}
665
666#[derive(Default, Debug, Clone)]
667pub struct DefaultVecDiffTransformer;
668
669impl<T> VecDiffTransformer<T> for DefaultVecDiffTransformer {
670    fn transform(collection: T) -> T {
671        collection
672    }
673}
674
675#[derive(Debug, Default, Clone, Serialize, Deserialize)]
676#[serde(transparent)]
677pub struct VecDiff<T, S = DefaultVecDiffTransformer>(
678    pub Vec<DiffResult<T>>,
679    #[serde(skip)] PhantomData<S>,
680);
681
682impl<T, S> Deref for VecDiff<T, S> {
683    type Target = Vec<DiffResult<T>>;
684
685    fn deref(&self) -> &Self::Target {
686        &self.0
687    }
688}
689
690impl<T, O, C: DiffContext, S> Diff<Vec<T>, VecDiff<O, S>, C> for Vec<T>
691where
692    T: Diff<T, O, C> + Keyed<usize> + Debug,
693    S: VecDiffTransformer<Vec<DiffResult<O>>>,
694    O: Debug,
695{
696    fn diff(
697        &self,
698        new: Option<&Vec<T>>,
699        context: &C,
700    ) -> DiffResult<VecDiff<O, S>> {
701        let diff = match new {
702            None => DiffResult::Removed(VecDiff(
703                self.iter().map(|x| x.diff(None, context)).collect(),
704                PhantomData,
705            )),
706            Some(value) => {
707                let o: IndexMap<_, _> = self
708                    .iter()
709                    .enumerate()
710                    .map(|(idx, v)| (v.key(idx), v))
711                    .collect();
712
713                let n: IndexMap<_, _> = value
714                    .iter()
715                    .enumerate()
716                    .map(|(idx, v)| (v.key(idx), v))
717                    .collect();
718
719                let o_keys: IndexSet<_> = o.keys().collect();
720                let n_keys: IndexSet<_> = n.keys().collect();
721
722                let new_keys: IndexSet<_> =
723                    n_keys.difference(&o_keys).collect();
724                let removed_keys: IndexSet<_> =
725                    o_keys.difference(&n_keys).collect();
726                let updated_keys: IndexSet<_> =
727                    o_keys.intersection(&n_keys).collect();
728
729                let added: Vec<_> = new_keys
730                    .into_iter()
731                    .map(|key| {
732                        let new = n.get(*key).expect("key must present");
733                        None.diff(Some(*new), context)
734                    })
735                    .collect();
736
737                let removed: Vec<_> = removed_keys
738                    .into_iter()
739                    .map(|key| {
740                        let old = o.get(*key).expect("key must present");
741                        old.diff(None, context)
742                    })
743                    .collect();
744
745                let updated: Vec<_> = updated_keys
746                    .into_iter()
747                    .map(|key| {
748                        let old = o.get(*key).expect("key must present");
749                        let new = n.get(*key).expect("key must present");
750                        old.diff(Some(*new), context)
751                    })
752                    .collect();
753
754                let values: Vec<_> = added
755                    .into_iter()
756                    .chain(updated.into_iter())
757                    .chain(removed.into_iter())
758                    .collect();
759
760                let is_same =
761                    values.iter().all(|value| value.is_same_or_none());
762
763                let values = S::transform(values);
764
765                let diff = VecDiff(values, PhantomData);
766
767                if is_same {
768                    DiffResult::Same(diff)
769                } else {
770                    DiffResult::Updated(diff, None)
771                }
772            }
773        };
774        DiffResult::new(diff, context)
775    }
776}
777
778impl<C: DiffContext> Diff<Value, Value, C> for Value {
779    fn diff(&self, new: Option<&Value>, context: &C) -> DiffResult<Value> {
780        let diff = match new {
781            None => DiffResult::Removed(self.clone()),
782            Some(value) => {
783                if self == value {
784                    DiffResult::Same(value.clone())
785                } else {
786                    DiffResult::Updated(
787                        value.clone(),
788                        Some(Box::new(self.clone())),
789                    )
790                }
791            }
792        };
793        DiffResult::new(diff, context)
794    }
795}
796
797impl Keyed<usize> for Value {
798    fn key(&self, idx: usize) -> String {
799        match self {
800            Value::Null => "null".to_owned(),
801            Value::Bool(value) => format!("[bool]:{value}"),
802            Value::Number(value) => format!("[number]:{value}"),
803            Value::String(value) => format!("[string]:{value}"),
804            // TODO: add type to Value Array and Object keys
805            Value::Array(_value) => format!("[array]: {idx}"),
806            Value::Object(_value) => format!("[object]: {idx}"),
807        }
808    }
809}
810
811impl<'a> Keyed<usize> for &'a str {
812    fn key(&self, _: usize) -> String {
813        self.to_string()
814    }
815}
816
817impl Keyed<usize> for String {
818    fn key(&self, _: usize) -> String {
819        self.clone()
820    }
821}
822
823impl Keyed<usize> for (String, String) {
824    fn key(&self, _: usize) -> String {
825        self.0.clone()
826    }
827}
828
829macro_rules! impl_keyed_diff {
830    ($typ:ty) => {
831        impl Keyed<usize> for $typ {
832            fn key(&self, _: usize) -> String {
833                self.to_string()
834            }
835        }
836
837        impl<C: DiffContext> Diff<$typ, $typ, C> for $typ {
838            fn diff(
839                &self,
840                new: Option<&$typ>,
841                context: &C,
842            ) -> DiffResult<$typ> {
843                let diff = match new {
844                    None => DiffResult::Removed(*self),
845                    Some(value) => {
846                        if self == value {
847                            DiffResult::Same(*value)
848                        } else {
849                            DiffResult::Updated(*value, Some(Box::new(*self)))
850                        }
851                    }
852                };
853                DiffResult::new(diff, context)
854            }
855        }
856    };
857}
858
859impl_keyed_diff!(u32);
860impl_keyed_diff!(u64);
861impl_keyed_diff!(i32);
862impl_keyed_diff!(i64);
863impl_keyed_diff!(f32);
864impl_keyed_diff!(f64);
865impl_keyed_diff!(bool);
866impl_keyed_diff!(usize);
867
868impl<C: DiffContext> Diff<Self, String, C> for String {
869    fn diff(&self, new: Option<&String>, context: &C) -> DiffResult<String> {
870        let diff = match new {
871            None => DiffResult::Removed(self.clone()),
872            Some(value) => {
873                if self == value {
874                    DiffResult::Same(value.to_string())
875                } else {
876                    DiffResult::Updated(
877                        value.to_string(),
878                        Some(Box::new(self.to_string())),
879                    )
880                }
881            }
882        };
883        DiffResult::new(diff, context)
884    }
885}