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}