1use serde::{Deserialize, Serialize};
4
5use super::{MuseError, MuseResult};
6use ih_muse_proto::{ElementKindRegistration, MetricDefinition, TimestampResolution};
7use tokio::time::Duration;
8
9#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
14pub enum ClientType {
15 Poet,
17 Mock,
19}
20
21#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
25pub struct Config {
26 pub endpoints: Vec<String>,
28 pub client_type: ClientType,
30 pub recording_enabled: bool,
32 pub recording_path: Option<String>,
34 pub recording_flush_interval: Option<Duration>,
36 pub default_resolution: TimestampResolution,
38 pub element_kinds: Vec<ElementKindRegistration>,
40 pub metric_definitions: Vec<MetricDefinition>,
42 pub initialization_interval: Option<Duration>,
44 pub cluster_monitor_interval: Option<Duration>,
46 pub max_reg_elem_retries: usize,
48}
49
50impl Config {
51 #[allow(clippy::too_many_arguments)]
92 pub fn new(
93 endpoints: Vec<String>,
94 client_type: ClientType,
95 recording_enabled: bool,
96 recording_path: Option<String>,
97 recording_flush_interval: Option<Duration>,
98 default_resolution: TimestampResolution,
99 element_kinds: Vec<ElementKindRegistration>,
100 metric_definitions: Vec<MetricDefinition>,
101 initialization_interval: Option<Duration>,
102 cluster_monitor_interval: Option<Duration>,
103 max_reg_elem_retries: usize,
104 ) -> MuseResult<Self> {
105 let config = Self {
106 endpoints,
107 client_type,
108 recording_enabled,
109 recording_path,
110 recording_flush_interval,
111 default_resolution,
112 element_kinds,
113 metric_definitions,
114 initialization_interval,
115 cluster_monitor_interval,
116 max_reg_elem_retries,
117 };
118 config.validate()?;
119 Ok(config)
120 }
121
122 pub fn validate(&self) -> MuseResult<()> {
130 if self.client_type == ClientType::Poet && self.endpoints.is_empty() {
131 return Err(MuseError::Configuration(
132 "At least one endpoint needs to be specified for Poet client.".to_string(),
133 ));
134 }
135 if self.element_kinds.is_empty() {
136 return Err(MuseError::Configuration(
137 "Element kinds cannot be empty.".to_string(),
138 ));
139 }
140 if self.metric_definitions.is_empty() {
141 return Err(MuseError::Configuration(
142 "Metric definitions cannot be empty.".to_string(),
143 ));
144 }
145 if self.recording_enabled && self.recording_path.is_none() {
146 return Err(MuseError::Configuration(
147 "Recording enabled without a file path.".to_string(),
148 ));
149 }
150 Ok(())
151 }
152
153 pub fn diff(&self, other: &Config) -> Vec<String> {
161 let mut differences = Vec::new();
162
163 if self.endpoints != other.endpoints {
164 differences.push(format!(
165 "endpoints: {:?} != {:?}",
166 self.endpoints, other.endpoints
167 ));
168 }
169
170 if self.client_type != other.client_type {
171 differences.push(format!(
172 "client_type: {:?} != {:?}",
173 self.client_type, other.client_type
174 ));
175 }
176
177 if self.recording_enabled != other.recording_enabled {
178 differences.push(format!(
179 "recording_enabled: {} != {}",
180 self.recording_enabled, other.recording_enabled
181 ));
182 }
183
184 if self.recording_path != other.recording_path {
185 differences.push(format!(
186 "recording_path: {:?} != {:?}",
187 self.recording_path, other.recording_path
188 ));
189 }
190
191 if self.default_resolution != other.default_resolution {
192 differences.push(format!(
193 "default_resolution: {:?} != {:?}",
194 self.default_resolution, other.default_resolution
195 ));
196 }
197
198 if self.element_kinds != other.element_kinds {
199 differences.push(format!(
200 "element_kinds: {:?} != {:?}",
201 self.element_kinds, other.element_kinds
202 ));
203 }
204
205 if self.metric_definitions != other.metric_definitions {
206 differences.push(format!(
207 "metric_definitions: {:?} != {:?}",
208 self.metric_definitions, other.metric_definitions
209 ));
210 }
211
212 if self.cluster_monitor_interval != other.cluster_monitor_interval {
213 differences.push(format!(
214 "cluster_monitor_interval: {:?} != {:?}",
215 self.cluster_monitor_interval, other.cluster_monitor_interval
216 ));
217 }
218
219 if self.max_reg_elem_retries != other.max_reg_elem_retries {
220 differences.push(format!(
221 "max_reg_elem_retries: {} != {}",
222 self.max_reg_elem_retries, other.max_reg_elem_retries
223 ));
224 }
225
226 differences
227 }
228
229 pub fn pretty_diff(&self, other: &Config) -> String {
237 self.diff(other).join("\n")
238 }
239
240 pub fn is_relevantly_equal(&self, other: &Config) -> bool {
249 self.endpoints == other.endpoints
250 && self.client_type == other.client_type
251 && self.default_resolution == other.default_resolution
252 && self.element_kinds == other.element_kinds
253 && self.metric_definitions == other.metric_definitions
254 && self.cluster_monitor_interval == other.cluster_monitor_interval
255 && self.max_reg_elem_retries == other.max_reg_elem_retries
256 }
257}
258
259#[cfg(test)]
260mod tests {
261
262 use super::*;
263
264 #[test]
265 fn test_relevant_config_comparison() {
266 let config1 = Config {
267 endpoints: vec!["http://localhost:8000".to_string()],
268 client_type: ClientType::Poet,
269 recording_enabled: true, recording_path: Some("recording.json".to_string()), recording_flush_interval: Some(Duration::from_millis(1)),
272 default_resolution: TimestampResolution::Milliseconds,
273 element_kinds: vec![ElementKindRegistration::new(
274 "kind_code",
275 None,
276 "kind_name",
277 "desc",
278 )],
279 metric_definitions: vec![MetricDefinition::new("metric_code", "metric_name", "desc")],
280 initialization_interval: Some(Duration::from_secs(60)),
281 cluster_monitor_interval: Some(Duration::from_secs(60)),
282 max_reg_elem_retries: 3,
283 };
284
285 let config2 = Config {
286 endpoints: vec!["http://localhost:8000".to_string()],
287 client_type: ClientType::Poet,
288 recording_enabled: false,
289 recording_path: None,
290 recording_flush_interval: None,
291 default_resolution: TimestampResolution::Milliseconds,
292 element_kinds: vec![ElementKindRegistration::new(
293 "kind_code",
294 None,
295 "kind_name",
296 "desc",
297 )],
298 metric_definitions: vec![MetricDefinition::new("metric_code", "metric_name", "desc")],
299 initialization_interval: Some(Duration::from_secs(60)),
300 cluster_monitor_interval: Some(Duration::from_secs(60)),
301 max_reg_elem_retries: 3,
302 };
303
304 assert!(
305 config1.is_relevantly_equal(&config2),
306 "Configs should be considered equal based on relevant fields"
307 );
308 }
309}