1use crate::merkle::{Merkle, Node};
2use crate::path::{FieldPath, PathSegment};
3
4#[derive(Debug, Clone, PartialEq)]
6pub enum Diff {
7 Same,
9 Different(DiffDetail),
11}
12
13#[derive(Debug, Clone, PartialEq)]
15pub enum DiffDetail {
16 Value,
18
19 Struct {
21 name: String,
23 changed_fields: Vec<(String, Diff)>,
25 },
26
27 Seq {
29 added: Vec<usize>,
31 removed: Vec<usize>,
33 modified: Vec<(usize, Diff)>,
35 },
36
37 Enum {
39 name: String,
41 old_variant: String,
43 new_variant: String,
45 inner: Option<Box<Diff>>,
47 },
48
49 Map {
51 added_keys: Vec<String>,
53 removed_keys: Vec<String>,
55 modified: Vec<(String, Diff)>,
57 },
58
59 TypeMismatch,
61}
62
63impl DiffDetail {
64 pub fn changed_paths(&self) -> Vec<(FieldPath, ChangeType)> {
66 let mut paths = Vec::new();
67 self.collect_paths(FieldPath::new(), &mut paths);
68 paths
69 }
70
71 fn collect_paths(&self, current: FieldPath, paths: &mut Vec<(FieldPath, ChangeType)>) {
72 match self {
73 DiffDetail::Value => {
74 paths.push((current, ChangeType::Modified));
75 }
76 DiffDetail::Struct { changed_fields, .. } => {
77 for (field_name, diff) in changed_fields {
78 let mut path = current.clone();
79 path.push(PathSegment::Field(field_name.clone()));
80 if let Diff::Different(detail) = diff {
81 detail.collect_paths(path, paths);
82 }
83 }
84 }
85 DiffDetail::Seq {
86 added,
87 removed,
88 modified,
89 } => {
90 for &idx in added {
91 let mut path = current.clone();
92 path.push(PathSegment::Index(idx));
93 paths.push((path, ChangeType::Added));
94 }
95 for &idx in removed {
96 let mut path = current.clone();
97 path.push(PathSegment::Index(idx));
98 paths.push((path, ChangeType::Removed));
99 }
100 for (idx, diff) in modified {
101 let mut path = current.clone();
102 path.push(PathSegment::Index(*idx));
103 if let Diff::Different(detail) = diff {
104 detail.collect_paths(path, paths);
105 }
106 }
107 }
108 DiffDetail::Enum {
109 old_variant,
110 new_variant,
111 inner,
112 ..
113 } => {
114 if old_variant != new_variant {
115 paths.push((current.clone(), ChangeType::VariantChanged));
116 }
117 if let Some(inner_diff) = inner {
118 if let Diff::Different(detail) = inner_diff.as_ref() {
119 let mut path = current;
120 path.push(PathSegment::Variant(new_variant.clone()));
121 detail.collect_paths(path, paths);
122 }
123 }
124 }
125 DiffDetail::Map {
126 added_keys,
127 removed_keys,
128 modified,
129 } => {
130 for key in added_keys {
131 let mut path = current.clone();
132 path.push(PathSegment::MapKey(key.clone()));
133 paths.push((path, ChangeType::Added));
134 }
135 for key in removed_keys {
136 let mut path = current.clone();
137 path.push(PathSegment::MapKey(key.clone()));
138 paths.push((path, ChangeType::Removed));
139 }
140 for (key, diff) in modified {
141 let mut path = current.clone();
142 path.push(PathSegment::MapKey(key.clone()));
143 if let Diff::Different(detail) = diff {
144 detail.collect_paths(path, paths);
145 }
146 }
147 }
148 DiffDetail::TypeMismatch => {
149 paths.push((current, ChangeType::TypeChanged));
150 }
151 }
152 }
153}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157pub enum ChangeType {
158 Modified,
159 Added,
160 Removed,
161 VariantChanged,
162 TypeChanged,
163}
164
165pub fn merkle_diff(old: &Merkle, new: &Merkle) -> Diff {
167 if old.digest == new.digest {
169 return Diff::Same;
170 }
171
172 match (&old.node, &new.node) {
173 (Node::Leaf(_), Node::Leaf(_)) => Diff::Different(DiffDetail::Value),
175 (Node::Unit, Node::Unit) => Diff::Different(DiffDetail::Value),
176 (Node::None, Node::None) => Diff::Different(DiffDetail::Value),
177
178 (Node::Struct(old_s), Node::Struct(new_s)) => {
180 let changed_fields: Vec<_> = old_s
181 .fields
182 .iter()
183 .zip(&new_s.fields)
184 .filter_map(|((old_name, old_m), (new_name, new_m))| {
185 debug_assert_eq!(old_name, new_name);
186 let diff = merkle_diff(old_m, new_m);
187 if diff != Diff::Same {
188 Some((old_name.clone(), diff))
189 } else {
190 None
191 }
192 })
193 .collect();
194
195 Diff::Different(DiffDetail::Struct {
196 name: old_s.name.clone(),
197 changed_fields,
198 })
199 }
200
201 (Node::Seq(old_s), Node::Seq(new_s)) => {
203 let old_len = old_s.elements.len();
204 let new_len = new_s.elements.len();
205 let common_len = old_len.min(new_len);
206
207 let modified: Vec<_> = old_s.elements[..common_len]
208 .iter()
209 .zip(&new_s.elements[..common_len])
210 .enumerate()
211 .filter_map(|(i, (o, n))| {
212 let diff = merkle_diff(o, n);
213 if diff != Diff::Same {
214 Some((i, diff))
215 } else {
216 None
217 }
218 })
219 .collect();
220
221 let added: Vec<_> = (old_len..new_len).collect();
222 let removed: Vec<_> = (new_len..old_len).collect();
223
224 Diff::Different(DiffDetail::Seq {
225 added,
226 removed,
227 modified,
228 })
229 }
230
231 (Node::Enum(old_e), Node::Enum(new_e)) => {
233 if old_e.variant != new_e.variant {
234 Diff::Different(DiffDetail::Enum {
235 name: old_e.name.clone(),
236 old_variant: old_e.variant.clone(),
237 new_variant: new_e.variant.clone(),
238 inner: None,
239 })
240 } else {
241 let inner = match (&old_e.data, &new_e.data) {
242 (Some(o), Some(n)) => {
243 let diff = merkle_diff(o, n);
244 if diff != Diff::Same {
245 Some(Box::new(diff))
246 } else {
247 None
248 }
249 }
250 _ => None,
251 };
252
253 if inner.is_some() {
254 Diff::Different(DiffDetail::Enum {
255 name: old_e.name.clone(),
256 old_variant: old_e.variant.clone(),
257 new_variant: new_e.variant.clone(),
258 inner,
259 })
260 } else {
261 Diff::Different(DiffDetail::Value)
262 }
263 }
264 }
265
266 (Node::Map(old_m), Node::Map(new_m)) => {
268 let old_keys: std::collections::HashSet<_> =
269 old_m.entries.iter().map(|(k, _)| k).collect();
270 let new_keys: std::collections::HashSet<_> =
271 new_m.entries.iter().map(|(k, _)| k).collect();
272
273 let added_keys: Vec<_> = new_keys
274 .difference(&old_keys)
275 .map(|k| (*k).clone())
276 .collect();
277 let removed_keys: Vec<_> = old_keys
278 .difference(&new_keys)
279 .map(|k| (*k).clone())
280 .collect();
281
282 let old_map: std::collections::HashMap<_, _> =
283 old_m.entries.iter().map(|(k, v)| (k, v)).collect();
284 let new_map: std::collections::HashMap<_, _> =
285 new_m.entries.iter().map(|(k, v)| (k, v)).collect();
286
287 let modified: Vec<_> = old_keys
288 .intersection(&new_keys)
289 .filter_map(|k| {
290 let old_v = old_map.get(*k).unwrap();
291 let new_v = new_map.get(*k).unwrap();
292 let diff = merkle_diff(old_v, new_v);
293 if diff != Diff::Same {
294 Some(((*k).clone(), diff))
295 } else {
296 None
297 }
298 })
299 .collect();
300
301 Diff::Different(DiffDetail::Map {
302 added_keys,
303 removed_keys,
304 modified,
305 })
306 }
307
308 _ => Diff::Different(DiffDetail::TypeMismatch),
310 }
311}
312
313#[cfg(test)]
314mod tests {
315 use super::*;
316 use crate::serializer::tree_hash;
317 use serde::Serialize;
318
319 #[test]
320 fn test_struct_diff_with_field_names() {
321 #[derive(Serialize)]
322 struct User {
323 name: String,
324 age: u32,
325 active: bool,
326 }
327
328 let u1 = User {
329 name: "Alice".into(),
330 age: 30,
331 active: true,
332 };
333 let u2 = User {
334 name: "Alice".into(),
335 age: 31,
336 active: true,
337 };
338
339 let m1 = tree_hash(&u1).unwrap();
340 let m2 = tree_hash(&u2).unwrap();
341
342 let diff = merkle_diff(&m1, &m2);
343
344 if let Diff::Different(DiffDetail::Struct { changed_fields, .. }) = diff {
345 assert_eq!(changed_fields.len(), 1);
346 assert_eq!(changed_fields[0].0, "age");
347 } else {
348 panic!("Expected Struct diff");
349 }
350 }
351
352 #[test]
353 fn test_nested_struct_paths() {
354 #[derive(Serialize)]
355 struct Address {
356 city: String,
357 zip: String,
358 }
359
360 #[derive(Serialize)]
361 struct User {
362 name: String,
363 address: Address,
364 }
365
366 let u1 = User {
367 name: "Alice".into(),
368 address: Address {
369 city: "NYC".into(),
370 zip: "10001".into(),
371 },
372 };
373 let u2 = User {
374 name: "Alice".into(),
375 address: Address {
376 city: "LA".into(),
377 zip: "10001".into(),
378 },
379 };
380
381 let m1 = tree_hash(&u1).unwrap();
382 let m2 = tree_hash(&u2).unwrap();
383
384 let diff = merkle_diff(&m1, &m2);
385
386 if let Diff::Different(detail) = diff {
387 let paths = detail.changed_paths();
388 assert_eq!(paths.len(), 1);
389 assert_eq!(paths[0].0.to_string(), "address.city");
390 } else {
391 panic!("Expected Different");
392 }
393 }
394
395 #[test]
396 fn test_vec_diff_paths() {
397 let v1: Vec<i32> = vec![1, 2, 3];
398 let v2: Vec<i32> = vec![1, 5, 3, 4];
399
400 let m1 = tree_hash(&v1).unwrap();
401 let m2 = tree_hash(&v2).unwrap();
402
403 let diff = merkle_diff(&m1, &m2);
404
405 if let Diff::Different(detail) = diff {
406 let paths = detail.changed_paths();
407 assert!(paths.iter().any(|(p, _)| p.to_string() == "[1]"));
409 assert!(paths
410 .iter()
411 .any(|(p, c)| p.to_string() == "[3]" && *c == ChangeType::Added));
412 } else {
413 panic!("Expected Different");
414 }
415 }
416
417 #[test]
418 fn test_enum_variant_change() {
419 #[derive(Serialize)]
420 enum Status {
421 Active,
422 Suspended { reason: String },
423 }
424
425 let s1 = Status::Active;
426 let s2 = Status::Suspended {
427 reason: "test".into(),
428 };
429
430 let m1 = tree_hash(&s1).unwrap();
431 let m2 = tree_hash(&s2).unwrap();
432
433 let diff = merkle_diff(&m1, &m2);
434
435 if let Diff::Different(DiffDetail::Enum {
436 old_variant,
437 new_variant,
438 ..
439 }) = diff
440 {
441 assert_eq!(old_variant, "Active");
442 assert_eq!(new_variant, "Suspended");
443 } else {
444 panic!("Expected Enum diff");
445 }
446 }
447}