1use std::collections::HashSet;
2
3use indexmap::IndexMap;
4use vector_lib::config::OutputId;
5
6use super::{ComponentKey, Config, EnrichmentTableOuter};
7
8#[derive(Debug)]
9pub struct ConfigDiff {
10 pub sources: Difference,
11 pub transforms: Difference,
12 pub sinks: Difference,
13 pub enrichment_tables: Difference,
16 pub components_to_reload: HashSet<ComponentKey>,
17}
18
19impl ConfigDiff {
20 pub fn initial(initial: &Config) -> Self {
21 Self::new(&Config::default(), initial, HashSet::new())
22 }
23
24 pub fn new(old: &Config, new: &Config, components_to_reload: HashSet<ComponentKey>) -> Self {
25 ConfigDiff {
26 sources: Difference::new(&old.sources, &new.sources, &components_to_reload),
27 transforms: Difference::new(&old.transforms, &new.transforms, &components_to_reload),
28 sinks: Difference::new(&old.sinks, &new.sinks, &components_to_reload),
29 enrichment_tables: Difference::new_tables(
30 &old.enrichment_tables,
31 &new.enrichment_tables,
32 ),
33 components_to_reload,
34 }
35 }
36
37 pub const fn flip(mut self) -> Self {
39 self.sources.flip();
40 self.transforms.flip();
41 self.sinks.flip();
42 self.enrichment_tables.flip();
43 self
44 }
45
46 pub fn contains(&self, key: &ComponentKey) -> bool {
48 self.sources.contains(key)
49 || self.transforms.contains(key)
50 || self.sinks.contains(key)
51 || self.enrichment_tables.contains(key)
52 }
53
54 pub fn is_changed(&self, key: &ComponentKey) -> bool {
56 self.sources.is_changed(key)
57 || self.transforms.is_changed(key)
58 || self.sinks.is_changed(key)
59 || self.enrichment_tables.contains(key)
60 }
61
62 pub fn is_removed(&self, key: &ComponentKey) -> bool {
64 self.sources.is_removed(key)
65 || self.transforms.is_removed(key)
66 || self.sinks.is_removed(key)
67 || self.enrichment_tables.contains(key)
68 }
69}
70
71#[derive(Debug)]
72pub struct Difference {
73 pub to_remove: HashSet<ComponentKey>,
74 pub to_change: HashSet<ComponentKey>,
75 pub to_add: HashSet<ComponentKey>,
76}
77
78impl Difference {
79 fn new<C>(
80 old: &IndexMap<ComponentKey, C>,
81 new: &IndexMap<ComponentKey, C>,
82 need_change: &HashSet<ComponentKey>,
83 ) -> Self
84 where
85 C: serde::Serialize + serde::Deserialize<'static>,
86 {
87 let old_names = old.keys().cloned().collect::<HashSet<_>>();
88 let new_names = new.keys().cloned().collect::<HashSet<_>>();
89
90 let to_change = old_names
91 .intersection(&new_names)
92 .filter(|&n| {
93 let old_value = serde_json::to_value(&old[n]).unwrap();
100 let new_value = serde_json::to_value(&new[n]).unwrap();
101 old_value != new_value || need_change.contains(n)
102 })
103 .cloned()
104 .collect::<HashSet<_>>();
105
106 let to_remove = &old_names - &new_names;
107 let to_add = &new_names - &old_names;
108
109 Self {
110 to_remove,
111 to_change,
112 to_add,
113 }
114 }
115
116 fn new_tables(
117 old: &IndexMap<ComponentKey, EnrichmentTableOuter<OutputId>>,
118 new: &IndexMap<ComponentKey, EnrichmentTableOuter<OutputId>>,
119 ) -> Self {
120 let old_names = old
121 .iter()
122 .flat_map(|(k, t)| vec![t.as_source(k).map(|(k, _)| k), t.as_sink(k).map(|(k, _)| k)])
123 .flatten()
124 .collect::<HashSet<_>>();
125 let new_names = new
126 .iter()
127 .flat_map(|(k, t)| vec![t.as_source(k).map(|(k, _)| k), t.as_sink(k).map(|(k, _)| k)])
128 .flatten()
129 .collect::<HashSet<_>>();
130
131 let to_change = old_names
132 .intersection(&new_names)
133 .filter(|&n| {
134 let old_value = serde_json::to_value(&old[n]).unwrap();
141 let new_value = serde_json::to_value(&new[n]).unwrap();
142 old_value != new_value
143 })
144 .cloned()
145 .collect::<HashSet<_>>();
146
147 let to_remove = &old_names - &new_names;
148 let to_add = &new_names - &old_names;
149
150 Self {
151 to_remove,
152 to_change,
153 to_add,
154 }
155 }
156
157 pub fn any_changed_or_added(&self) -> bool {
159 !(self.to_change.is_empty() && self.to_add.is_empty())
160 }
161
162 pub fn any_changed_or_removed(&self) -> bool {
164 !(self.to_change.is_empty() && self.to_remove.is_empty())
165 }
166
167 pub fn contains(&self, id: &ComponentKey) -> bool {
169 self.to_add.contains(id) || self.to_change.contains(id) || self.to_remove.contains(id)
170 }
171
172 pub fn contains_new(&self, id: &ComponentKey) -> bool {
174 self.to_add.contains(id) || self.to_change.contains(id)
175 }
176
177 pub fn is_changed(&self, key: &ComponentKey) -> bool {
179 self.to_change.contains(key)
180 }
181
182 pub fn is_added(&self, id: &ComponentKey) -> bool {
184 self.to_add.contains(id)
185 }
186
187 pub fn is_removed(&self, key: &ComponentKey) -> bool {
189 self.to_remove.contains(key)
190 }
191
192 const fn flip(&mut self) {
193 std::mem::swap(&mut self.to_remove, &mut self.to_add);
194 }
195
196 pub fn changed_and_added(&self) -> impl Iterator<Item = &ComponentKey> {
197 self.to_change.iter().chain(self.to_add.iter())
198 }
199
200 pub fn removed_and_changed(&self) -> impl Iterator<Item = &ComponentKey> {
201 self.to_change.iter().chain(self.to_remove.iter())
202 }
203}