Skip to main content

cumulo_dipa/
set.rs

1use serde::Serialize;
2use std::fmt::{Debug, Formatter};
3
4#[macro_use]
5mod set_impl_macro;
6
7set_impl!(std::collections::HashSet<K>, hash_map_impl,);
8set_impl!(std::collections::BTreeSet<K>, btree_map_impl, + Ord);
9
10#[derive(Serialize)]
11/// The delta between two sets.
12pub enum SetDelta<'s, 'e, K> {
13    /// Nothing has changed
14    NoChange,
15    /// Remove all elements from the HashMap
16    RemoveAll,
17    /// Add one entry to the set
18    AddOneField(&'e K),
19    /// Remove one entry from the set
20    RemoveOneField(&'s K),
21    /// Modify multiple entries
22    ModifyMany {
23        added: Vec<&'e K>,
24        removed: Vec<&'s K>,
25    },
26}
27
28#[derive(Deserialize)]
29#[allow(missing_docs)]
30/// The delta between two sets.
31pub enum SetDeltaOwned<K> {
32    /// Nothing has changed
33    NoChange,
34    /// Remove all elements from the set
35    RemoveAll,
36    /// Add one entry to the set
37    AddOneField(K),
38    /// Remove one entry from the set
39    RemoveOneField(K),
40    /// Modify multiple entries
41    ModifyMany { added: Vec<K>, removed: Vec<K> },
42}
43
44// Used by DipaImplTester
45impl<'s, 'e, K> Debug for SetDelta<'s, 'e, K>
46where
47    K: Debug,
48{
49    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
50        match self {
51            SetDelta::NoChange => {
52                f.write_str("NoChange")?;
53            }
54            SetDelta::RemoveAll => {
55                f.write_str("RemoveAll")?;
56            }
57            SetDelta::AddOneField(k) => {
58                f.debug_tuple("AddOneField").field(k).finish()?;
59            }
60            SetDelta::RemoveOneField(k) => {
61                f.debug_tuple("RemoveOneField").field(k).finish()?;
62            }
63            SetDelta::ModifyMany { added, removed } => {
64                f.debug_struct("ModifyMany")
65                    .field("added", added)
66                    .field("removed", removed)
67                    .finish()?;
68            }
69        };
70
71        Ok(())
72    }
73}
74
75// Used by DipaImplTester
76impl<'s, 'e, K> PartialEq for SetDelta<'s, 'e, K>
77where
78    K: PartialEq,
79{
80    fn eq(&self, other: &Self) -> bool {
81        // Matched exhaustive so that we remember to add new variants.
82        match (self, other) {
83            (Self::NoChange, Self::NoChange) => true,
84            (Self::NoChange, _) => false,
85
86            (
87                Self::ModifyMany {
88                    added: left_added,
89                    removed: left_removed,
90                },
91                Self::ModifyMany {
92                    added: right_added,
93                    removed: right_removed,
94                },
95            ) => left_added == right_added && left_removed == right_removed,
96            (Self::ModifyMany { .. }, _) => false,
97
98            (Self::AddOneField(left_k), Self::AddOneField(right_k)) => left_k == right_k,
99            (Self::AddOneField(..), _) => false,
100
101            (Self::RemoveOneField(left_k), Self::RemoveOneField(right_k)) => left_k == right_k,
102            (Self::RemoveOneField(_), _) => false,
103
104            (Self::RemoveAll, Self::RemoveAll) => true,
105            (Self::RemoveAll, _) => false,
106        }
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113    use crate::DipaImplTester;
114    use std::collections::{BTreeSet, HashSet};
115
116    /// Verify that we properly handle an unchanged empty HashMap
117    #[test]
118    fn unchanged_empty_hashmap() {
119        DipaImplTester {
120            label: None,
121            start: &mut HashSet::<()>::new(),
122            end: &HashSet::new(),
123            expected_delta: SetDelta::NoChange,
124            expected_serialized_patch_size: 1,
125            expected_did_change: false,
126        }
127        .test();
128    }
129
130    /// Verify that we properly handle an unchanged HashMap that has fields.
131    #[test]
132    fn unchanged_hashmap() {
133        DipaImplTester {
134            label: None,
135            start: &mut vec![1u32].into_iter().collect::<HashSet<_>>(),
136            end: &vec![1].into_iter().collect(),
137            expected_delta: SetDelta::NoChange,
138            expected_serialized_patch_size: 1,
139            expected_did_change: false,
140        }
141        .test();
142    }
143
144    /// Verify that we can remove a field from the original HashMap
145    #[test]
146    fn all_fields_removed() {
147        DipaImplTester {
148            label: None,
149            start: &mut vec![1u32, 22].into_iter().collect(),
150            end: &HashSet::new(),
151            expected_delta: SetDelta::RemoveAll,
152            expected_serialized_patch_size: 1,
153            expected_did_change: true,
154        }
155        .test();
156    }
157
158    /// Verify that we can add a field to the original HashMap
159    #[test]
160    fn one_field_added() {
161        DipaImplTester {
162            label: None,
163            start: &mut HashSet::new(),
164            end: &vec![1u32].into_iter().collect(),
165            expected_delta: SetDelta::AddOneField(&1),
166            expected_serialized_patch_size: 2,
167            expected_did_change: true,
168        }
169        .test();
170    }
171
172    /// Verify that we can remove a field to the original HashMap
173    #[test]
174    fn one_field_removed() {
175        DipaImplTester {
176            label: None,
177            start: &mut vec![1u32, 3].into_iter().collect::<HashSet<_>>(),
178            end: &vec![1].into_iter().collect(),
179            expected_delta: SetDelta::RemoveOneField(&3),
180            expected_serialized_patch_size: 2,
181            expected_did_change: true,
182        }
183        .test();
184    }
185
186    /// Verify that we can add multiple fields to the map.
187    #[test]
188    fn many_fields_added() {
189        DipaImplTester {
190            label: None,
191            start: &mut BTreeSet::new(),
192            end: &vec![1u32, 3].into_iter().collect(),
193            expected_delta: SetDelta::ModifyMany {
194                added: vec![&1, &3],
195                removed: vec![],
196            },
197            expected_serialized_patch_size: 5,
198            expected_did_change: true,
199        }
200        .test();
201    }
202
203    /// Verify that we can remove multiple entries from the map.
204    #[test]
205    fn many_fields_removed() {
206        DipaImplTester {
207            label: None,
208            start: &mut vec![1u32, 3, 5].into_iter().collect::<BTreeSet<_>>(),
209            end: &vec![1].into_iter().collect(),
210            expected_delta: SetDelta::ModifyMany {
211                added: vec![],
212                removed: vec![&3, &5],
213            },
214            expected_serialized_patch_size: 5,
215            expected_did_change: true,
216        }
217        .test();
218    }
219}