1use crate::ConfigResult;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Deserialize, Serialize)]
8pub struct FeaturesConfig {
9 #[serde(default = "default_true")]
11 pub enable_trading: bool,
12
13 #[serde(default)]
15 pub enable_bridging: bool,
16
17 #[serde(default)]
19 pub enable_social_monitoring: bool,
20
21 #[serde(default)]
23 pub enable_graph_memory: bool,
24
25 #[serde(default = "default_true")]
27 pub enable_streaming: bool,
28
29 #[serde(default)]
31 pub enable_webhooks: bool,
32
33 #[serde(default)]
35 pub enable_analytics: bool,
36
37 #[serde(default)]
39 pub debug_mode: bool,
40
41 #[serde(default)]
43 pub experimental: bool,
44
45 #[serde(default)]
47 pub custom: std::collections::HashMap<String, bool>,
48}
49
50impl FeaturesConfig {
51 pub fn is_enabled(&self, feature: Feature) -> bool {
53 match feature {
54 Feature::Trading => self.enable_trading,
55 Feature::Bridging => self.enable_bridging,
56 Feature::SocialMonitoring => self.enable_social_monitoring,
57 Feature::GraphMemory => self.enable_graph_memory,
58 Feature::Streaming => self.enable_streaming,
59 Feature::Webhooks => self.enable_webhooks,
60 Feature::Analytics => self.enable_analytics,
61 Feature::Debug => self.debug_mode,
62 Feature::Experimental => self.experimental,
63 }
64 }
65
66 pub fn is_custom_enabled(&self, name: &str) -> bool {
68 self.custom.get(name).copied().unwrap_or(false)
69 }
70
71 pub fn enable(&mut self, feature: Feature) {
73 match feature {
74 Feature::Trading => self.enable_trading = true,
75 Feature::Bridging => self.enable_bridging = true,
76 Feature::SocialMonitoring => self.enable_social_monitoring = true,
77 Feature::GraphMemory => self.enable_graph_memory = true,
78 Feature::Streaming => self.enable_streaming = true,
79 Feature::Webhooks => self.enable_webhooks = true,
80 Feature::Analytics => self.enable_analytics = true,
81 Feature::Debug => self.debug_mode = true,
82 Feature::Experimental => self.experimental = true,
83 }
84 }
85
86 pub fn disable(&mut self, feature: Feature) {
88 match feature {
89 Feature::Trading => self.enable_trading = false,
90 Feature::Bridging => self.enable_bridging = false,
91 Feature::SocialMonitoring => self.enable_social_monitoring = false,
92 Feature::GraphMemory => self.enable_graph_memory = false,
93 Feature::Streaming => self.enable_streaming = false,
94 Feature::Webhooks => self.enable_webhooks = false,
95 Feature::Analytics => self.enable_analytics = false,
96 Feature::Debug => self.debug_mode = false,
97 Feature::Experimental => self.experimental = false,
98 }
99 }
100
101 pub fn validate_config(&self) -> ConfigResult<()> {
103 if self.experimental && !self.debug_mode {
105 tracing::warn!("Experimental features enabled without debug mode");
106 }
107
108 Ok(())
109 }
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq)]
114pub enum Feature {
115 Trading,
117 Bridging,
119 SocialMonitoring,
121 GraphMemory,
123 Streaming,
125 Webhooks,
127 Analytics,
129 Debug,
131 Experimental,
133}
134
135impl std::fmt::Display for Feature {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 match self {
138 Feature::Trading => write!(f, "trading"),
139 Feature::Bridging => write!(f, "bridging"),
140 Feature::SocialMonitoring => write!(f, "social_monitoring"),
141 Feature::GraphMemory => write!(f, "graph_memory"),
142 Feature::Streaming => write!(f, "streaming"),
143 Feature::Webhooks => write!(f, "webhooks"),
144 Feature::Analytics => write!(f, "analytics"),
145 Feature::Debug => write!(f, "debug"),
146 Feature::Experimental => write!(f, "experimental"),
147 }
148 }
149}
150
151fn default_true() -> bool {
152 true
153}
154
155impl Default for FeaturesConfig {
156 fn default() -> Self {
157 Self {
158 enable_trading: true,
159 enable_bridging: false, enable_social_monitoring: false,
161 enable_graph_memory: false,
162 enable_streaming: true,
163 enable_webhooks: false,
164 enable_analytics: false,
165 debug_mode: false,
166 experimental: false,
167 custom: std::collections::HashMap::new(),
168 }
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
177 fn test_features_config_default() {
178 let config = FeaturesConfig::default();
179
180 assert!(config.enable_trading);
182 assert!(!config.enable_bridging); assert!(!config.enable_social_monitoring);
184 assert!(!config.enable_graph_memory);
185 assert!(config.enable_streaming);
186 assert!(!config.enable_webhooks);
187 assert!(!config.enable_analytics);
188 assert!(!config.debug_mode);
189 assert!(!config.experimental);
190 assert!(config.custom.is_empty());
191 }
192
193 #[test]
194 fn test_is_enabled_when_trading_should_return_correct_value() {
195 let mut config = FeaturesConfig::default();
196 assert!(config.is_enabled(Feature::Trading));
197
198 config.enable_trading = false;
199 assert!(!config.is_enabled(Feature::Trading));
200 }
201
202 #[test]
203 fn test_is_enabled_when_bridging_should_return_correct_value() {
204 let mut config = FeaturesConfig::default();
205 assert!(!config.is_enabled(Feature::Bridging)); config.enable_bridging = true;
208 assert!(config.is_enabled(Feature::Bridging));
209
210 config.enable_bridging = false;
211 assert!(!config.is_enabled(Feature::Bridging));
212 }
213
214 #[test]
215 fn test_is_enabled_when_social_monitoring_should_return_correct_value() {
216 let mut config = FeaturesConfig::default();
217 assert!(!config.is_enabled(Feature::SocialMonitoring));
218
219 config.enable_social_monitoring = true;
220 assert!(config.is_enabled(Feature::SocialMonitoring));
221 }
222
223 #[test]
224 fn test_is_enabled_when_graph_memory_should_return_correct_value() {
225 let mut config = FeaturesConfig::default();
226 assert!(!config.is_enabled(Feature::GraphMemory));
227
228 config.enable_graph_memory = true;
229 assert!(config.is_enabled(Feature::GraphMemory));
230 }
231
232 #[test]
233 fn test_is_enabled_when_streaming_should_return_correct_value() {
234 let mut config = FeaturesConfig::default();
235 assert!(config.is_enabled(Feature::Streaming));
236
237 config.enable_streaming = false;
238 assert!(!config.is_enabled(Feature::Streaming));
239 }
240
241 #[test]
242 fn test_is_enabled_when_webhooks_should_return_correct_value() {
243 let mut config = FeaturesConfig::default();
244 assert!(!config.is_enabled(Feature::Webhooks));
245
246 config.enable_webhooks = true;
247 assert!(config.is_enabled(Feature::Webhooks));
248 }
249
250 #[test]
251 fn test_is_enabled_when_analytics_should_return_correct_value() {
252 let mut config = FeaturesConfig::default();
253 assert!(!config.is_enabled(Feature::Analytics));
254
255 config.enable_analytics = true;
256 assert!(config.is_enabled(Feature::Analytics));
257 }
258
259 #[test]
260 fn test_is_enabled_when_debug_should_return_correct_value() {
261 let mut config = FeaturesConfig::default();
262 assert!(!config.is_enabled(Feature::Debug));
263
264 config.debug_mode = true;
265 assert!(config.is_enabled(Feature::Debug));
266 }
267
268 #[test]
269 fn test_is_enabled_when_experimental_should_return_correct_value() {
270 let mut config = FeaturesConfig::default();
271 assert!(!config.is_enabled(Feature::Experimental));
272
273 config.experimental = true;
274 assert!(config.is_enabled(Feature::Experimental));
275 }
276
277 #[test]
278 fn test_is_custom_enabled_when_feature_exists_should_return_true() {
279 let mut config = FeaturesConfig::default();
280 config.custom.insert("custom_feature".to_string(), true);
281
282 assert!(config.is_custom_enabled("custom_feature"));
283 }
284
285 #[test]
286 fn test_is_custom_enabled_when_feature_exists_false_should_return_false() {
287 let mut config = FeaturesConfig::default();
288 config.custom.insert("custom_feature".to_string(), false);
289
290 assert!(!config.is_custom_enabled("custom_feature"));
291 }
292
293 #[test]
294 fn test_is_custom_enabled_when_feature_not_exists_should_return_false() {
295 let config = FeaturesConfig::default();
296
297 assert!(!config.is_custom_enabled("nonexistent_feature"));
298 }
299
300 #[test]
301 fn test_enable_trading_should_set_true() {
302 let mut config = FeaturesConfig::default();
303 config.enable_trading = false;
304
305 config.enable(Feature::Trading);
306 assert!(config.enable_trading);
307 }
308
309 #[test]
310 fn test_enable_bridging_should_set_true() {
311 let mut config = FeaturesConfig::default();
312 config.enable_bridging = false;
313
314 config.enable(Feature::Bridging);
315 assert!(config.enable_bridging);
316 }
317
318 #[test]
319 fn test_enable_social_monitoring_should_set_true() {
320 let mut config = FeaturesConfig::default();
321
322 config.enable(Feature::SocialMonitoring);
323 assert!(config.enable_social_monitoring);
324 }
325
326 #[test]
327 fn test_enable_graph_memory_should_set_true() {
328 let mut config = FeaturesConfig::default();
329
330 config.enable(Feature::GraphMemory);
331 assert!(config.enable_graph_memory);
332 }
333
334 #[test]
335 fn test_enable_streaming_should_set_true() {
336 let mut config = FeaturesConfig::default();
337 config.enable_streaming = false;
338
339 config.enable(Feature::Streaming);
340 assert!(config.enable_streaming);
341 }
342
343 #[test]
344 fn test_enable_webhooks_should_set_true() {
345 let mut config = FeaturesConfig::default();
346
347 config.enable(Feature::Webhooks);
348 assert!(config.enable_webhooks);
349 }
350
351 #[test]
352 fn test_enable_analytics_should_set_true() {
353 let mut config = FeaturesConfig::default();
354
355 config.enable(Feature::Analytics);
356 assert!(config.enable_analytics);
357 }
358
359 #[test]
360 fn test_enable_debug_should_set_true() {
361 let mut config = FeaturesConfig::default();
362
363 config.enable(Feature::Debug);
364 assert!(config.debug_mode);
365 }
366
367 #[test]
368 fn test_enable_experimental_should_set_true() {
369 let mut config = FeaturesConfig::default();
370
371 config.enable(Feature::Experimental);
372 assert!(config.experimental);
373 }
374
375 #[test]
376 fn test_disable_trading_should_set_false() {
377 let mut config = FeaturesConfig::default();
378
379 config.disable(Feature::Trading);
380 assert!(!config.enable_trading);
381 }
382
383 #[test]
384 fn test_disable_bridging_should_set_false() {
385 let mut config = FeaturesConfig::default();
386
387 config.disable(Feature::Bridging);
388 assert!(!config.enable_bridging);
389 }
390
391 #[test]
392 fn test_disable_social_monitoring_should_set_false() {
393 let mut config = FeaturesConfig::default();
394 config.enable_social_monitoring = true;
395
396 config.disable(Feature::SocialMonitoring);
397 assert!(!config.enable_social_monitoring);
398 }
399
400 #[test]
401 fn test_disable_graph_memory_should_set_false() {
402 let mut config = FeaturesConfig::default();
403 config.enable_graph_memory = true;
404
405 config.disable(Feature::GraphMemory);
406 assert!(!config.enable_graph_memory);
407 }
408
409 #[test]
410 fn test_disable_streaming_should_set_false() {
411 let mut config = FeaturesConfig::default();
412
413 config.disable(Feature::Streaming);
414 assert!(!config.enable_streaming);
415 }
416
417 #[test]
418 fn test_disable_webhooks_should_set_false() {
419 let mut config = FeaturesConfig::default();
420 config.enable_webhooks = true;
421
422 config.disable(Feature::Webhooks);
423 assert!(!config.enable_webhooks);
424 }
425
426 #[test]
427 fn test_disable_analytics_should_set_false() {
428 let mut config = FeaturesConfig::default();
429 config.enable_analytics = true;
430
431 config.disable(Feature::Analytics);
432 assert!(!config.enable_analytics);
433 }
434
435 #[test]
436 fn test_disable_debug_should_set_false() {
437 let mut config = FeaturesConfig::default();
438 config.debug_mode = true;
439
440 config.disable(Feature::Debug);
441 assert!(!config.debug_mode);
442 }
443
444 #[test]
445 fn test_disable_experimental_should_set_false() {
446 let mut config = FeaturesConfig::default();
447 config.experimental = true;
448
449 config.disable(Feature::Experimental);
450 assert!(!config.experimental);
451 }
452
453 #[test]
454 fn test_validate_when_experimental_without_debug_should_warn_and_return_ok() {
455 let mut config = FeaturesConfig::default();
456 config.experimental = true;
457 config.debug_mode = false;
458
459 let result = config.validate_config();
460 assert!(result.is_ok());
461 }
462
463 #[test]
464 fn test_validate_when_experimental_with_debug_should_return_ok() {
465 let mut config = FeaturesConfig::default();
466 config.experimental = true;
467 config.debug_mode = true;
468
469 let result = config.validate_config();
470 assert!(result.is_ok());
471 }
472
473 #[test]
474 fn test_validate_when_no_experimental_should_return_ok() {
475 let config = FeaturesConfig::default();
476
477 let result = config.validate_config();
478 assert!(result.is_ok());
479 }
480
481 #[test]
482 fn test_feature_display_trading() {
483 assert_eq!(Feature::Trading.to_string(), "trading");
484 }
485
486 #[test]
487 fn test_feature_display_bridging() {
488 assert_eq!(Feature::Bridging.to_string(), "bridging");
489 }
490
491 #[test]
492 fn test_feature_display_social_monitoring() {
493 assert_eq!(Feature::SocialMonitoring.to_string(), "social_monitoring");
494 }
495
496 #[test]
497 fn test_feature_display_graph_memory() {
498 assert_eq!(Feature::GraphMemory.to_string(), "graph_memory");
499 }
500
501 #[test]
502 fn test_feature_display_streaming() {
503 assert_eq!(Feature::Streaming.to_string(), "streaming");
504 }
505
506 #[test]
507 fn test_feature_display_webhooks() {
508 assert_eq!(Feature::Webhooks.to_string(), "webhooks");
509 }
510
511 #[test]
512 fn test_feature_display_analytics() {
513 assert_eq!(Feature::Analytics.to_string(), "analytics");
514 }
515
516 #[test]
517 fn test_feature_display_debug() {
518 assert_eq!(Feature::Debug.to_string(), "debug");
519 }
520
521 #[test]
522 fn test_feature_display_experimental() {
523 assert_eq!(Feature::Experimental.to_string(), "experimental");
524 }
525
526 #[test]
527 fn test_default_true_function() {
528 assert!(default_true());
529 }
530
531 #[test]
532 fn test_feature_debug_format() {
533 let feature = Feature::Trading;
534 assert_eq!(format!("{:?}", feature), "Trading");
535 }
536
537 #[test]
538 fn test_feature_clone() {
539 let feature = Feature::Trading;
540 let cloned = feature.clone();
541 assert_eq!(feature, cloned);
542 }
543
544 #[test]
545 fn test_feature_copy() {
546 let feature = Feature::Trading;
547 let copied = feature;
548 assert_eq!(feature, copied);
549 }
550
551 #[test]
552 fn test_feature_partial_eq() {
553 assert_eq!(Feature::Trading, Feature::Trading);
554 assert_ne!(Feature::Trading, Feature::Bridging);
555 }
556
557 #[test]
558 fn test_features_config_debug_format() {
559 let config = FeaturesConfig::default();
560 let debug_string = format!("{:?}", config);
561 assert!(debug_string.contains("FeaturesConfig"));
562 }
563
564 #[test]
565 fn test_features_config_clone() {
566 let config = FeaturesConfig::default();
567 let cloned = config.clone();
568 assert_eq!(config.enable_trading, cloned.enable_trading);
569 assert_eq!(config.enable_bridging, cloned.enable_bridging);
570 }
571
572 #[test]
573 fn test_serde_serialization() {
574 let config = FeaturesConfig::default();
575 let serialized = serde_json::to_string(&config);
576 assert!(serialized.is_ok());
577 }
578
579 #[test]
580 fn test_serde_deserialization() {
581 let json = r#"{"enable_trading":true,"enable_bridging":false,"enable_social_monitoring":true,"enable_graph_memory":false,"enable_streaming":true,"enable_webhooks":false,"enable_analytics":false,"debug_mode":false,"experimental":false,"custom":{}}"#;
582 let config: Result<FeaturesConfig, _> = serde_json::from_str(json);
583 assert!(config.is_ok());
584 let config = config.unwrap();
585 assert!(config.enable_trading);
586 assert!(!config.enable_bridging);
587 assert!(config.enable_social_monitoring);
588 }
589
590 #[test]
591 fn test_serde_deserialization_with_defaults() {
592 let json = r#"{}"#;
593 let config: Result<FeaturesConfig, _> = serde_json::from_str(json);
594 assert!(config.is_ok());
595 let config = config.unwrap();
596 assert!(config.enable_trading);
598 assert!(!config.enable_bridging); assert!(!config.enable_social_monitoring);
600 assert!(config.enable_streaming);
601 }
602}