serde_diff/
apply.rs

1use crate::{
2    difference::{
3        DeserWrapper, DiffCommandDeserWrapper, DiffCommandIgnoreValue, DiffCommandValue,
4        DiffPathElementValue,
5    },
6    Config, SerdeDiff,
7};
8use serde::{de, Deserialize};
9
10/// A deserializable structure that will apply a sequence of diff commands to the target
11///
12/// # Examples
13///
14/// ```rust
15/// use serde_diff::{SerdeDiff, Diff, Apply};
16/// use serde::{Serialize, Deserialize};
17/// #[derive(SerdeDiff, Serialize, Deserialize, PartialEq)]
18/// struct Test {
19///     a: i32,
20/// }
21/// let diff = Diff::serializable(&Test { a: 3 }, &Test { a: 5 });
22/// let msgpack_data = rmp_serde::to_vec_named(&diff).expect("failed to serialize diff");
23/// let mut deserializer = rmp_serde::Deserializer::new(msgpack_data.as_slice());
24/// let mut target = Test { a: 4 };
25/// Apply::apply(&mut deserializer, &mut target).expect("failed when deserializing diff");
26/// ```
27pub struct Apply<'a, T: SerdeDiff> {
28    pub(crate) target: &'a mut T,
29}
30
31impl<'a, 'de, T: SerdeDiff> Apply<'a, T> {
32    /// Create a deserializable apply, where the given target will be changed when the resulting
33    /// Apply struct is deserialized
34    pub fn deserializable(target: &'a mut T) -> Self {
35        Config::default().deserializable_apply(target)
36    }
37
38    /// Applies a sequence of diff commands to the target, as read by the deserializer
39    pub fn apply<D>(
40        deserializer: D,
41        target: &mut T,
42    ) -> Result<(), <D as de::Deserializer<'de>>::Error>
43    where
44        D: de::Deserializer<'de>,
45    {
46        Config::default().apply(deserializer, target)
47    }
48}
49
50impl<'a, 'de, T: SerdeDiff> de::DeserializeSeed<'de> for Apply<'a, T> {
51    type Value = ();
52    fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
53    where
54        D: de::Deserializer<'de>,
55    {
56        deserializer.deserialize_seq(self)
57    }
58}
59
60impl<'a, 'de, T: SerdeDiff> de::Visitor<'de> for Apply<'a, T> {
61    type Value = ();
62    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
63        write!(formatter, "a sequence containing DiffCommands")
64    }
65
66    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, <A as de::SeqAccess<'de>>::Error>
67    where
68        A: de::SeqAccess<'de>,
69    {
70        let mut ctx = ApplyContext {};
71        self.target.apply(&mut seq, &mut ctx)?;
72        Ok(())
73    }
74}
75
76/// Used during an apply operation for transient data used during the apply
77#[doc(hidden)]
78pub struct ApplyContext {}
79
80impl ApplyContext {
81    /// Returns the next element if it is a path. If it is a Value or Exit, it returns None.
82    pub fn next_path_element<'de, A>(
83        &mut self,
84        seq: &mut A,
85    ) -> Result<Option<DiffPathElementValue<'de>>, <A as de::SeqAccess<'de>>::Error>
86    where
87        A: de::SeqAccess<'de>,
88    {
89        use DiffCommandValue::*;
90        let element = match seq.next_element_seed(DiffCommandIgnoreValue {})? {
91            Some(Enter(element)) => Ok(Some(element)),
92            Some(AddKey(_)) | Some(EnterKey(_)) | Some(RemoveKey(_)) => {
93                //self.skip_value(seq);
94                Ok(None)
95            }
96            Some(Value(_)) | Some(Remove(_)) => panic!("unexpected DiffCommand Value or Remove"),
97            Some(Exit) | Some(Nothing) | Some(DeserializedValue) | None => Ok(None),
98        };
99        element
100    }
101    /// To be called after next_path_element returns a path, but the path is not recognized.
102    pub fn skip_value<'de, A>(
103        &mut self,
104        seq: &mut A,
105    ) -> Result<(), <A as de::SeqAccess<'de>>::Error>
106    where
107        A: de::SeqAccess<'de>,
108    {
109        self.skip_value_internal(seq, 1)
110    }
111    fn skip_value_internal<'de, A>(
112        &mut self,
113        seq: &mut A,
114        mut depth: i32,
115    ) -> Result<(), <A as de::SeqAccess<'de>>::Error>
116    where
117        A: de::SeqAccess<'de>,
118    {
119        // this tries to skip the value without knowing the type - not possible for some formats..
120        while let Some(cmd) = seq.next_element_seed(DiffCommandIgnoreValue {})? {
121            match cmd {
122                DiffCommandValue::Enter(_)
123                | DiffCommandValue::AddKey(_)
124                | DiffCommandValue::EnterKey(_) => depth += 1,
125                DiffCommandValue::Exit => depth -= 1,
126                DiffCommandValue::Value(_) | DiffCommandValue::Remove(_) => depth -= 1, // ignore value, but reduce depth, as it is an implicit Exit
127                DiffCommandValue::RemoveKey(_) => {}
128                DiffCommandValue::Nothing | DiffCommandValue::DeserializedValue => {
129                    panic!("should never serialize cmd Nothing or DeserializedValue")
130                }
131            }
132            if depth == 0 {
133                break;
134            }
135        }
136        if depth != 0 {
137            panic!("mismatched DiffCommand::Enter/Exit ")
138        }
139        Ok(())
140    }
141    /// Attempts to deserialize a value
142    pub fn read_value<'de, A, T: for<'c> Deserialize<'c>>(
143        &mut self,
144        seq: &mut A,
145        val: &mut T,
146    ) -> Result<bool, <A as de::SeqAccess<'de>>::Error>
147    where
148        A: de::SeqAccess<'de>,
149    {
150        // The visitor for DiffCommandDeserWrapper handles enum cases and returns
151        // a command if the next element was not a Value
152        let cmd = seq.next_element_seed::<DiffCommandDeserWrapper<T>>(DiffCommandDeserWrapper {
153            val_wrapper: DeserWrapper { val },
154        })?;
155        match cmd {
156            Some(DiffCommandValue::DeserializedValue) => return Ok(true),
157            Some(DiffCommandValue::Enter(_)) => {
158                self.skip_value_internal(seq, 1)?;
159            }
160            Some(DiffCommandValue::Exit) => panic!("unexpected Exit command"),
161            _ => {}
162        }
163
164        Ok(false)
165    }
166    /// Returns the next command in the stream. Make sure you know what you're doing!
167    pub fn read_next_command<'de, A, T: for<'c> Deserialize<'c>>(
168        &mut self,
169        seq: &mut A,
170    ) -> Result<Option<DiffCommandValue<'de, T>>, <A as de::SeqAccess<'de>>::Error>
171    where
172        A: de::SeqAccess<'de>,
173    {
174        // The visitor for DiffCommandDeserWrapper handles enum cases and returns
175        // a command if the next element was not a Value
176        let cmd = seq.next_element::<DiffCommandValue<'de, T>>()?;
177        Ok(match cmd {
178            cmd @ Some(DiffCommandValue::Remove(_))
179            | cmd @ Some(DiffCommandValue::Value(_))
180            | cmd @ Some(DiffCommandValue::Enter(_))
181            | cmd @ Some(DiffCommandValue::AddKey(_))
182            | cmd @ Some(DiffCommandValue::EnterKey(_))
183            | cmd @ Some(DiffCommandValue::RemoveKey(_))
184            | cmd @ Some(DiffCommandValue::Exit) => cmd,
185            _ => None,
186        })
187    }
188}