1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
use crate::{ edit::{map, Edit}, Diffable, }; macro_rules! map_impl { ($(($typ:ident, $key_constraint:ident)),*) => { $( impl<'a, K: Eq + $key_constraint + 'a, V: Diffable<'a> + 'a> Diffable<'a> for $typ<K, V> { type Diff = $typ<&'a K, map::Edit<'a, V>>; fn diff(&'a self, other: &'a Self) -> Edit<Self> { let intersection = self .iter() .filter_map(|(k, v)| Some((k, (v, other.get(k)?)))); let unique_self = self.iter().filter(|(k, _)| !other.contains_key(*k)); let unique_other = other.iter().filter(|(k, _)| !self.contains_key(*k)); let value_diffs = unique_other .map(|(k, v)| (k, map::Edit::Insert(v))) .chain(unique_self.map(|(k, v)| (k, map::Edit::Remove(v)))) .chain(intersection.map(|(k, (self_v, other_v))| (k, self_v.diff(other_v).into()))) .collect::<$typ<_, _>>(); if value_diffs.values().any(|v| !v.is_copy()) { Edit::Change(value_diffs) } else { Edit::Copy(self) } } } )* } } use std::{ collections::{BTreeMap, HashMap}, hash::Hash, }; map_impl! { (BTreeMap, Ord), (HashMap, Hash) } #[cfg(feature = "indexmap-impl")] use indexmap::IndexMap; #[cfg(feature = "indexmap-impl")] map_impl! { (IndexMap, Hash) } #[cfg(test)] mod tests { use super::*; #[test] fn example() { let unity: std::collections::HashMap<_, _> = [(1, 1), (2, 2), (3, 3)].iter().cloned().collect(); let not_unity: std::collections::HashMap<_, _> = [(1, 1), (2, 3), (4, 4)].iter().cloned().collect(); if let Edit::Change(diff) = unity.diff(¬_unity) { assert!(diff[&1].is_copy()); assert_eq!(diff[&2].change().unwrap(), &(&2, &3)); assert!(diff[&3].is_remove()); assert_eq!(diff[&4].insert().unwrap(), &4); } else { unreachable!() } } }