kode_bridge/
config.rs

1use crate::errors::{KodeBridgeError, Result};
2use crate::pool::PoolConfig;
3use serde::{Deserialize, Serialize};
4use std::path::Path;
5use std::time::Duration;
6
7/// Global configuration for kode-bridge
8#[derive(Debug, Clone, Serialize, Deserialize, Default)]
9pub struct GlobalConfig {
10    /// Default client configuration
11    pub client: ClientGlobalConfig,
12    /// Streaming client configuration
13    pub streaming: StreamingGlobalConfig,
14    /// Logging configuration
15    pub logging: LoggingConfig,
16    /// Feature flags
17    pub features: FeatureFlags,
18}
19
20/// Client configuration
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct ClientGlobalConfig {
23    /// Default timeout for requests
24    pub default_timeout_ms: u64,
25    /// Enable connection pooling by default
26    pub enable_pooling: bool,
27    /// Pool configuration
28    pub pool: PoolConfig,
29    /// Retry configuration
30    pub max_retries: usize,
31    pub retry_delay_ms: u64,
32    /// Connection timeout
33    pub connection_timeout_ms: u64,
34}
35
36impl Default for ClientGlobalConfig {
37    fn default() -> Self {
38        Self {
39            default_timeout_ms: 10_000, // Reduce default timeout to 10 seconds
40            enable_pooling: true,
41            pool: PoolConfig::default(),
42            max_retries: 5,               // Increase retry attempts
43            retry_delay_ms: 50,           // Reduce retry delay
44            connection_timeout_ms: 5_000, // Reduce connection timeout
45        }
46    }
47}
48
49/// Streaming client configuration
50#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct StreamingGlobalConfig {
52    /// Default timeout for streaming connections
53    pub default_timeout_ms: u64,
54    /// Buffer size for streaming
55    pub buffer_size: usize,
56    /// Max retries for streaming connections
57    pub max_retries: usize,
58    /// Retry delay for streaming connections
59    pub retry_delay_ms: u64,
60}
61
62impl Default for StreamingGlobalConfig {
63    fn default() -> Self {
64        Self {
65            default_timeout_ms: 60_000,
66            buffer_size: 8192,
67            max_retries: 3,
68            retry_delay_ms: 100,
69        }
70    }
71}
72
73/// Logging configuration
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct LoggingConfig {
76    /// Enable tracing
77    pub enabled: bool,
78    /// Log level (trace, debug, info, warn, error)
79    pub level: String,
80    /// Enable structured logging
81    pub structured: bool,
82    /// Log request/response details
83    pub log_requests: bool,
84    /// Log connection events
85    pub log_connections: bool,
86}
87
88impl Default for LoggingConfig {
89    fn default() -> Self {
90        Self {
91            enabled: false,
92            level: "info".to_string(),
93            structured: false,
94            log_requests: false,
95            log_connections: false,
96        }
97    }
98}
99
100/// Feature flags for experimental or optional features
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct FeatureFlags {
103    /// Enable HTTP/2 support (future feature)
104    pub http2_support: bool,
105    /// Enable compression
106    pub compression: bool,
107    /// Enable request/response caching
108    pub caching: bool,
109    /// Enable metrics collection
110    pub metrics: bool,
111    /// Enable connection keep-alive optimization
112    pub keep_alive: bool,
113    /// Enable automatic reconnection
114    pub auto_reconnect: bool,
115}
116
117impl Default for FeatureFlags {
118    fn default() -> Self {
119        Self {
120            http2_support: false,
121            compression: false,
122            caching: false,
123            metrics: false,
124            keep_alive: true,
125            auto_reconnect: true,
126        }
127    }
128}
129
130impl GlobalConfig {
131    /// Load configuration from a TOML file
132    pub fn from_toml_file<P: AsRef<Path>>(path: P) -> Result<Self> {
133        let content = std::fs::read_to_string(path).map_err(|e| {
134            KodeBridgeError::configuration(format!("Failed to read config file: {}", e))
135        })?;
136
137        toml::from_str(&content).map_err(|e| {
138            KodeBridgeError::configuration(format!("Failed to parse TOML config: {}", e))
139        })
140    }
141
142    /// Load configuration from a JSON file
143    pub fn from_json_file<P: AsRef<Path>>(path: P) -> Result<Self> {
144        let content = std::fs::read_to_string(path).map_err(|e| {
145            KodeBridgeError::configuration(format!("Failed to read config file: {}", e))
146        })?;
147
148        serde_json::from_str(&content).map_err(|e| {
149            KodeBridgeError::configuration(format!("Failed to parse JSON config: {}", e))
150        })
151    }
152
153    /// Save configuration to a TOML file
154    pub fn save_toml_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
155        let content = toml::to_string_pretty(self).map_err(|e| {
156            KodeBridgeError::configuration(format!("Failed to serialize config: {}", e))
157        })?;
158
159        std::fs::write(path, content).map_err(|e| {
160            KodeBridgeError::configuration(format!("Failed to write config file: {}", e))
161        })
162    }
163
164    /// Save configuration to a JSON file
165    pub fn save_json_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
166        let content = serde_json::to_string_pretty(self).map_err(|e| {
167            KodeBridgeError::configuration(format!("Failed to serialize config: {}", e))
168        })?;
169
170        std::fs::write(path, content).map_err(|e| {
171            KodeBridgeError::configuration(format!("Failed to write config file: {}", e))
172        })
173    }
174
175    /// Apply environment variable overrides
176    pub fn apply_env_overrides(&mut self) {
177        if let Ok(timeout) = std::env::var("KODE_BRIDGE_TIMEOUT_MS") {
178            if let Ok(timeout_ms) = timeout.parse::<u64>() {
179                self.client.default_timeout_ms = timeout_ms;
180            }
181        }
182
183        if let Ok(pooling) = std::env::var("KODE_BRIDGE_ENABLE_POOLING") {
184            self.client.enable_pooling = pooling.to_lowercase() == "true";
185        }
186
187        if let Ok(retries) = std::env::var("KODE_BRIDGE_MAX_RETRIES") {
188            if let Ok(retries_num) = retries.parse::<usize>() {
189                self.client.max_retries = retries_num;
190            }
191        }
192
193        if let Ok(log_level) = std::env::var("KODE_BRIDGE_LOG_LEVEL") {
194            self.logging.level = log_level;
195            self.logging.enabled = true;
196        }
197
198        if let Ok(pool_size) = std::env::var("KODE_BRIDGE_POOL_SIZE") {
199            if let Ok(size) = pool_size.parse::<usize>() {
200                self.client.pool.max_size = size;
201            }
202        }
203    }
204
205    /// Convert client timeout to Duration
206    pub fn client_timeout(&self) -> Duration {
207        Duration::from_millis(self.client.default_timeout_ms)
208    }
209
210    /// Convert streaming timeout to Duration
211    pub fn streaming_timeout(&self) -> Duration {
212        Duration::from_millis(self.streaming.default_timeout_ms)
213    }
214
215    /// Convert retry delay to Duration
216    pub fn retry_delay(&self) -> Duration {
217        Duration::from_millis(self.client.retry_delay_ms)
218    }
219
220    /// Convert connection timeout to Duration
221    pub fn connection_timeout(&self) -> Duration {
222        Duration::from_millis(self.client.connection_timeout_ms)
223    }
224
225    /// Check if feature is enabled
226    pub fn is_feature_enabled(&self, feature: &str) -> bool {
227        match feature {
228            "http2" => self.features.http2_support,
229            "compression" => self.features.compression,
230            "caching" => self.features.caching,
231            "metrics" => self.features.metrics,
232            "keep_alive" => self.features.keep_alive,
233            "auto_reconnect" => self.features.auto_reconnect,
234            _ => false,
235        }
236    }
237
238    /// Validate configuration
239    pub fn validate(&self) -> Result<()> {
240        if self.client.default_timeout_ms == 0 {
241            return Err(KodeBridgeError::configuration(
242                "Client timeout cannot be zero",
243            ));
244        }
245
246        if self.client.max_retries > 10 {
247            return Err(KodeBridgeError::configuration(
248                "Max retries should be <= 10",
249            ));
250        }
251
252        if self.client.pool.max_size == 0 {
253            return Err(KodeBridgeError::configuration(
254                "Pool max size cannot be zero",
255            ));
256        }
257
258        if self.client.pool.min_idle > self.client.pool.max_size {
259            return Err(KodeBridgeError::configuration(
260                "Pool min_idle cannot be greater than max_size",
261            ));
262        }
263
264        if !["trace", "debug", "info", "warn", "error"].contains(&self.logging.level.as_str()) {
265            return Err(KodeBridgeError::configuration("Invalid log level"));
266        }
267
268        Ok(())
269    }
270}
271
272/// Configuration builder for fluent configuration setup
273pub struct ConfigBuilder {
274    config: GlobalConfig,
275}
276
277impl ConfigBuilder {
278    pub fn new() -> Self {
279        Self {
280            config: GlobalConfig::default(),
281        }
282    }
283
284    /// Set client timeout
285    pub fn client_timeout(mut self, timeout: Duration) -> Self {
286        self.config.client.default_timeout_ms = timeout.as_millis() as u64;
287        self
288    }
289
290    /// Enable or disable connection pooling
291    pub fn enable_pooling(mut self, enabled: bool) -> Self {
292        self.config.client.enable_pooling = enabled;
293        self
294    }
295
296    /// Set pool configuration
297    pub fn pool_config(mut self, pool_config: PoolConfig) -> Self {
298        self.config.client.pool = pool_config;
299        self
300    }
301
302    /// Set max retries
303    pub fn max_retries(mut self, retries: usize) -> Self {
304        self.config.client.max_retries = retries;
305        self
306    }
307
308    /// Enable logging
309    pub fn enable_logging(mut self, level: &str) -> Self {
310        self.config.logging.enabled = true;
311        self.config.logging.level = level.to_string();
312        self
313    }
314
315    /// Enable feature
316    pub fn enable_feature(mut self, feature: &str) -> Self {
317        match feature {
318            "compression" => self.config.features.compression = true,
319            "caching" => self.config.features.caching = true,
320            "metrics" => self.config.features.metrics = true,
321            "keep_alive" => self.config.features.keep_alive = true,
322            "auto_reconnect" => self.config.features.auto_reconnect = true,
323            _ => {}
324        }
325        self
326    }
327
328    /// Build the configuration
329    pub fn build(self) -> Result<GlobalConfig> {
330        self.config.validate()?;
331        Ok(self.config)
332    }
333}
334
335impl Default for ConfigBuilder {
336    fn default() -> Self {
337        Self::new()
338    }
339}