libqaul_types/
diff.rs

1//! API diffs
2
3use serde::{Deserialize, Serialize};
4use std::collections::{BTreeMap, BTreeSet};
5
6/// Represents a generic change made to some structure
7#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Debug)]
8#[serde(rename_all = "snake_case")]
9pub enum ItemDiff<T> {
10    /// Don't change this value
11    Ignore,
12    /// Set a field to a value
13    Set(T),
14    /// Unset a field to "None"
15    Unset,
16}
17
18impl<T> ItemDiff<T> {
19    pub fn set<I: Into<T>>(i: I) -> Self {
20        Self::Set(i.into())
21    }
22}
23
24impl<T> Default for ItemDiff<T> {
25    fn default() -> Self {
26        ItemDiff::Ignore
27    }
28}
29
30pub trait ItemDiffExt<T> {
31    /// Apply the contents of a change to some field value
32    fn apply(self, prev: T) -> T;
33}
34
35impl<T> ItemDiffExt<Option<T>> for ItemDiff<T> {
36    fn apply(self, prev: Option<T>) -> Option<T> {
37        match self {
38            ItemDiff::Ignore => prev,
39            ItemDiff::Set(t) => Some(t),
40            ItemDiff::Unset => None,
41        }
42    }
43}
44
45impl<T> ItemDiffExt<&mut Option<T>> for ItemDiff<T> {
46    fn apply(self, prev: &mut Option<T>) -> &mut Option<T> {
47        match self {
48            ItemDiff::Ignore => {}
49            ItemDiff::Set(t) => {
50                *prev = Some(t);
51            }
52            ItemDiff::Unset => {
53                *prev = None;
54            }
55        }
56        prev
57    }
58}
59
60impl<T: Default> ItemDiffExt<T> for ItemDiff<T> {
61    fn apply(self, prev: T) -> T {
62        match self {
63            ItemDiff::Ignore => prev,
64            ItemDiff::Set(t) => t,
65            ItemDiff::Unset => T::default(),
66        }
67    }
68}
69
70/// Represents a generic change made to an unordered set of items
71#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Debug, Ord, PartialOrd)]
72pub enum SetDiff<T> {
73    /// Add an item to the set
74    Add(T),
75    /// Remove an item from the set
76    Remove(T),
77    /// Make no change to the set
78    Ignore,
79}
80
81impl<T> Default for SetDiff<T> {
82    fn default() -> Self {
83        SetDiff::Ignore
84    }
85}
86
87pub trait SetDiffExt<T> {
88    /// Apply a set of changes to some object
89    ///
90    /// **This may not respect ordering**
91    fn apply<I: IntoIterator<Item = SetDiff<T>>>(&mut self, iter: I);
92}
93
94impl<T: Ord> SetDiffExt<T> for BTreeSet<T> {
95    fn apply<I: IntoIterator<Item = SetDiff<T>>>(&mut self, iter: I) {
96        iter.into_iter().for_each(|item| match item {
97            SetDiff::Add(t) => {
98                self.insert(t);
99            }
100            SetDiff::Remove(t) => {
101                self.remove(&t);
102            }
103            _ => {}
104        });
105    }
106}
107
108/// Represents a generic change made to a map
109#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Debug)]
110#[serde(rename_all = "snake_case")]
111pub enum MapDiff<K, V> {
112    /// Set the key `key` to value `value` in the map
113    Add { key: K, value: V },
114    /// Remove the given key from the map
115    Remove(K),
116    /// Make no change to the map
117    Ignore,
118}
119
120impl<K, V> Default for MapDiff<K, V> {
121    fn default() -> Self {
122        MapDiff::Ignore
123    }
124}
125
126pub trait MapDiffExt<K, V> {
127    /// Apply a set of changes to some object
128    ///
129    /// **This may not respect ordering**
130    fn apply<I: IntoIterator<Item = MapDiff<K, V>>>(&mut self, iter: I);
131}
132
133impl<K: Ord, V> MapDiffExt<K, V> for BTreeMap<K, V> {
134    fn apply<I: IntoIterator<Item = MapDiff<K, V>>>(&mut self, iter: I) {
135        iter.into_iter().for_each(|item| match item {
136            MapDiff::Add { key, value } => {
137                self.insert(key, value);
138            }
139            MapDiff::Remove(key) => {
140                self.remove(&key);
141            }
142            _ => {}
143        });
144    }
145}
146
147#[cfg(test)]
148mod test {
149    use super::*;
150
151    #[test]
152    fn json_serde() {
153        let variants = [
154            (ItemDiff::Ignore, "\"ignore\""),
155            (ItemDiff::Set(true), "{\"set\":true}"),
156            (ItemDiff::Unset, "\"unset\""),
157        ];
158
159        for (v, s) in variants.iter() {
160            let string = serde_json::to_string(v).unwrap();
161            assert_eq!(&string, s);
162            let value: ItemDiff<bool> = serde_json::from_str(&string).unwrap();
163            assert_eq!(value, *v);
164        }
165    }
166
167    #[test]
168    fn bincode_serde() {
169        let variants = [
170            (ItemDiff::Ignore, vec![0, 0, 0, 0]),
171            (ItemDiff::Set(true), vec![1, 0, 0, 0, 1]),
172            (ItemDiff::Unset, vec![2, 0, 0, 0]),
173        ];
174
175        for (v, s) in variants.iter() {
176            let data = bincode::serialize(v).unwrap();
177            assert_eq!(&data, s);
178            let value: ItemDiff<bool> = bincode::deserialize(&data).unwrap();
179            assert_eq!(value, *v);
180        }
181    }
182}