opsview/config/compare.rs
1use crate::prelude::*;
2use std::collections::HashSet;
3
4/// Compares two `ConfigObjectMap` instances and returns the names of objects that are exclusive to
5/// each map, as well as the names of objects that are common to both maps.
6///
7/// # Type Parameters
8/// * `T` - The type of the objects stored in the maps. Must implement the `ConfigObject` trait.
9///
10/// # Arguments
11/// * `a` - The first `ConfigObjectMap` to compare.
12/// * `b` - The second `ConfigObjectMap` to compare.
13///
14/// # Returns
15/// A tuple containing the following:
16/// * `a_exclusive` - A `HashSet<String>` containing the names of objects that are in `a` but not `b`.
17/// * `b_exclusive` - A `HashSet<String>` containing the names of objects that are in `b` but not `a`.
18/// * `common` - A `HashSet<String>` containing the names of objects that are in both `a` and `b`.
19///
20/// # Example
21/// ```rust
22/// use opsview::prelude::*;
23/// use opsview::config::{Hashtag, compare_config_object_maps};
24///
25/// let mut a = ConfigObjectMap::<Hashtag>::new();
26/// let mut b = ConfigObjectMap::<Hashtag>::new();
27///
28/// let hashtag1 = Hashtag::minimal("MyHashtag1").unwrap();
29/// let hashtag2 = Hashtag::minimal("MyHashtag2").unwrap();
30/// let hashtag3 = Hashtag::minimal("MyHashtag3").unwrap();
31///
32/// a.add(hashtag1.clone());
33/// a.add(hashtag2.clone());
34///
35/// b.add(hashtag2.clone());
36/// b.add(hashtag3.clone());
37///
38/// let (a_exclusive, b_exclusive, common) = compare_config_object_maps(&a, &b);
39///
40/// assert_eq!(a_exclusive.len(), 1);
41/// assert_eq!(b_exclusive.len(), 1);
42/// assert_eq!(common.len(), 1);
43/// assert!(a_exclusive.is_disjoint(&b_exclusive));
44/// assert!(a_exclusive.contains(&hashtag1.name));
45/// assert!(b_exclusive.contains(&hashtag3.name));
46/// assert!(common.contains(&hashtag2.name));
47/// ```
48pub fn compare_config_object_maps<T: ConfigObject>(
49 a: &ConfigObjectMap<T>,
50 b: &ConfigObjectMap<T>,
51) -> (
52 HashSet<String>, // a_exclusive
53 HashSet<String>, // b_exclusive
54 HashSet<String>, // common
55) {
56 let a_keys: HashSet<String> = a.keys().cloned().collect();
57 let b_keys: HashSet<String> = b.keys().cloned().collect();
58
59 let a_exclusive = a_keys.difference(&b_keys).cloned().collect();
60 let b_exclusive = b_keys.difference(&a_keys).cloned().collect();
61 let common = a_keys.intersection(&b_keys).cloned().collect();
62
63 (a_exclusive, b_exclusive, common)
64}
65
66/// Compares two `ConfigRefMap` instances and returns the names of objects that are exclusive to
67/// each map, as well as the names of objects that are common to both maps.
68///
69/// # Type Parameters
70/// * `T` - The type of the objects stored in the maps. Must implement the `ConfigRef` trait.
71///
72/// # Arguments
73/// * `a` - The first `ConfigRefMap` to compare.
74/// * `b` - The second `ConfigRefMap` to compare.
75///
76/// # Returns
77/// A tuple containing the following:
78/// * `a_exclusive` - A `HashSet<String>` containing the names of objects that are in `a` but not `b`.
79/// * `b_exclusive` - A `HashSet<String>` containing the names of objects that are in `b` but not `a`.
80/// * `common` - A `HashSet<String>` containing the names of objects that are in both `a` and `b`.
81///
82/// # Example
83/// ```rust
84/// use opsview::prelude::*;
85/// use opsview::config::{Hashtag, HashtagRef, compare_config_ref_maps};
86///
87/// let mut a_objs = ConfigObjectMap::<Hashtag>::new();
88/// let mut b_objs = ConfigObjectMap::<Hashtag>::new();
89///
90/// let hashtag1 = Hashtag::minimal("MyHashtag1").unwrap();
91/// let hashtag2 = Hashtag::minimal("MyHashtag2").unwrap();
92/// let hashtag3 = Hashtag::minimal("MyHashtag3").unwrap();
93///
94/// a_objs.add(hashtag1.clone());
95/// a_objs.add(hashtag2.clone());
96/// let a_refs: ConfigRefMap<HashtagRef> = ref_map_from(&a_objs);
97///
98/// b_objs.add(hashtag2.clone());
99/// b_objs.add(hashtag3.clone());
100/// let b_refs: ConfigRefMap<HashtagRef> = ref_map_from(&b_objs);
101///
102/// let (a_exclusive, b_exclusive, common) = compare_config_ref_maps(&a_refs, &b_refs);
103///
104/// assert_eq!(a_exclusive.len(), 1);
105/// assert_eq!(b_exclusive.len(), 1);
106/// assert_eq!(common.len(), 1);
107/// assert!(a_exclusive.is_disjoint(&b_exclusive));
108/// assert!(a_exclusive.contains(&hashtag1.name));
109/// assert!(b_exclusive.contains(&hashtag3.name));
110/// assert!(common.contains(&hashtag2.name));
111/// ```
112pub fn compare_config_ref_maps<T: ConfigRef>(
113 a: &ConfigRefMap<T>,
114 b: &ConfigRefMap<T>,
115) -> (
116 HashSet<String>, // a_exclusive
117 HashSet<String>, // b_exclusive
118 HashSet<String>, // common
119) {
120 let a_keys: HashSet<String> = a.keys().cloned().collect();
121 let b_keys: HashSet<String> = b.keys().cloned().collect();
122
123 let a_exclusive = a_keys.difference(&b_keys).cloned().collect();
124 let b_exclusive = b_keys.difference(&a_keys).cloned().collect();
125 let common = a_keys.intersection(&b_keys).cloned().collect();
126
127 (a_exclusive, b_exclusive, common)
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133 #[test]
134 fn test_compare_config_object_maps() -> Result<(), OpsviewError> {
135 use crate::config::Hashtag;
136 let mut old = ConfigObjectMap::<Hashtag>::new();
137 let mut new = ConfigObjectMap::<Hashtag>::new();
138
139 let hashtag1 = Hashtag::minimal("MyHashtag")?;
140 let hashtag2 = Hashtag::minimal("MyHashtag2")?;
141 let hashtag3 = Hashtag::minimal("MyHashtag3")?;
142
143 old.add(hashtag1);
144 old.add(hashtag2.clone());
145
146 new.add(hashtag2);
147 new.add(hashtag3);
148
149 let (a_exclusive, b_exclusive, common) = compare_config_object_maps(&old, &new);
150
151 assert_eq!(a_exclusive.len(), 1);
152 assert_eq!(b_exclusive.len(), 1);
153 assert_eq!(common.len(), 1);
154 assert!(a_exclusive.is_disjoint(&b_exclusive));
155
156 Ok(())
157 }
158}