hash_map_diff/
lib.rs

1use std::collections::HashMap;
2
3#[derive(Eq, PartialEq, Debug)]
4pub struct HashMapDiff<K, V>
5where
6    K: Eq + std::hash::Hash,
7{
8    pub updated: HashMap<K, V>,
9    pub removed: HashMap<K, V>,
10}
11
12pub fn hash_map_diff<'a, K, V>(
13    lhs: &'a HashMap<K, V>,
14    rhs: &'a HashMap<K, V>,
15) -> HashMapDiff<&'a K, &'a V>
16where
17    K: Eq + std::hash::Hash + Clone,
18    V: Eq + Clone,
19{
20    let mut removed: HashMap<&'a K, &'a V> = HashMap::new();
21    for (key, value) in lhs {
22        if !rhs.contains_key(key) {
23            removed.insert(key, value);
24        }
25    }
26
27    let mut updated: HashMap<&'a K, &'a V> = HashMap::new();
28    for (key, new_value) in rhs {
29        if let Some(old_value) = lhs.get(key) {
30            if new_value != old_value {
31                updated.insert(key, new_value);
32            }
33        } else {
34            updated.insert(key, new_value);
35        }
36    }
37
38    HashMapDiff { updated, removed }
39}
40
41#[cfg(test)]
42mod should {
43    use super::*;
44
45    #[test]
46    fn detect_removed_elements() {
47        let old_hash_map = [("key1", 1), ("key2", 2)].into();
48        let new_hash_map = [("key2", 2)].into();
49        let diff = hash_map_diff(&old_hash_map, &new_hash_map);
50        let expected_diff = HashMapDiff {
51            updated: HashMap::new(),
52            removed: [(&"key1", &1)].into(),
53        };
54        assert_eq!(diff, expected_diff);
55    }
56
57    #[test]
58    fn detect_changed_values() {
59        let old_hash_map = [("key1", 1), ("key2", 2)].into();
60        let new_hash_map = [("key1", 1), ("key2", 3)].into();
61        let diff = hash_map_diff(&old_hash_map, &new_hash_map);
62        let expected_diff = HashMapDiff {
63            updated: [(&"key2", &3)].into(),
64            removed: HashMap::new(),
65        };
66        assert_eq!(diff, expected_diff);
67    }
68
69    #[test]
70    fn detect_new_keys() {
71        let old_hash_map = [("key1", 1)].into();
72        let new_hash_map = [("key1", 1), ("key3", 3)].into();
73        let diff = hash_map_diff(&old_hash_map, &new_hash_map);
74        let expected_diff = HashMapDiff {
75            updated: [(&"key3", &3)].into(),
76            removed: HashMap::new(),
77        };
78        assert_eq!(diff, expected_diff);
79    }
80
81    #[test]
82    fn usage_test() {
83        let lhs = [("unchanged", 1), ("removed", 2), ("changed", 3)].into();
84        let rhs = [("unchanged", 1), ("changed", 5), ("added", 4)].into();
85
86        let received_diff = hash_map_diff(&lhs, &rhs);
87
88        let expected_diff = HashMapDiff {
89            updated: [(&"changed", &5), (&"added", &4)].into(),
90            removed: [(&"removed", &2)].into(),
91        };
92
93        assert_eq!(received_diff, expected_diff);
94    }
95}