1use crate::errors::{KodeBridgeError, Result};
2use crate::pool::PoolConfig;
3use serde::{Deserialize, Serialize};
4use std::path::Path;
5use std::time::Duration;
6
7#[derive(Debug, Clone, Serialize, Deserialize, Default)]
9pub struct GlobalConfig {
10 pub client: ClientGlobalConfig,
12 pub streaming: StreamingGlobalConfig,
14 pub logging: LoggingConfig,
16 pub features: FeatureFlags,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct ClientGlobalConfig {
23 pub default_timeout_ms: u64,
25 pub enable_pooling: bool,
27 pub pool: PoolConfig,
29 pub max_retries: usize,
31 pub retry_delay_ms: u64,
32 pub connection_timeout_ms: u64,
34}
35
36impl Default for ClientGlobalConfig {
37 fn default() -> Self {
38 Self {
39 default_timeout_ms: 10_000, enable_pooling: true,
41 pool: PoolConfig::default(),
42 max_retries: 5, retry_delay_ms: 50, connection_timeout_ms: 5_000, }
46 }
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct StreamingGlobalConfig {
52 pub default_timeout_ms: u64,
54 pub buffer_size: usize,
56 pub max_retries: usize,
58 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#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct LoggingConfig {
76 pub enabled: bool,
78 pub level: String,
80 pub structured: bool,
82 pub log_requests: bool,
84 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#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct FeatureFlags {
103 pub http2_support: bool,
105 pub compression: bool,
107 pub caching: bool,
109 pub metrics: bool,
111 pub keep_alive: bool,
113 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 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 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 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 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 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 pub fn client_timeout(&self) -> Duration {
207 Duration::from_millis(self.client.default_timeout_ms)
208 }
209
210 pub fn streaming_timeout(&self) -> Duration {
212 Duration::from_millis(self.streaming.default_timeout_ms)
213 }
214
215 pub fn retry_delay(&self) -> Duration {
217 Duration::from_millis(self.client.retry_delay_ms)
218 }
219
220 pub fn connection_timeout(&self) -> Duration {
222 Duration::from_millis(self.client.connection_timeout_ms)
223 }
224
225 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 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
272pub struct ConfigBuilder {
274 config: GlobalConfig,
275}
276
277impl ConfigBuilder {
278 pub fn new() -> Self {
279 Self {
280 config: GlobalConfig::default(),
281 }
282 }
283
284 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 pub fn enable_pooling(mut self, enabled: bool) -> Self {
292 self.config.client.enable_pooling = enabled;
293 self
294 }
295
296 pub fn pool_config(mut self, pool_config: PoolConfig) -> Self {
298 self.config.client.pool = pool_config;
299 self
300 }
301
302 pub fn max_retries(mut self, retries: usize) -> Self {
304 self.config.client.max_retries = retries;
305 self
306 }
307
308 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 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 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}