1use serde::{Deserialize, Serialize};
7use serde_yaml;
8use std::collections::HashMap;
9use std::path::Path;
10use std::sync::Arc;
11use thiserror::Error;
12use tokio::sync::RwLock;
13
14#[derive(Error, Debug)]
16pub enum ConfigError {
17 #[error("IO error: {0}")]
18 Io(#[from] std::io::Error),
19 #[error("YAML parsing error: {0}")]
20 Yaml(#[from] serde_yaml::Error),
21 #[error("Configuration validation error: {message}")]
22 Validation { message: String },
23 #[error("Key not found: {key}")]
24 KeyNotFound { key: String },
25 #[error("Type conversion error: {0}")]
26 TypeConversion(String),
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31#[serde(untagged)]
32pub enum ConfigValue {
33 String(String),
34 Integer(i64),
35 Float(f64),
36 Boolean(bool),
37 Array(Vec<ConfigValue>),
38 Object(HashMap<String, ConfigValue>),
39}
40
41impl ConfigValue {
42 pub fn as_string(&self) -> Result<String, ConfigError> {
43 match self {
44 ConfigValue::String(s) => Ok(s.clone()),
45 _ => Err(ConfigError::TypeConversion("Expected string".to_string())),
46 }
47 }
48
49 pub fn as_bool(&self) -> Result<bool, ConfigError> {
50 match self {
51 ConfigValue::Boolean(b) => Ok(*b),
52 _ => Err(ConfigError::TypeConversion("Expected boolean".to_string())),
53 }
54 }
55
56 pub fn as_i64(&self) -> Result<i64, ConfigError> {
57 match self {
58 ConfigValue::Integer(i) => Ok(*i),
59 _ => Err(ConfigError::TypeConversion("Expected integer".to_string())),
60 }
61 }
62}
63
64pub struct ConfigManager {
66 data: Arc<RwLock<HashMap<String, ConfigValue>>>,
67}
68
69impl Default for ConfigManager {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75impl ConfigManager {
76 pub fn new() -> Self {
77 Self {
78 data: Arc::new(RwLock::new(HashMap::new())),
79 }
80 }
81
82 pub async fn load_from_file<P: AsRef<Path>>(&self, path: P) -> Result<(), ConfigError> {
83 let content = tokio::fs::read_to_string(path).await?;
84 let yaml_value: serde_yaml::Value = serde_yaml::from_str(&content)?;
85
86 let mut data = self.data.write().await;
87 self.load_yaml_recursive(&yaml_value, "", &mut data)?;
88 Ok(())
89 }
90
91 fn load_yaml_recursive(
92 &self,
93 value: &serde_yaml::Value,
94 prefix: &str,
95 data: &mut HashMap<String, ConfigValue>,
96 ) -> Result<(), ConfigError> {
97 match value {
98 serde_yaml::Value::Mapping(map) => {
99 for (key, val) in map {
100 let key_str = key.as_str().ok_or_else(|| ConfigError::Validation {
101 message: "Non-string key in YAML".to_string(),
102 })?;
103 let full_key = if prefix.is_empty() {
104 key_str.to_string()
105 } else {
106 format!("{}.{}", prefix, key_str)
107 };
108
109 self.load_yaml_recursive(val, &full_key, data)?;
110 }
111 }
112 _ => {
113 let config_value = self.yaml_to_config_value(value)?;
114 data.insert(prefix.to_string(), config_value);
115 }
116 }
117 Ok(())
118 }
119
120 fn yaml_to_config_value(&self, value: &serde_yaml::Value) -> Result<ConfigValue, ConfigError> {
121 match value {
122 serde_yaml::Value::String(s) => Ok(ConfigValue::String(s.clone())),
123 serde_yaml::Value::Number(n) => {
124 if let Some(i) = n.as_i64() {
125 Ok(ConfigValue::Integer(i))
126 } else if let Some(f) = n.as_f64() {
127 Ok(ConfigValue::Float(f))
128 } else {
129 Err(ConfigError::TypeConversion("Invalid number".to_string()))
130 }
131 }
132 serde_yaml::Value::Bool(b) => Ok(ConfigValue::Boolean(*b)),
133 serde_yaml::Value::Sequence(seq) => {
134 let array: Result<Vec<_>, _> =
135 seq.iter().map(|v| self.yaml_to_config_value(v)).collect();
136 Ok(ConfigValue::Array(array?))
137 }
138 serde_yaml::Value::Mapping(map) => {
139 let mut object = HashMap::new();
140 for (k, v) in map {
141 let key = k
142 .as_str()
143 .ok_or_else(|| ConfigError::TypeConversion("Non-string key".to_string()))?;
144 object.insert(key.to_string(), self.yaml_to_config_value(v)?);
145 }
146 Ok(ConfigValue::Object(object))
147 }
148 serde_yaml::Value::Null => Ok(ConfigValue::String("".to_string())),
149 serde_yaml::Value::Tagged(tagged) => {
150 self.yaml_to_config_value(&tagged.value)
152 }
153 }
154 }
155
156 pub async fn get(&self, key: &str) -> Result<ConfigValue, ConfigError> {
157 let data = self.data.read().await;
158 data.get(key)
159 .cloned()
160 .ok_or_else(|| ConfigError::KeyNotFound {
161 key: key.to_string(),
162 })
163 }
164
165 pub async fn set(&self, key: &str, value: ConfigValue) -> Result<(), ConfigError> {
166 let mut data = self.data.write().await;
167 data.insert(key.to_string(), value);
168 Ok(())
169 }
170
171 pub async fn get_value(&self, key: &str) -> Result<ConfigValue, ConfigError> {
172 self.get(key).await
173 }
174
175 pub async fn set_value(&self, key: &str, value: ConfigValue) -> Result<(), ConfigError> {
176 self.set(key, value).await
177 }
178
179 pub async fn watch(&self, _key: &str) -> Result<(), ConfigError> {
180 Ok(())
182 }
183
184 pub async fn save_to_file<P: AsRef<Path>>(&self, _path: P) -> Result<(), ConfigError> {
185 Ok(())
187 }
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct AdapterConfig {
193 pub name: String,
194 pub enabled: bool,
195 pub settings: HashMap<String, ConfigValue>,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct MiddlewareConfig {
201 pub cors: CorsConfig,
202 pub compression: CompressionConfig,
203 pub rate_limiting: RateLimitingConfig,
204 pub security_headers: SecurityHeadersConfig,
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct CorsConfig {
210 pub enabled: bool,
211 pub allowed_origins: Vec<String>,
212 pub allowed_methods: Vec<String>,
213 pub allowed_headers: Vec<String>,
214 pub credentials: bool,
215 pub max_age: u32,
216}
217
218impl Default for CorsConfig {
219 fn default() -> Self {
220 Self {
221 enabled: true,
222 allowed_origins: vec!["*".to_string()],
223 allowed_methods: vec![
224 "GET".to_string(),
225 "POST".to_string(),
226 "PUT".to_string(),
227 "DELETE".to_string(),
228 ],
229 allowed_headers: vec!["Content-Type".to_string(), "Authorization".to_string()],
230 credentials: false,
231 max_age: 3600,
232 }
233 }
234}
235
236#[derive(Debug, Clone, Serialize, Deserialize)]
238pub struct CompressionConfig {
239 pub enabled: bool,
240 pub algorithms: Vec<String>,
241 pub min_size: usize,
242}
243
244impl Default for CompressionConfig {
245 fn default() -> Self {
246 Self {
247 enabled: true,
248 algorithms: vec!["gzip".to_string(), "br".to_string()],
249 min_size: 1024,
250 }
251 }
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct RateLimitingConfig {
257 pub enabled: bool,
258 pub requests_per_minute: u64,
259 pub burst_size: u64,
260 pub window_size: u64,
261}
262
263impl Default for RateLimitingConfig {
264 fn default() -> Self {
265 Self {
266 enabled: true,
267 requests_per_minute: 1000,
268 burst_size: 100,
269 window_size: 60,
270 }
271 }
272}
273
274#[derive(Debug, Clone, Serialize, Deserialize)]
276pub struct SecurityHeadersConfig {
277 pub enabled: bool,
278 pub content_security_policy: Option<String>,
279 pub x_frame_options: Option<String>,
280 pub x_content_type_options: bool,
281 pub strict_transport_security: Option<String>,
282}
283
284impl Default for SecurityHeadersConfig {
285 fn default() -> Self {
286 Self {
287 enabled: true,
288 content_security_policy: Some("default-src 'self'".to_string()),
289 x_frame_options: Some("DENY".to_string()),
290 x_content_type_options: true,
291 strict_transport_security: Some("max-age=31536000; includeSubDomains".to_string()),
292 }
293 }
294}
295
296#[derive(Debug, Clone, Serialize, Deserialize)]
298pub struct SecurityConfig {
299 pub enable_csrf_protection: bool,
300 pub enable_rate_limiting: bool,
301 pub enable_input_validation: bool,
302 pub enable_request_logging: bool,
303 pub max_request_size: usize,
304 pub allowed_file_types: Vec<String>,
305 pub tls: TlsConfig,
306 pub rate_limit_per_minute: Option<u32>,
307}
308
309impl Default for SecurityConfig {
310 fn default() -> Self {
311 Self {
312 enable_csrf_protection: true,
313 enable_rate_limiting: true,
314 enable_input_validation: true,
315 enable_request_logging: true,
316 max_request_size: 10 * 1024 * 1024, allowed_file_types: vec!["jpg".to_string(), "png".to_string(), "pdf".to_string()],
318 tls: TlsConfig::default(),
319 rate_limit_per_minute: Some(60), }
321 }
322}
323
324#[derive(Debug, Clone, Serialize, Deserialize)]
326pub struct MonitoringConfig {
327 pub enable_metrics: bool,
328 pub enable_tracing: bool,
329 pub enable_health_checks: bool,
330 pub enable_alerts: bool,
331 pub metrics_endpoint: String,
332 pub health_endpoint: String,
333}
334
335impl Default for MonitoringConfig {
336 fn default() -> Self {
337 Self {
338 enable_metrics: true,
339 enable_tracing: true,
340 enable_health_checks: true,
341 enable_alerts: true,
342 metrics_endpoint: "/metrics".to_string(),
343 health_endpoint: "/health".to_string(),
344 }
345 }
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize)]
350pub struct TlsConfig {
351 pub enabled: bool,
352 pub cert_file: String,
353 pub key_file: String,
354 pub ca_file: Option<String>,
355 pub verify_client: bool,
356}
357
358impl Default for TlsConfig {
359 fn default() -> Self {
360 Self {
361 enabled: false,
362 cert_file: "cert.pem".to_string(),
363 key_file: "key.pem".to_string(),
364 ca_file: None,
365 verify_client: false,
366 }
367 }
368}
369
370#[derive(Debug, Clone, Serialize, Deserialize)]
372pub struct FeatureFlags {
373 pub enable_caching: bool,
374 pub enable_compression: bool,
375 pub enable_websockets: bool,
376 pub enable_sse: bool,
377 pub enable_graphql: bool,
378}
379
380impl Default for FeatureFlags {
381 fn default() -> Self {
382 Self {
383 enable_caching: true,
384 enable_compression: true,
385 enable_websockets: false,
386 enable_sse: false,
387 enable_graphql: false,
388 }
389 }
390}
391
392#[derive(Debug, Clone, Serialize, Deserialize)]
394pub struct WebServerConfig {
395 pub server: ServerConfig,
396 pub adapters: Vec<AdapterConfig>,
397 pub middleware: MiddlewareConfig,
398 pub security: SecurityConfig,
399 pub monitoring: MonitoringConfig,
400 pub tls: TlsConfig,
401 pub feature_flags: FeatureFlags,
402}
403
404#[derive(Debug, Clone, Serialize, Deserialize)]
406pub struct ServerConfig {
407 pub host: String,
408 pub port: u16,
409 pub max_connections: usize,
410 pub timeout: u64,
411 pub keep_alive: bool,
412}
413
414impl Default for ServerConfig {
415 fn default() -> Self {
416 Self {
417 host: "127.0.0.1".to_string(),
418 port: 8080,
419 max_connections: 1000,
420 timeout: 30,
421 keep_alive: true,
422 }
423 }
424}
425
426impl Default for WebServerConfig {
427 fn default() -> Self {
428 Self {
429 server: ServerConfig::default(),
430 adapters: vec![],
431 middleware: MiddlewareConfig {
432 cors: CorsConfig::default(),
433 compression: CompressionConfig::default(),
434 rate_limiting: RateLimitingConfig::default(),
435 security_headers: SecurityHeadersConfig::default(),
436 },
437 security: SecurityConfig::default(),
438 monitoring: MonitoringConfig::default(),
439 tls: TlsConfig::default(),
440 feature_flags: FeatureFlags::default(),
441 }
442 }
443}
444
445pub struct UnifiedConfigManager {
447 manager: ConfigManager,
448 config: Arc<RwLock<WebServerConfig>>,
449}
450
451impl UnifiedConfigManager {
452 pub async fn new() -> Result<Self, ConfigError> {
453 let manager = ConfigManager::new();
454 let config = Arc::new(RwLock::new(WebServerConfig::default()));
455
456 Ok(Self { manager, config })
457 }
458
459 pub async fn load_from_file<P: AsRef<Path>>(&self, path: P) -> Result<(), ConfigError> {
460 self.manager.load_from_file(path).await?;
461 self.sync_config().await
462 }
463
464 async fn sync_config(&self) -> Result<(), ConfigError> {
465 Ok(())
467 }
468
469 pub async fn get_server_config(&self) -> ServerConfig {
470 self.config.read().await.server.clone()
471 }
472
473 pub async fn get_adapters_config(&self) -> Result<Vec<AdapterConfig>, ConfigError> {
474 Ok(self.config.read().await.adapters.clone())
475 }
476
477 pub async fn get_middleware_config(&self) -> Result<MiddlewareConfig, ConfigError> {
478 Ok(self.config.read().await.middleware.clone())
479 }
480
481 pub async fn get_security_config(&self) -> Result<SecurityConfig, ConfigError> {
482 Ok(self.config.read().await.security.clone())
483 }
484
485 pub async fn get_monitoring_config(&self) -> Result<MonitoringConfig, ConfigError> {
486 Ok(self.config.read().await.monitoring.clone())
487 }
488
489 pub async fn get_feature_flags(&self) -> Result<FeatureFlags, ConfigError> {
490 Ok(self.config.read().await.feature_flags.clone())
491 }
492
493 pub async fn is_feature_enabled(&self, feature: &str) -> Result<bool, ConfigError> {
494 let flags = self.get_feature_flags().await?;
495 let enabled = match feature {
496 "caching" => flags.enable_caching,
497 "compression" => flags.enable_compression,
498 "websockets" => flags.enable_websockets,
499 "sse" => flags.enable_sse,
500 "graphql" => flags.enable_graphql,
501 _ => false,
502 };
503 Ok(enabled)
504 }
505
506 pub async fn set_feature(&self, key: &str, value: ConfigValue) -> Result<(), ConfigError> {
507 self.manager.set(key, value).await
508 }
509
510 pub async fn watch_config(&self, _key: &str) -> Result<(), ConfigError> {
511 self.manager.watch(_key).await
512 }
513
514 pub async fn save_config<P: AsRef<Path>>(&self, path: P) -> Result<(), ConfigError> {
515 self.manager.save_to_file(path).await
516 }
517}
518
519#[cfg(test)]
520mod tests {
521 use super::*;
522 use tokio;
523
524 #[tokio::test]
525 async fn test_config_manager_creation() {
526 let manager = ConfigManager::new();
527
528 let test_value = ConfigValue::String("test".to_string());
530 manager.set("test_key", test_value.clone()).await.unwrap();
531
532 let retrieved = manager.get("test_key").await.unwrap();
533 match (test_value, retrieved) {
534 (ConfigValue::String(expected), ConfigValue::String(actual)) => {
535 assert_eq!(expected, actual);
536 }
537 _ => panic!("Values don't match"),
538 }
539 }
540
541 #[tokio::test]
542 async fn test_unified_config_manager() {
543 let manager = UnifiedConfigManager::new().await.unwrap();
544
545 let server_config = manager.get_server_config().await;
547 assert_eq!(server_config.host, "127.0.0.1");
548 assert_eq!(server_config.port, 8080);
549
550 let feature_flags = manager.get_feature_flags().await.unwrap();
551 assert!(feature_flags.enable_caching);
552 assert!(feature_flags.enable_compression);
553
554 let caching_enabled = manager.is_feature_enabled("caching").await.unwrap();
556 assert!(caching_enabled);
557
558 let unknown_feature = manager.is_feature_enabled("unknown").await.unwrap();
559 assert!(!unknown_feature);
560 }
561
562 #[tokio::test]
563 async fn test_config_value_conversions() {
564 let string_val = ConfigValue::String("test".to_string());
565 assert_eq!(string_val.as_string().unwrap(), "test");
566
567 let bool_val = ConfigValue::Boolean(true);
568 assert!(bool_val.as_bool().unwrap());
569
570 let int_val = ConfigValue::Integer(42);
571 assert_eq!(int_val.as_i64().unwrap(), 42);
572
573 assert!(string_val.as_bool().is_err());
575 assert!(bool_val.as_string().is_err());
576 assert!(int_val.as_string().is_err());
577 }
578}