1use serde::{Deserialize, Serialize, Serializer};
22
23use crate::entry::ID;
24
25#[derive(Debug, Clone, Default)]
32pub struct Snapshot {
33 tips: Vec<ID>,
35}
36
37impl Serialize for Snapshot {
38 fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
39 self.tips.serialize(serializer)
40 }
41}
42
43impl<'de> Deserialize<'de> for Snapshot {
44 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
45 where
46 D: serde::Deserializer<'de>,
47 {
48 let tips = Vec::<ID>::deserialize(deserializer)?;
49 Ok(Self::new(tips))
50 }
51}
52
53impl Snapshot {
54 pub const EMPTY: Snapshot = Snapshot { tips: Vec::new() };
56
57 pub fn new(mut tips: Vec<ID>) -> Self {
61 tips.sort();
62 tips.dedup();
63 Self { tips }
64 }
65
66 pub fn tips(&self) -> &[ID] {
68 &self.tips
69 }
70
71 pub fn into_tips(self) -> Vec<ID> {
73 self.tips
74 }
75
76 pub fn is_empty(&self) -> bool {
78 self.tips.is_empty()
79 }
80
81 pub fn len(&self) -> usize {
83 self.tips.len()
84 }
85}
86
87impl PartialEq for Snapshot {
89 fn eq(&self, other: &Self) -> bool {
90 self.tips == other.tips
92 }
93}
94
95impl Eq for Snapshot {}
96
97impl std::hash::Hash for Snapshot {
98 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
99 self.tips.hash(state);
100 }
101}
102
103impl From<Vec<ID>> for Snapshot {
104 fn from(tips: Vec<ID>) -> Self {
105 Self::new(tips)
106 }
107}
108
109impl From<&[ID]> for Snapshot {
110 fn from(tips: &[ID]) -> Self {
111 Self::new(tips.to_vec())
112 }
113}
114
115impl<const N: usize> From<[ID; N]> for Snapshot {
116 fn from(tips: [ID; N]) -> Self {
117 Self::new(tips.to_vec())
118 }
119}
120
121impl<const N: usize> From<&[ID; N]> for Snapshot {
122 fn from(tips: &[ID; N]) -> Self {
123 Self::new(tips.to_vec())
124 }
125}
126
127impl From<&Vec<ID>> for Snapshot {
128 fn from(tips: &Vec<ID>) -> Self {
129 Self::new(tips.clone())
130 }
131}
132
133impl AsRef<[ID]> for Snapshot {
134 fn as_ref(&self) -> &[ID] {
135 &self.tips
136 }
137}
138
139impl std::ops::Deref for Snapshot {
140 type Target = [ID];
141 fn deref(&self) -> &[ID] {
142 &self.tips
143 }
144}
145
146impl<'a> IntoIterator for &'a Snapshot {
147 type Item = &'a ID;
148 type IntoIter = std::slice::Iter<'a, ID>;
149
150 fn into_iter(self) -> Self::IntoIter {
151 self.tips.iter()
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 fn id(byte: u8) -> ID {
160 ID::from_bytes([byte])
161 }
162
163 #[test]
164 fn empty_const_is_empty() {
165 assert!(Snapshot::EMPTY.is_empty());
166 assert_eq!(Snapshot::EMPTY.len(), 0);
167 assert_eq!(Snapshot::EMPTY.tips(), &[] as &[ID]);
168 }
169
170 #[test]
171 fn default_equals_empty() {
172 assert_eq!(Snapshot::default(), Snapshot::EMPTY);
173 }
174
175 #[test]
176 fn new_sorts_input() {
177 let a = id(1);
178 let b = id(2);
179 let c = id(3);
180 let unsorted = Snapshot::new(vec![c.clone(), a.clone(), b.clone()]);
181 let sorted = Snapshot::new(vec![a, b, c]);
182 assert_eq!(unsorted.tips(), sorted.tips());
183 }
184
185 #[test]
186 fn new_dedups_input() {
187 let a = id(1);
188 let b = id(2);
189 let with_dupes = Snapshot::new(vec![a.clone(), b.clone(), a.clone(), b.clone(), a.clone()]);
190 let mut expected = vec![a, b];
191 expected.sort();
192 assert_eq!(with_dupes.len(), 2);
193 assert_eq!(with_dupes.tips(), expected.as_slice());
194 }
195
196 #[test]
197 fn set_equality_holds_via_canonical_form() {
198 let a = id(1);
199 let b = id(2);
200 let s1: Snapshot = vec![a.clone(), b.clone()].into();
201 let s2: Snapshot = vec![b, a].into();
202 assert_eq!(s1, s2);
203 }
204
205 #[test]
206 fn hash_matches_for_set_equal_snapshots() {
207 use std::collections::hash_map::DefaultHasher;
208 use std::hash::{Hash, Hasher};
209 let a = id(1);
210 let b = id(2);
211 let s1: Snapshot = vec![a.clone(), b.clone()].into();
212 let s2: Snapshot = vec![b, a].into();
213 let mut h1 = DefaultHasher::new();
214 s1.hash(&mut h1);
215 let mut h2 = DefaultHasher::new();
216 s2.hash(&mut h2);
217 assert_eq!(h1.finish(), h2.finish());
218 }
219
220 #[test]
221 fn from_slice_sorts_and_dedups() {
222 let a = id(1);
223 let b = id(2);
224 let snap = Snapshot::from(&[b.clone(), a.clone(), a.clone()][..]);
225 let mut expected = vec![a, b];
226 expected.sort();
227 assert_eq!(snap.tips(), expected.as_slice());
228 }
229
230 #[test]
231 fn from_array_sorts_and_dedups() {
232 let a = id(1);
233 let b = id(2);
234 let snap = Snapshot::from([b.clone(), a.clone(), a.clone()]);
235 let mut expected = vec![a, b];
236 expected.sort();
237 assert_eq!(snap.tips(), expected.as_slice());
238 }
239
240 #[test]
241 fn into_tips_returns_sorted_vec() {
242 let mut expected = vec![id(1), id(2), id(3)];
243 expected.sort();
244 let snap = Snapshot::new(vec![id(3), id(1), id(2)]);
245 assert_eq!(snap.into_tips(), expected);
246 }
247
248 #[test]
249 fn iter_yields_sorted_tips() {
250 let mut expected = [id(1), id(2), id(3)];
251 expected.sort();
252 let snap = Snapshot::new(vec![id(3), id(1), id(2)]);
253 let collected: Vec<&ID> = (&snap).into_iter().collect();
254 let expected_refs: Vec<&ID> = expected.iter().collect();
255 assert_eq!(collected, expected_refs);
256 }
257
258 #[test]
259 fn as_ref_exposes_sorted_slice() {
260 let a = id(1);
261 let b = id(2);
262 let snap = Snapshot::new(vec![b.clone(), a.clone()]);
263 let slice: &[ID] = snap.as_ref();
264 let mut expected = vec![a, b];
265 expected.sort();
266 assert_eq!(slice, expected.as_slice());
267 }
268
269 #[test]
270 fn serde_roundtrip_preserves_invariant() {
271 let a = id(1);
272 let b = id(2);
273 let snap = Snapshot::new(vec![b, a]);
274 let json = serde_json::to_string(&snap).unwrap();
275 let parsed: Snapshot = serde_json::from_str(&json).unwrap();
276 assert_eq!(parsed, snap);
277 }
278
279 #[test]
280 fn deserialize_normalizes_unsorted_wire_data() {
281 let a = id(1);
284 let b = id(2);
285 let canonical = Snapshot::new(vec![a.clone(), b.clone()]);
286 let unsorted_json = serde_json::to_string(&vec![b, a]).unwrap();
287 let parsed: Snapshot = serde_json::from_str(&unsorted_json).unwrap();
288 assert_eq!(parsed, canonical);
289 }
290
291 #[test]
292 fn deserialize_dedups_wire_data() {
293 let a = id(1);
294 let b = id(2);
295 let canonical = Snapshot::new(vec![a.clone(), b.clone()]);
296 let duped_json = serde_json::to_string(&vec![a.clone(), b, a]).unwrap();
297 let parsed: Snapshot = serde_json::from_str(&duped_json).unwrap();
298 assert_eq!(parsed, canonical);
299 }
300
301 #[test]
302 fn serializes_as_bare_id_array() {
303 let a = id(1);
307 let b = id(2);
308 let snap = Snapshot::new(vec![a.clone(), b.clone()]);
309 let snap_json = serde_json::to_string(&snap).unwrap();
310 let vec_json = serde_json::to_string(snap.tips()).unwrap();
311 assert_eq!(snap_json, vec_json);
312 }
313}