Skip to main content

starla_common/
config.rs

1//! Configuration management
2
3use serde::{Deserialize, Serialize};
4
5/// Main probe configuration
6#[derive(Debug, Clone, Default, Serialize, Deserialize)]
7pub struct ProbeConfig {
8    #[serde(default)]
9    pub probe: ProbeSettings,
10
11    #[serde(default)]
12    pub network: NetworkSettings,
13
14    #[serde(default)]
15    pub controller: ControllerSettings,
16
17    #[serde(default)]
18    pub storage: StorageSettings,
19
20    #[serde(default)]
21    pub metrics: MetricsSettings,
22
23    #[serde(default)]
24    pub logging: LoggingSettings,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct ProbeSettings {
29    /// Log level
30    #[serde(default = "default_log_level")]
31    pub log_level: String,
32}
33
34impl Default for ProbeSettings {
35    fn default() -> Self {
36        Self {
37            log_level: default_log_level(),
38        }
39    }
40}
41
42#[derive(Debug, Clone, Default, Serialize, Deserialize)]
43pub struct NetworkSettings {
44    #[serde(default)]
45    pub rxtxrpt: bool,
46
47    /// Enable the status socket for tray app communication.
48    /// Only needed on desktop systems. Default: false.
49    #[serde(default)]
50    pub status_socket: bool,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct ControllerSettings {
55    #[serde(default = "default_registration_servers")]
56    pub registration_servers: Vec<String>,
57
58    #[serde(default = "default_ssh_timeout")]
59    pub ssh_timeout: u64,
60
61    #[serde(default = "default_keepalive_interval")]
62    pub keepalive_interval: u64,
63}
64
65impl Default for ControllerSettings {
66    fn default() -> Self {
67        Self {
68            registration_servers: default_registration_servers(),
69            ssh_timeout: default_ssh_timeout(),
70            keepalive_interval: default_keepalive_interval(),
71        }
72    }
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct StorageSettings {
77    /// Maximum number of results to hold in the upload queue
78    #[serde(default = "default_max_queue_size")]
79    pub max_queue_size: usize,
80
81    #[serde(default = "default_retention_days")]
82    pub retention_days: u32,
83}
84
85impl Default for StorageSettings {
86    fn default() -> Self {
87        Self {
88            max_queue_size: default_max_queue_size(),
89            retention_days: default_retention_days(),
90        }
91    }
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct MetricsSettings {
96    #[serde(default = "default_metrics_enabled")]
97    pub enabled: bool,
98
99    #[serde(default = "default_metrics_listen_addr")]
100    pub listen_addr: String,
101}
102
103impl Default for MetricsSettings {
104    fn default() -> Self {
105        Self {
106            enabled: default_metrics_enabled(),
107            listen_addr: default_metrics_listen_addr(),
108        }
109    }
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct LoggingSettings {
114    #[serde(default = "default_log_format")]
115    pub format: String,
116
117    #[serde(default = "default_log_output")]
118    pub output: String,
119
120    #[serde(default = "default_max_file_size_mb")]
121    pub max_file_size_mb: u64,
122
123    #[serde(default = "default_max_files")]
124    pub max_files: u32,
125}
126
127impl Default for LoggingSettings {
128    fn default() -> Self {
129        Self {
130            format: default_log_format(),
131            output: default_log_output(),
132            max_file_size_mb: default_max_file_size_mb(),
133            max_files: default_max_files(),
134        }
135    }
136}
137
138// Default value functions
139fn default_log_level() -> String {
140    "info".to_string()
141}
142fn default_registration_servers() -> Vec<String> {
143    vec![
144        // Primary registration server (hostname, IPv4, IPv6)
145        "reg03.atlas.ripe.net:443".to_string(),
146        "193.0.19.246:443".to_string(),
147        "[2001:67c:2e8:11::c100:13f6]:443".to_string(),
148        // Secondary registration server (hostname, IPv4, IPv6)
149        "reg04.atlas.ripe.net:443".to_string(),
150        "193.0.19.247:443".to_string(),
151        "[2001:67c:2e8:11::c100:13f7]:443".to_string(),
152    ]
153}
154fn default_ssh_timeout() -> u64 {
155    30
156}
157fn default_keepalive_interval() -> u64 {
158    60
159}
160fn default_max_queue_size() -> usize {
161    10000
162}
163fn default_retention_days() -> u32 {
164    30
165}
166fn default_metrics_enabled() -> bool {
167    true
168}
169fn default_metrics_listen_addr() -> String {
170    "127.0.0.1:9695".to_string()
171}
172fn default_log_format() -> String {
173    "json".to_string()
174}
175fn default_log_output() -> String {
176    "stdout".to_string()
177}
178fn default_max_file_size_mb() -> u64 {
179    10
180}
181fn default_max_files() -> u32 {
182    5
183}
184
185impl ProbeConfig {
186    /// Load configuration from TOML file
187    pub fn from_file(path: &std::path::Path) -> Result<Self, crate::Error> {
188        let content = std::fs::read_to_string(path)
189            .map_err(|e| crate::Error::Config(format!("Failed to read config: {}", e)))?;
190
191        let config: ProbeConfig = toml::from_str(&content)
192            .map_err(|e| crate::Error::Config(format!("Failed to parse config: {}", e)))?;
193
194        Ok(config)
195    }
196
197    /// Save configuration to TOML file
198    pub fn to_file(&self, path: &std::path::Path) -> Result<(), crate::Error> {
199        let content = toml::to_string_pretty(self)
200            .map_err(|e| crate::Error::Config(format!("Failed to serialize config: {}", e)))?;
201
202        std::fs::write(path, content)
203            .map_err(|e| crate::Error::Config(format!("Failed to write config: {}", e)))?;
204
205        Ok(())
206    }
207}
208
209#[cfg(test)]
210mod tests {
211    use super::*;
212
213    #[test]
214    fn test_default_config() {
215        let config = ProbeConfig::default();
216        assert_eq!(config.probe.log_level, "info");
217        assert!(!config.network.rxtxrpt);
218    }
219
220    #[test]
221    fn test_config_serde() {
222        let config = ProbeConfig::default();
223        let toml_str = toml::to_string(&config).unwrap();
224        let parsed: ProbeConfig = toml::from_str(&toml_str).unwrap();
225        assert_eq!(parsed.probe.log_level, "info");
226    }
227}