ultrafast_mcp_core/config/
base.rs

1//! Base configuration traits and common implementations
2//!
3//! This module provides common configuration patterns that can be reused
4//! across all crates to reduce duplication.
5
6use crate::error::{MCPResult, ValidationError};
7use crate::validation::timeout::{
8    validate_backoff_multiplier, validate_retry_count, validate_timeout,
9};
10use serde::{Deserialize, Serialize};
11use std::time::Duration;
12
13/// Common configuration defaults
14pub trait ConfigDefaults {
15    fn default_timeout() -> Duration {
16        Duration::from_secs(30)
17    }
18
19    fn default_retries() -> u32 {
20        3
21    }
22
23    fn default_backoff_multiplier() -> f64 {
24        2.0
25    }
26
27    fn default_host() -> String {
28        "127.0.0.1".to_string()
29    }
30
31    fn default_port() -> u16 {
32        8080
33    }
34}
35
36/// Base configuration trait that all config structs should implement
37pub trait BaseConfig:
38    Default + Serialize + for<'de> Deserialize<'de> + Clone + Send + Sync
39{
40    /// Validate the configuration
41    fn validate(&self) -> MCPResult<()>;
42
43    /// Get configuration name for logging/debugging
44    fn config_name(&self) -> &'static str;
45}
46
47/// Common timeout configuration
48#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
49pub struct TimeoutConfig {
50    pub connect_timeout: Duration,
51    pub request_timeout: Duration,
52    pub shutdown_timeout: Duration,
53}
54
55impl Default for TimeoutConfig {
56    fn default() -> Self {
57        Self {
58            connect_timeout: Duration::from_secs(10),
59            request_timeout: Self::default_timeout(),
60            shutdown_timeout: Duration::from_secs(5),
61        }
62    }
63}
64
65impl ConfigDefaults for TimeoutConfig {}
66
67impl BaseConfig for TimeoutConfig {
68    fn validate(&self) -> MCPResult<()> {
69        validate_timeout(self.connect_timeout)?;
70        validate_timeout(self.request_timeout)?;
71        validate_timeout(self.shutdown_timeout)?;
72        Ok(())
73    }
74
75    fn config_name(&self) -> &'static str {
76        "TimeoutConfig"
77    }
78}
79
80/// Common retry configuration
81#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
82pub struct RetryConfig {
83    pub max_retries: u32,
84    pub initial_delay: Duration,
85    pub max_delay: Duration,
86    pub backoff_multiplier: f64,
87    pub enable_jitter: bool,
88}
89
90impl Default for RetryConfig {
91    fn default() -> Self {
92        Self {
93            max_retries: Self::default_retries(),
94            initial_delay: Duration::from_millis(100),
95            max_delay: Duration::from_secs(30),
96            backoff_multiplier: Self::default_backoff_multiplier(),
97            enable_jitter: true,
98        }
99    }
100}
101
102impl ConfigDefaults for RetryConfig {}
103
104impl BaseConfig for RetryConfig {
105    fn validate(&self) -> MCPResult<()> {
106        validate_retry_count(self.max_retries)?;
107        validate_timeout(self.initial_delay)?;
108        validate_timeout(self.max_delay)?;
109        validate_backoff_multiplier(self.backoff_multiplier)?;
110
111        if self.initial_delay > self.max_delay {
112            return Err(ValidationError::InvalidFormat {
113                field: "initial_delay".to_string(),
114                expected: "less than or equal to max_delay".to_string(),
115            }
116            .into());
117        }
118
119        Ok(())
120    }
121
122    fn config_name(&self) -> &'static str {
123        "RetryConfig"
124    }
125}
126
127/// Common network configuration
128#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
129pub struct NetworkConfig {
130    pub host: String,
131    pub port: u16,
132    pub bind_address: Option<String>,
133    pub enable_keepalive: bool,
134    pub keepalive_timeout: Duration,
135}
136
137impl Default for NetworkConfig {
138    fn default() -> Self {
139        Self {
140            host: Self::default_host(),
141            port: Self::default_port(),
142            bind_address: None,
143            enable_keepalive: true,
144            keepalive_timeout: Duration::from_secs(60),
145        }
146    }
147}
148
149impl ConfigDefaults for NetworkConfig {}
150
151impl BaseConfig for NetworkConfig {
152    fn validate(&self) -> MCPResult<()> {
153        if self.host.is_empty() {
154            return Err(ValidationError::RequiredField {
155                field: "host".to_string(),
156            }
157            .into());
158        }
159
160        if self.port == 0 {
161            return Err(ValidationError::ValueOutOfRange {
162                field: "port".to_string(),
163                min: "1".to_string(),
164                max: "65535".to_string(),
165                actual: "0".to_string(),
166            }
167            .into());
168        }
169
170        validate_timeout(self.keepalive_timeout)?;
171
172        Ok(())
173    }
174
175    fn config_name(&self) -> &'static str {
176        "NetworkConfig"
177    }
178}
179
180/// Security configuration
181#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
182pub struct SecurityConfig {
183    pub enable_tls: bool,
184    pub cert_path: Option<String>,
185    pub key_path: Option<String>,
186    pub ca_cert_path: Option<String>,
187    pub allow_insecure: bool,
188    pub allowed_origins: Vec<String>,
189}
190
191impl Default for SecurityConfig {
192    fn default() -> Self {
193        Self {
194            enable_tls: false,
195            cert_path: None,
196            key_path: None,
197            ca_cert_path: None,
198            allow_insecure: true, // Default to true for development
199            allowed_origins: vec!["*".to_string()], // Default to allow all for development
200        }
201    }
202}
203
204impl BaseConfig for SecurityConfig {
205    fn validate(&self) -> MCPResult<()> {
206        if self.enable_tls {
207            if self.cert_path.is_none() {
208                return Err(ValidationError::RequiredField {
209                    field: "cert_path".to_string(),
210                }
211                .into());
212            }
213
214            if self.key_path.is_none() {
215                return Err(ValidationError::RequiredField {
216                    field: "key_path".to_string(),
217                }
218                .into());
219            }
220        }
221
222        Ok(())
223    }
224
225    fn config_name(&self) -> &'static str {
226        "SecurityConfig"
227    }
228}
229
230/// Comprehensive configuration builder
231#[derive(Debug, Clone, Serialize, Deserialize)]
232pub struct ConfigBuilder<T> {
233    config: T,
234}
235
236impl<T: BaseConfig> ConfigBuilder<T> {
237    pub fn new(config: T) -> Self {
238        Self { config }
239    }
240
241    pub fn build(self) -> MCPResult<T> {
242        self.config.validate()?;
243        Ok(self.config)
244    }
245
246    pub fn validate(&self) -> MCPResult<()> {
247        self.config.validate()
248    }
249}
250
251/// Macro to implement common configuration patterns
252#[macro_export]
253macro_rules! impl_config_defaults {
254    ($config_type:ty, $name:expr) => {
255        impl $crate::config::base::ConfigDefaults for $config_type {}
256
257        impl $crate::config::base::BaseConfig for $config_type {
258            fn validate(&self) -> $crate::MCPResult<()> {
259                // Default implementation - override if needed
260                Ok(())
261            }
262
263            fn config_name(&self) -> &'static str {
264                $name
265            }
266        }
267    };
268}
269
270#[cfg(test)]
271mod tests {
272    use super::*;
273
274    #[test]
275    fn test_timeout_config_defaults() {
276        let config = TimeoutConfig::default();
277        assert_eq!(config.connect_timeout, Duration::from_secs(10));
278        assert_eq!(config.request_timeout, Duration::from_secs(30));
279        assert_eq!(config.shutdown_timeout, Duration::from_secs(5));
280    }
281
282    #[test]
283    fn test_timeout_config_validation() {
284        let config = TimeoutConfig::default();
285        assert!(config.validate().is_ok());
286
287        let invalid_config = TimeoutConfig {
288            connect_timeout: Duration::from_millis(50), // Too short
289            request_timeout: Duration::from_secs(30),
290            shutdown_timeout: Duration::from_secs(5),
291        };
292        assert!(invalid_config.validate().is_err());
293    }
294
295    #[test]
296    fn test_retry_config_defaults() {
297        let config = RetryConfig::default();
298        assert_eq!(config.max_retries, 3);
299        assert_eq!(config.initial_delay, Duration::from_millis(100));
300        assert_eq!(config.max_delay, Duration::from_secs(30));
301        assert_eq!(config.backoff_multiplier, 2.0);
302        assert!(config.enable_jitter);
303    }
304
305    #[test]
306    fn test_retry_config_validation() {
307        let config = RetryConfig::default();
308        assert!(config.validate().is_ok());
309
310        let invalid_config = RetryConfig {
311            max_retries: 15, // Too many
312            ..Default::default()
313        };
314        assert!(invalid_config.validate().is_err());
315    }
316
317    #[test]
318    fn test_network_config_defaults() {
319        let config = NetworkConfig::default();
320        assert_eq!(config.host, "127.0.0.1");
321        assert_eq!(config.port, 8080);
322        assert!(config.enable_keepalive);
323    }
324
325    #[test]
326    fn test_security_config_defaults() {
327        let config = SecurityConfig::default();
328        assert!(!config.enable_tls);
329        assert!(config.allow_insecure);
330        assert_eq!(config.allowed_origins, vec!["*"]);
331    }
332
333    #[test]
334    fn test_config_builder() {
335        let config = TimeoutConfig::default();
336        let builder = ConfigBuilder::new(config);
337        let built_config = builder.build().unwrap();
338        assert_eq!(built_config.config_name(), "TimeoutConfig");
339    }
340}