serde_diff/
implementation.rs

1use crate::{
2    apply::ApplyContext,
3    difference::{DiffCommandRef, DiffContext, DiffPathElementValue},
4    SerdeDiff,
5};
6
7use serde::{de, ser::SerializeSeq, Deserialize, Serialize};
8
9use std::{
10    collections::{BTreeMap, HashMap},
11    hash::Hash,
12};
13
14macro_rules! array_impls {
15    ($($len:tt)+) => {
16        $(
17            impl<T: $crate::SerdeDiff + serde::Serialize + for<'a> serde::Deserialize<'a>> $crate::SerdeDiff for [T; $len] {
18                fn diff<'a, S: serde::ser::SerializeSeq>(
19                    &self,
20                    ctx: &mut $crate::difference::DiffContext<'a, S>,
21                    other: &Self,
22                ) -> Result<bool, S::Error> {
23                    use $crate::difference::DiffCommandRef;
24
25                    let mut need_exit = false;
26                    let mut changed = false;
27                    for (idx, (self_item, other_item)) in self.iter().zip(other.iter()).enumerate() {
28                        ctx.push_collection_index(idx);
29                        if <T as $crate::SerdeDiff>::diff(self_item, ctx, other_item)? {
30                            need_exit = true;
31                            changed = true;
32                        }
33                        ctx.pop_path_element()?;
34                    }
35                    if need_exit {
36                        ctx.save_command::<()>(&DiffCommandRef::Exit, true, false)?;
37                    }
38                    Ok(changed)
39                }
40
41                fn apply<'de, A>(
42                    &mut self,
43                    seq: &mut A,
44                    ctx: &mut $crate::apply::ApplyContext,
45                ) -> Result<bool, <A as serde::de::SeqAccess<'de>>::Error>
46                where
47                    A: serde::de::SeqAccess<'de>,
48                {
49                    let mut changed = false;
50                    while let Some(cmd) = ctx.read_next_command::<A, T>(seq)? {
51                        use $crate::difference::DiffCommandValue::*;
52                        use $crate::difference::DiffPathElementValue::*;
53                        match cmd {
54                            // we should not be getting fields when reading collection commands
55                            Enter(Field(_)) => {
56                                ctx.skip_value(seq)?;
57                                break;
58                            }
59                            Enter(CollectionIndex(idx)) => {
60                                if let Some(value_ref) = self.get_mut(idx) {
61                                    changed |= <T as $crate::SerdeDiff>::apply(value_ref, seq, ctx)?;
62                                } else {
63                                    ctx.skip_value(seq)?;
64                                }
65                            }
66                            _ => break,
67                        }
68                    }
69                    Ok(changed)
70                }
71            }
72        )+
73    }
74}
75
76array_impls! {
77    01 02 03 04 05 06 07 08 09 10
78    11 12 13 14 15 16 17 18 19 20
79    21 22 23 24 25 26 27 28 29 30
80    31 32
81    40 48 50 56 64 72 96 100 128 160 192 200 224 256 384 512
82    768 1024 2048 4096 8192 16384 32768 65536
83}
84
85macro_rules! tuple_impls {
86    ($($len:expr => ($($n:tt $name:ident)+))+) => {
87        $(
88            impl<$($name),+> $crate::SerdeDiff for ($($name,)+)
89            where
90                $($name: $crate::SerdeDiff + serde::Serialize + for<'a> serde::Deserialize<'a>,)+
91            {
92                fn diff<'a, S: serde::ser::SerializeSeq>(
93                    &self,
94                    ctx: &mut $crate::difference::DiffContext<'a, S>,
95                    other: &Self,
96                ) -> Result<bool, S::Error> {
97                    let mut changed = false;
98                    $(
99                        ctx.push_field(stringify!($n));
100                        changed |= <$name as $crate::SerdeDiff>::diff(&self.$n, ctx, &other.$n)?;
101                        ctx.pop_path_element()?;
102                    )+
103                    Ok(changed)
104                }
105
106                fn apply<'de, A>(
107                    &mut self,
108                    seq: &mut A,
109                    ctx: &mut $crate::apply::ApplyContext,
110                ) -> Result<bool, <A as serde::de::SeqAccess<'de>>::Error>
111                where
112                    A: serde::de::SeqAccess<'de>,
113                {
114                    let mut changed = false;
115                    while let Some($crate::difference::DiffPathElementValue::Field(element)) = ctx.next_path_element(seq)? {
116                        match element.as_ref() {
117                            $(
118                                stringify!($n) => changed |= <$name as $crate::SerdeDiff>::apply(&mut self.$n, seq, ctx)?,
119                            )+
120                            _ => ctx.skip_value(seq)?,
121                        }
122                    }
123                    Ok(changed)
124                }
125            }
126        )+
127    }
128}
129
130tuple_impls! {
131    1 => (0 T0)
132    2 => (0 T0 1 T1)
133    3 => (0 T0 1 T1 2 T2)
134    4 => (0 T0 1 T1 2 T2 3 T3)
135    5 => (0 T0 1 T1 2 T2 3 T3 4 T4)
136    6 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5)
137    7 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6)
138    8 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7)
139    9 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8)
140    10 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9)
141    11 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10)
142    12 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11)
143    13 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12)
144    14 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13)
145    15 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14)
146    16 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15)
147}
148/// Implement SerdeDiff on a "map-like" type such as HashMap.
149macro_rules! map_serde_diff {
150    ($t:ty, $($extra_traits:path),*) => {
151        impl<K, V> SerdeDiff for $t
152        where
153            K: SerdeDiff + Serialize + for<'a> Deserialize<'a> $(+ $extra_traits)*, // + Hash + Eq,
154            V: SerdeDiff + Serialize + for<'a> Deserialize<'a>,
155        {
156            fn diff<'a, S: SerializeSeq>(
157                &self,
158                ctx: &mut $crate::difference::DiffContext<'a, S>,
159                other: &Self,
160            ) -> Result<bool, S::Error> {
161                use $crate::difference::DiffCommandRef;
162
163                let mut changed = false;
164
165                // TODO: detect renames
166                for (key, self_value) in self.iter() {
167                    match other.get(key) {
168                        Some(other_value) => {
169                            let save_closure = |serializer: &mut S| serializer.serialize_element(&DiffCommandRef::EnterKey(key));
170                            let mut subctx = ctx.reborrow();
171                            subctx.push_field_element(&save_closure);
172                            if <V as SerdeDiff>::diff(self_value, &mut subctx, other_value)? {
173                                changed = true;
174                            }
175                        },
176                        None => {
177                            ctx.save_command(&DiffCommandRef::RemoveKey(key), true, true)?;
178                            changed = true;
179                        },
180                    }
181                }
182
183                for (key, other_value) in other.iter() {
184                    if !self.contains_key(key) {
185                        ctx.save_command(&DiffCommandRef::AddKey(key), true, true)?;
186                        ctx.save_command(&DiffCommandRef::Value(other_value), true, true)?;
187                        changed = true;
188                    }
189                }
190
191                if changed {
192                    ctx.save_command::<()>(&DiffCommandRef::Exit, true, false)?;
193                }
194                Ok(changed)
195            }
196
197            fn apply<'de, A>(
198                &mut self,
199                seq: &mut A,
200                ctx: &mut ApplyContext,
201            ) -> Result<bool, <A as de::SeqAccess<'de>>::Error>
202            where
203                A: de::SeqAccess<'de>,
204            {
205                let mut changed = false;
206                while let Some(cmd) = ctx.read_next_command::<A, K>(seq)? {
207                    use $crate::difference::DiffCommandValue::*;
208                    use $crate::difference::DiffPathElementValue::*;
209                    match cmd {
210                        // we should not be getting fields when reading collection commands
211                        Enter(Field(_)) => {
212                            ctx.skip_value(seq)?;
213                            break;
214                        }
215                        AddKey(key) => if let Some(Value(v)) = ctx.read_next_command(seq)? {
216                            //changed |= self.insert(key, v).map(|old_val| v != old_val).unwrap_or(true);
217                            self.insert(key, v);
218                            changed = true;
219                        } else {
220                            panic!("Expected value after AddKey");
221                        }
222                        EnterKey(key) => if let Some(value_ref) = self.get_mut(&key) {
223                            changed |= <V as SerdeDiff>::apply(value_ref, seq, ctx)?;
224                        } else {
225                            ctx.skip_value(seq)?;
226                        }
227                        RemoveKey(key) => changed |= self.remove(&key).is_some(),
228                        _ => break,
229                    }
230                }
231                Ok(changed)
232            }
233        }
234    };
235}
236
237map_serde_diff!(HashMap<K, V>, Hash, Eq);
238map_serde_diff!(BTreeMap<K, V>, Ord);
239
240/// Implements SerdeDiff on a type given that it impls Serialize + Deserialize + PartialEq.
241/// This makes the type a "terminal" type in the SerdeDiff hierarchy, meaning deeper inspection
242/// will not be possible. Use the SerdeDiff derive macro for recursive field inspection.
243#[macro_export]
244macro_rules! opaque_serde_diff {
245    ($t:ty) => {
246        impl SerdeDiff for $t {
247            fn diff<'a, S: $crate::_serde::ser::SerializeSeq>(
248                &self,
249                ctx: &mut $crate::DiffContext<'a, S>,
250                other: &Self,
251            ) -> Result<bool, S::Error> {
252                if self != other {
253                    ctx.save_value(other)?;
254                    Ok(true)
255                } else {
256                    Ok(false)
257                }
258            }
259
260            fn apply<'de, A>(
261                &mut self,
262                seq: &mut A,
263                ctx: &mut $crate::ApplyContext,
264            ) -> Result<bool, <A as $crate::_serde::de::SeqAccess<'de>>::Error>
265            where
266                A: $crate::_serde::de::SeqAccess<'de>,
267            {
268                ctx.read_value(seq, self)
269            }
270        }
271    };
272}
273
274// Implement `SerdeDiff` for primitive types and types defined in the standard library.
275opaque_serde_diff!(bool);
276opaque_serde_diff!(isize);
277opaque_serde_diff!(i8);
278opaque_serde_diff!(i16);
279opaque_serde_diff!(i32);
280opaque_serde_diff!(i64);
281opaque_serde_diff!(usize);
282opaque_serde_diff!(u8);
283opaque_serde_diff!(u16);
284opaque_serde_diff!(u32);
285opaque_serde_diff!(u64);
286opaque_serde_diff!(i128);
287opaque_serde_diff!(u128);
288opaque_serde_diff!(f32);
289opaque_serde_diff!(f64);
290opaque_serde_diff!(char);
291opaque_serde_diff!(String);
292opaque_serde_diff!(std::ffi::CString);
293#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
294opaque_serde_diff!(std::ffi::OsString);
295opaque_serde_diff!(std::num::NonZeroU8);
296opaque_serde_diff!(std::num::NonZeroU16);
297opaque_serde_diff!(std::num::NonZeroU32);
298opaque_serde_diff!(std::num::NonZeroU64);
299opaque_serde_diff!(std::time::Duration);
300opaque_serde_diff!(std::time::SystemTime);
301opaque_serde_diff!(std::net::IpAddr);
302opaque_serde_diff!(std::net::Ipv4Addr);
303opaque_serde_diff!(std::net::Ipv6Addr);
304opaque_serde_diff!(std::net::SocketAddr);
305opaque_serde_diff!(std::net::SocketAddrV4);
306opaque_serde_diff!(std::net::SocketAddrV6);
307opaque_serde_diff!(std::path::PathBuf);
308
309impl<T: SerdeDiff + Serialize + for<'a> Deserialize<'a>> SerdeDiff for Option<T> {
310    fn diff<'a, S: SerializeSeq>(
311        &self,
312        ctx: &mut DiffContext<'a, S>,
313        other: &Self,
314    ) -> Result<bool, S::Error> {
315        let mut self_iter = self.iter();
316        let mut other_iter = other.iter();
317        let mut idx = 0;
318        let mut need_exit = false;
319        let mut changed = false;
320        loop {
321            let self_item = self_iter.next();
322            let other_item = other_iter.next();
323            match (self_item, other_item) {
324                (None, None) => break,
325                (Some(_), None) => {
326                    let mut num_to_remove = 1;
327                    while self_iter.next().is_some() {
328                        num_to_remove += 1;
329                    }
330                    ctx.save_command::<()>(&DiffCommandRef::Remove(num_to_remove), true, true)?;
331                    changed = true;
332                }
333                (None, Some(other_item)) => {
334                    ctx.save_command::<()>(
335                        &DiffCommandRef::Enter(DiffPathElementValue::AddToCollection),
336                        false,
337                        true,
338                    )?;
339                    ctx.save_command(&DiffCommandRef::Value(other_item), true, true)?;
340                    need_exit = true;
341                    changed = true;
342                }
343                (Some(self_item), Some(other_item)) => {
344                    ctx.push_collection_index(idx);
345                    if <T as SerdeDiff>::diff(self_item, ctx, other_item)? {
346                        need_exit = true;
347                        changed = true;
348                    }
349                    ctx.pop_path_element()?;
350                }
351            }
352            idx += 1;
353        }
354        if need_exit {
355            ctx.save_command::<()>(&DiffCommandRef::Exit, true, false)?;
356        }
357        Ok(changed)
358    }
359
360    fn apply<'de, A>(
361        &mut self,
362        seq: &mut A,
363        ctx: &mut ApplyContext,
364    ) -> Result<bool, <A as de::SeqAccess<'de>>::Error>
365    where
366        A: de::SeqAccess<'de>,
367    {
368        let mut changed = false;
369        while let Some(cmd) = ctx.read_next_command::<A, T>(seq)? {
370            use crate::difference::DiffCommandValue::*;
371            use DiffPathElementValue::*;
372
373            match cmd {
374                // we should not be getting fields when reading collection commands
375                Enter(Field(_)) => {
376                    ctx.skip_value(seq)?;
377                    break;
378                }
379                Enter(CollectionIndex(0)) => {
380                    if let Some(value_ref) = self {
381                        changed |= <T as SerdeDiff>::apply(value_ref, seq, ctx)?;
382                    } else {
383                        ctx.skip_value(seq)?;
384                    }
385                }
386                Enter(AddToCollection) => {
387                    if let Value(v) = ctx
388                        .read_next_command(seq)?
389                        .expect("Expected value after AddToCollection")
390                    {
391                        debug_assert!(self.is_none());
392                        changed = true;
393                        *self = Some(v);
394                    } else {
395                        panic!("Expected value after AddToCollection");
396                    }
397                }
398                Remove(1) => {
399                    *self = None;
400                    changed = true;
401                    break;
402                }
403                _ => break,
404            }
405        }
406        Ok(changed)
407    }
408}
409
410type Unit = ();
411opaque_serde_diff!(Unit);