event_notification/
config.rs

1use crate::Error;
2use figment::providers::Format;
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6/// Configuration for the notification system.
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct WebhookConfig {
9    pub endpoint: String,
10    pub auth_token: Option<String>,
11    pub custom_headers: Option<HashMap<String, String>>,
12    pub max_retries: u32,
13    pub timeout: u64,
14}
15
16impl WebhookConfig {
17    /// verify that the configuration is valid
18    pub fn validate(&self) -> Result<(), String> {
19        // verify that endpoint cannot be empty
20        if self.endpoint.trim().is_empty() {
21            return Err("Webhook endpoint cannot be empty".to_string());
22        }
23
24        // verification timeout must be reasonable
25        if self.timeout == 0 {
26            return Err("Webhook timeout must be greater than 0".to_string());
27        }
28
29        // Verify that the maximum number of retry is reasonable
30        if self.max_retries > 10 {
31            return Err("Maximum retry count cannot exceed 10".to_string());
32        }
33
34        Ok(())
35    }
36}
37
38/// Configuration for the Kafka adapter.
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct KafkaConfig {
41    pub brokers: String,
42    pub topic: String,
43    pub max_retries: u32,
44}
45
46/// Configuration for the MQTT adapter.
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct MqttConfig {
49    pub broker: String,
50    pub port: u16,
51    pub client_id: String,
52    pub topic: String,
53    pub max_retries: u32,
54}
55
56/// Configuration for the notification system.
57#[derive(Debug, Clone, Serialize, Deserialize)]
58#[serde(tag = "type")]
59pub enum AdapterConfig {
60    Webhook(WebhookConfig),
61    Kafka(KafkaConfig),
62    Mqtt(MqttConfig),
63}
64
65/// Configuration for the notification system.
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct NotificationConfig {
68    #[serde(default = "default_store_path")]
69    pub store_path: String,
70    #[serde(default = "default_channel_capacity")]
71    pub channel_capacity: usize,
72    pub adapters: Vec<AdapterConfig>,
73}
74
75impl Default for NotificationConfig {
76    fn default() -> Self {
77        Self {
78            store_path: default_store_path(),
79            channel_capacity: default_channel_capacity(),
80            adapters: Vec::new(),
81        }
82    }
83}
84
85impl NotificationConfig {
86    /// create a new configuration with default values
87    pub fn new() -> Self {
88        Self::default()
89    }
90
91    /// create a configuration from a configuration file
92    pub fn from_file(path: &str) -> Result<Self, Error> {
93        let config = figment::Figment::new()
94            .merge(figment::providers::Toml::file(path))
95            .extract()?;
96
97        Ok(config)
98    }
99
100    /// Read configuration from multiple sources (support TOML, YAML, .env)
101    pub fn load() -> Result<Self, Error> {
102        let figment = figment::Figment::new()
103            // First try to read the config.toml of the current directory
104            .merge(figment::providers::Toml::file("event.toml"))
105            // Then try to read the config.yaml of the current directory
106            .merge(figment::providers::Yaml::file("event.yaml"))
107            // Finally read the environment variable and overwrite the previous value
108            .merge(figment::providers::Env::prefixed("EVENT_NOTIF_"));
109
110        Ok(figment.extract()?)
111    }
112
113    /// loading configuration from env file
114    pub fn from_env_file(path: &str) -> Result<Self, Error> {
115        // loading env files
116        dotenv::from_path(path)
117            .map_err(|e| Error::ConfigError(format!("unable to load env file: {}", e)))?;
118
119        // Extract configuration from environment variables using figurement
120        let figment =
121            figment::Figment::new().merge(figment::providers::Env::prefixed("EVENT_NOTIF_"));
122
123        Ok(figment.extract()?)
124    }
125}
126
127/// Provide temporary directories as default storage paths
128fn default_store_path() -> String {
129    std::env::temp_dir()
130        .join("event-notification")
131        .to_string_lossy()
132        .to_string()
133}
134
135/// Provides the recommended default channel capacity for high concurrency systems
136fn default_channel_capacity() -> usize {
137    10000 // Reasonable default values for high concurrency systems
138}