serde_intermediate/
versioning.rs

1use crate::{error::*, value::intermediate::Intermediate};
2use petgraph::{algo::astar, Graph};
3use serde::{de::DeserializeOwned, Deserialize, Serialize};
4
5/// Optimization hint used in calculating change between two intermediate data.
6#[derive(Debug, Default, Copy, Clone, PartialEq)]
7pub enum DiffOptimizationHint {
8    /// Don't assume any optimization.
9    #[default]
10    Default,
11    /// Put entire new object if change information size is greater than source (old) size.
12    SizeSource,
13    /// Put entire new object if change information size is greater than target (new) size.
14    SizeTarget,
15    /// Put entire new object if change information size is greater than bytesize threshold.
16    SizeValue(
17        /// Bytesize threshold.
18        usize,
19    ),
20    /// Put entire new object if change information size is greater than percentage of source size.
21    SizePercentage(
22        /// Percentage threshold.
23        f64,
24    ),
25}
26
27/// Change calculation options.
28#[derive(Debug, Default, Copy, Clone, PartialEq)]
29pub struct DiffOptions {
30    /// Optimization hint.
31    pub optimization_hint: DiffOptimizationHint,
32}
33
34impl DiffOptions {
35    pub fn optimization_hint(mut self, hint: DiffOptimizationHint) -> Self {
36        self.optimization_hint = hint;
37        self
38    }
39}
40
41/// Information about change between two intermediate data.
42#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
43pub enum Change {
44    /// Values are same.
45    Same,
46    /// Value was removed.
47    Removed,
48    /// Value was entirely changed.
49    Changed(
50        /// Value.
51        Intermediate,
52    ),
53    /// Value was added.
54    Added(
55        /// Value.
56        Intermediate,
57    ),
58    /// Value was partially changed.
59    PartialChange(
60        /// Content change.
61        Box<Change>,
62    ),
63    /// Sequence of values was partially changed.
64    PartialSeq(
65        /// List of changes: `(index, change)`.
66        Vec<(usize, Change)>,
67    ),
68    /// Map of key-values was partially changed.
69    PartialMap(
70        /// List of changes: `(key, change)`.
71        Vec<(Intermediate, Change)>,
72    ),
73    /// Structure with field-value was partially changed.
74    PartialStruct(
75        /// List of changes: `(field, change)`.
76        Vec<(String, Change)>,
77    ),
78}
79
80impl Change {
81    pub fn same() -> Self {
82        Self::Same
83    }
84
85    pub fn removed() -> Self {
86        Self::Removed
87    }
88
89    pub fn changed(value: impl Into<Intermediate>) -> Self {
90        Self::Changed(value.into())
91    }
92
93    pub fn added(value: impl Into<Intermediate>) -> Self {
94        Self::Added(value.into())
95    }
96
97    pub fn partial_change(change: Self) -> Self {
98        Self::PartialChange(Box::new(change))
99    }
100
101    pub fn partial_seq() -> Self {
102        Self::PartialSeq(vec![])
103    }
104
105    pub fn partial_map() -> Self {
106        Self::PartialMap(vec![])
107    }
108
109    pub fn partial_struct() -> Self {
110        Self::PartialStruct(vec![])
111    }
112
113    pub fn partial_seq_item(mut self, index: usize, change: Self) -> Self {
114        if let Self::PartialSeq(v) = &mut self {
115            v.push((index, change));
116        }
117        self
118    }
119
120    pub fn partial_map_item(mut self, key: impl Into<Intermediate>, change: Self) -> Self {
121        if let Self::PartialMap(v) = &mut self {
122            let key = key.into();
123            if let Some(item) = v.iter_mut().find(|(k, _)| k == &key) {
124                item.1 = change;
125            } else {
126                v.push((key, change));
127            }
128        }
129        self
130    }
131
132    pub fn partial_struct_item(mut self, name: impl ToString, change: Self) -> Self {
133        if let Self::PartialStruct(v) = &mut self {
134            let name = name.to_string();
135            if let Some(item) = v.iter_mut().find(|(n, _)| n == &name) {
136                item.1 = change;
137            } else {
138                v.push((name, change));
139            }
140        }
141        self
142    }
143
144    pub fn is_same(&self) -> bool {
145        matches!(self, Self::Same)
146    }
147
148    fn optimize(
149        self,
150        source: &Intermediate,
151        target: &Intermediate,
152        hint: DiffOptimizationHint,
153    ) -> Self {
154        match hint {
155            DiffOptimizationHint::Default => self,
156            DiffOptimizationHint::SizeSource => {
157                if self.total_bytesize() > source.total_bytesize() {
158                    Self::Changed(target.to_owned())
159                } else {
160                    self
161                }
162            }
163            DiffOptimizationHint::SizeTarget => {
164                if self.total_bytesize() > target.total_bytesize() {
165                    Self::Changed(target.to_owned())
166                } else {
167                    self
168                }
169            }
170            DiffOptimizationHint::SizeValue(threshold) => {
171                if self.total_bytesize() > threshold {
172                    Self::Changed(target.to_owned())
173                } else {
174                    self
175                }
176            }
177            DiffOptimizationHint::SizePercentage(threshold) => {
178                if self.total_bytesize()
179                    > (threshold.clamp(0.0, 1.0) * source.total_bytesize() as f64) as _
180                {
181                    Self::Changed(target.to_owned())
182                } else {
183                    self
184                }
185            }
186        }
187    }
188
189    pub fn difference(prev: &Intermediate, next: &Intermediate, options: &DiffOptions) -> Self {
190        if prev == next {
191            Self::Same
192        } else {
193            match (prev, next) {
194                (Intermediate::Option(Some(prev)), Intermediate::Option(Some(next))) => {
195                    Self::PartialChange(Box::new(Self::difference(prev, next, options)))
196                }
197                (Intermediate::NewTypeStruct(prev), Intermediate::NewTypeStruct(next)) => {
198                    Self::PartialChange(Box::new(Self::difference(prev, next, options)))
199                }
200                (
201                    Intermediate::NewTypeVariant(prev_name, prev_value),
202                    Intermediate::NewTypeVariant(next_name, next_value),
203                ) => {
204                    if prev_name != next_name {
205                        Self::Changed(next.to_owned())
206                    } else {
207                        Self::PartialChange(Box::new(Self::difference(
208                            prev_value, next_value, options,
209                        )))
210                    }
211                }
212                (Intermediate::Seq(prev), Intermediate::Seq(next))
213                | (Intermediate::Tuple(prev), Intermediate::Tuple(next))
214                | (Intermediate::TupleStruct(prev), Intermediate::TupleStruct(next)) => {
215                    Self::PartialSeq(Self::sequence_difference(prev, next, options))
216                }
217                (Intermediate::Map(prev), Intermediate::Map(next)) => {
218                    let mut result = vec![];
219                    for (nk, nv) in next {
220                        if !prev.iter().any(|(pk, _)| pk == nk) {
221                            result.push((nk.to_owned(), Self::Added(nv.to_owned())));
222                        }
223                    }
224                    for (pk, _) in prev {
225                        if !next.iter().any(|(nk, _)| pk == nk) {
226                            result.push((pk.to_owned(), Self::Removed));
227                        }
228                    }
229                    for (pk, pv) in prev {
230                        if let Some((_, nv)) = next
231                            .iter()
232                            .find(|(nk, _)| pk == nk)
233                            .filter(|(_, nv)| pv != nv)
234                        {
235                            let diff = Self::difference(pv, nv, options);
236                            if !diff.is_same() {
237                                result.push((pk.to_owned(), diff));
238                            }
239                        }
240                    }
241                    Self::PartialMap(result)
242                }
243                (Intermediate::Struct(prev), Intermediate::Struct(next))
244                | (Intermediate::StructVariant(_, prev), Intermediate::StructVariant(_, next)) => {
245                    let mut result = vec![];
246                    for (nk, nv) in next {
247                        if !prev.iter().any(|(pk, _)| pk == nk) {
248                            result.push((nk.to_owned(), Self::Added(nv.to_owned())));
249                        }
250                    }
251                    for (pk, _) in prev {
252                        if !next.iter().any(|(nk, _)| pk == nk) {
253                            result.push((pk.to_owned(), Self::Removed));
254                        }
255                    }
256                    for (pk, pv) in prev {
257                        if let Some((_, nv)) = next
258                            .iter()
259                            .find(|(nk, _)| pk == nk)
260                            .filter(|(_, nv)| pv != nv)
261                        {
262                            let diff = Self::difference(pv, nv, options);
263                            if !diff.is_same() {
264                                result.push((pk.to_owned(), diff));
265                            }
266                        }
267                    }
268                    Self::PartialStruct(result)
269                }
270                _ => Self::Changed(next.to_owned()),
271            }
272        }
273        .optimize(prev, next, options.optimization_hint)
274    }
275
276    pub fn sequence_difference(
277        prev: &[Intermediate],
278        next: &[Intermediate],
279        options: &DiffOptions,
280    ) -> Vec<(usize, Self)> {
281        if prev.is_empty() && next.is_empty() {
282            return vec![];
283        } else if prev.is_empty() {
284            return next
285                .iter()
286                .enumerate()
287                .map(|(i, v)| (i, Self::Added(v.to_owned())))
288                .collect();
289        } else if next.is_empty() {
290            return (0..prev.len()).map(|_| (0, Self::Removed)).collect();
291        }
292
293        /// (prev index, next index)
294        type Location = (usize, usize);
295
296        #[derive(Debug, Copy, Clone, PartialEq, Eq)]
297        enum Diff {
298            Unchanged,
299            Changed,
300            Added,
301            Removed,
302        }
303
304        impl Diff {
305            fn cost(&self) -> usize {
306                match self {
307                    Self::Added => 8,
308                    Self::Removed => 9,
309                    Self::Unchanged => 10,
310                    Self::Changed => 11,
311                }
312            }
313        }
314
315        let cols = prev.len() + 1;
316        let rows = next.len() + 1;
317        let mut graph = Graph::<Location, Diff>::with_capacity(
318            cols * rows,
319            (cols - 1) * rows + (rows - 1) * cols + (cols - 1) * (rows - 1),
320        );
321        let mut nodes = Vec::with_capacity(cols * rows);
322        for row in 0..rows {
323            for col in 0..cols {
324                nodes.push(graph.add_node((col, row)));
325            }
326        }
327        let get_node = |col, row| nodes[row * cols + col];
328        for row in 0..rows {
329            for col in 0..(cols - 1) {
330                graph.add_edge(get_node(col, row), get_node(col + 1, row), Diff::Removed);
331            }
332        }
333        for col in 0..cols {
334            for row in 0..(rows - 1) {
335                graph.add_edge(get_node(col, row), get_node(col, row + 1), Diff::Added);
336            }
337        }
338        for (col, prev) in prev.iter().enumerate().take(cols - 1) {
339            for (row, next) in next.iter().enumerate().take(rows - 1) {
340                if prev == next {
341                    graph.add_edge(
342                        get_node(col, row),
343                        get_node(col + 1, row + 1),
344                        Diff::Unchanged,
345                    );
346                } else {
347                    graph.add_edge(
348                        get_node(col, row),
349                        get_node(col + 1, row + 1),
350                        Diff::Changed,
351                    );
352                }
353            }
354        }
355        let finish = *nodes.last().unwrap();
356        astar(
357            &graph,
358            *nodes.first().unwrap(),
359            |n| n == finish,
360            |e| e.weight().cost(),
361            |_| 0,
362        )
363        .map(|(_, path)| {
364            let mut pos = 0;
365            path.windows(2)
366                .filter_map(|chunk| {
367                    let diff = graph
368                        .find_edge(chunk[0], chunk[1])
369                        .and_then(|e| graph.edge_weight(e))?;
370                    let old_pos = pos;
371                    match diff {
372                        Diff::Unchanged => {
373                            pos += 1;
374                            None
375                        }
376                        Diff::Changed => {
377                            pos += 1;
378                            let (prev_pos, _) = graph.node_weight(chunk[0])?;
379                            let prev = &prev[*prev_pos];
380                            let next = &next[old_pos];
381                            let diff = Self::difference(prev, next, options).optimize(
382                                prev,
383                                next,
384                                options.optimization_hint,
385                            );
386                            Some((old_pos, diff))
387                        }
388                        Diff::Removed => Some((old_pos, Self::Removed)),
389                        Diff::Added => {
390                            pos += 1;
391                            Some((old_pos, Self::Added(next[old_pos].to_owned())))
392                        }
393                    }
394                })
395                .collect()
396        })
397        .unwrap_or_default()
398    }
399
400    pub fn patch(&self, value: &Intermediate) -> Result<Option<Intermediate>> {
401        match self {
402            Self::Same => Ok(Some(value.to_owned())),
403            Self::Removed => Ok(None),
404            Self::Changed(v) => Ok(Some(v.to_owned())),
405            Self::Added(_) => Err(Error::CannotAdd(value.to_owned())),
406            Self::PartialChange(change) => {
407                let result = match value {
408                    Intermediate::Option(Some(v)) => match change.patch(v)? {
409                        Some(v) => Intermediate::Option(Some(Box::new(v))),
410                        _ => return Err(Error::NotPartial(value.to_owned())),
411                    },
412                    Intermediate::NewTypeStruct(v) => match change.patch(v)? {
413                        Some(v) => Intermediate::NewTypeStruct(Box::new(v)),
414                        _ => return Err(Error::NotPartial(value.to_owned())),
415                    },
416                    Intermediate::NewTypeVariant(n, v) => match change.patch(v)? {
417                        Some(v) => Intermediate::NewTypeVariant(n.to_owned(), Box::new(v)),
418                        _ => return Err(Error::NotPartial(value.to_owned())),
419                    },
420                    _ => return Err(Error::NotPartial(value.to_owned())),
421                };
422                Ok(Some(result))
423            }
424            Self::PartialSeq(changes) => {
425                fn implement(
426                    v: &[Intermediate],
427                    changes: &[(usize, Change)],
428                ) -> Result<Vec<Intermediate>> {
429                    let mut result = v.to_owned();
430                    for (index, change) in changes {
431                        match change {
432                            Change::Removed => {
433                                result.remove(*index);
434                            }
435                            Change::Changed(v) => {
436                                if let Some(item) = result.get_mut(*index) {
437                                    *item = v.to_owned();
438                                }
439                            }
440                            Change::Added(v) => result.insert(*index, v.to_owned()),
441                            change => {
442                                if let Some(item) = result.get_mut(*index) {
443                                    if let Some(patched) = change.patch(item)? {
444                                        *item = patched;
445                                    }
446                                }
447                            }
448                        }
449                    }
450                    Ok(result)
451                }
452
453                match value {
454                    Intermediate::Seq(v) => Ok(Some(Intermediate::Seq(implement(v, changes)?))),
455                    Intermediate::Tuple(v) => Ok(Some(Intermediate::Tuple(implement(v, changes)?))),
456                    Intermediate::TupleStruct(v) => {
457                        Ok(Some(Intermediate::TupleStruct(implement(v, changes)?)))
458                    }
459                    _ => Err(Error::NotSeq(value.to_owned())),
460                }
461            }
462            Self::PartialMap(changes) => match value {
463                Intermediate::Map(v) => {
464                    let mut result = v.to_owned();
465                    for (key, change) in changes {
466                        match change {
467                            Self::Removed => {
468                                if let Some(index) = result.iter().position(|(k, _)| k == key) {
469                                    result.remove(index);
470                                }
471                            }
472                            Self::Changed(v) => {
473                                if let Some(index) = result.iter().position(|(k, _)| k == key) {
474                                    if let Some(item) = result.get_mut(index) {
475                                        item.1 = v.to_owned();
476                                    }
477                                }
478                            }
479                            Self::Added(v) => {
480                                if let Some(item) = result.iter_mut().find(|(k, _)| k == key) {
481                                    item.1 = v.to_owned();
482                                } else {
483                                    result.push((key.to_owned(), v.to_owned()))
484                                }
485                            }
486                            change => {
487                                if let Some(item) = result.iter_mut().find(|(k, _)| k == key) {
488                                    if let Some(patched) = change.patch(&item.1)? {
489                                        item.1 = patched;
490                                    }
491                                }
492                            }
493                        }
494                    }
495                    Ok(Some(Intermediate::Map(result)))
496                }
497                _ => Err(Error::NotMap(value.to_owned())),
498            },
499            Self::PartialStruct(changes) => {
500                fn implement(
501                    v: &[(String, Intermediate)],
502                    changes: &[(String, Change)],
503                ) -> Result<Vec<(String, Intermediate)>> {
504                    let mut result = v.to_owned();
505                    for (key, change) in changes {
506                        match change {
507                            Change::Removed => {
508                                if let Some(index) = result.iter().position(|(k, _)| k == key) {
509                                    result.remove(index);
510                                }
511                            }
512                            Change::Changed(v) => {
513                                if let Some(index) = result.iter().position(|(k, _)| k == key) {
514                                    if let Some(item) = result.get_mut(index) {
515                                        item.1 = v.to_owned();
516                                    }
517                                }
518                            }
519                            Change::Added(v) => {
520                                if let Some(item) = result.iter_mut().find(|(k, _)| k == key) {
521                                    item.1 = v.to_owned();
522                                } else {
523                                    result.push((key.to_owned(), v.to_owned()))
524                                }
525                            }
526                            change => {
527                                if let Some(item) = result.iter_mut().find(|(k, _)| k == key) {
528                                    if let Some(patched) = change.patch(&item.1)? {
529                                        item.1 = patched;
530                                    }
531                                }
532                            }
533                        }
534                    }
535                    Ok(result)
536                }
537
538                match value {
539                    Intermediate::Struct(v) => {
540                        Ok(Some(Intermediate::Struct(implement(v, changes)?)))
541                    }
542                    Intermediate::StructVariant(n, v) => Ok(Some(Intermediate::StructVariant(
543                        n.to_owned(),
544                        implement(v, changes)?,
545                    ))),
546                    _ => Err(Error::NotMap(value.to_owned())),
547                }
548            }
549        }
550    }
551
552    pub fn data_difference<P, N>(prev: &P, next: &N, options: &DiffOptions) -> Result<Self>
553    where
554        P: Serialize,
555        N: Serialize,
556    {
557        let prev = crate::to_intermediate(prev)?;
558        let next = crate::to_intermediate(next)?;
559        Ok(Self::difference(&prev, &next, options))
560    }
561
562    pub fn data_patch<T>(&self, data: &T) -> Result<Option<T>>
563    where
564        T: Serialize + DeserializeOwned,
565    {
566        let serialized = crate::to_intermediate(data)?;
567        let patched = match self.patch(&serialized)? {
568            Some(patched) => patched,
569            None => return Ok(None),
570        };
571        Ok(Some(crate::from_intermediate::<T>(&patched)?))
572    }
573
574    pub fn total_bytesize(&self) -> usize {
575        fn string_bytesize(v: &str) -> usize {
576            std::mem::size_of_val(v.as_bytes())
577        }
578
579        std::mem::size_of_val(self)
580            + match self {
581                Self::Changed(v) | Self::Added(v) => v.total_bytesize(),
582                Self::PartialChange(v) => v.total_bytesize(),
583                Self::PartialSeq(v) => v
584                    .iter()
585                    .map(|(i, v)| std::mem::size_of_val(i) + v.total_bytesize())
586                    .sum(),
587                Self::PartialMap(v) => v
588                    .iter()
589                    .map(|(k, v)| k.total_bytesize() + v.total_bytesize())
590                    .sum(),
591                Self::PartialStruct(v) => v
592                    .iter()
593                    .map(|(k, v)| string_bytesize(k) + v.total_bytesize())
594                    .sum(),
595                _ => 0,
596            }
597    }
598}