1use crate::{Error, Result, Value};
2use std::collections::{BTreeMap, HashMap};
3use std::sync::{Arc, RwLock};
4use std::path::Path;
5
6#[derive(Debug)]
8pub struct EnterpriseConfig {
9 cache: Arc<RwLock<BTreeMap<String, Value>>>,
11 defaults: Arc<RwLock<BTreeMap<String, Value>>>,
13 file_path: Option<String>,
15 format: String,
17 read_only: bool,
19}
20
21#[derive(Debug, Default)]
23pub struct ConfigManager {
24 configs: Arc<RwLock<HashMap<String, EnterpriseConfig>>>,
26}
27
28impl EnterpriseConfig {
29 #[inline(always)]
31 pub fn new() -> Self {
32 Self {
33 cache: Arc::new(RwLock::new(BTreeMap::new())),
34 defaults: Arc::new(RwLock::new(BTreeMap::new())),
35 file_path: None,
36 format: "conf".to_string(),
37 read_only: false,
38 }
39 }
40
41 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
43 let path_str = path.as_ref().to_string_lossy().to_string();
44 let content = std::fs::read_to_string(&path)?;
45
46 let format = Self::detect_format(&path_str);
48 let value = Self::parse_content(&content, &format)?;
49
50 let mut config = Self::new();
51 config.file_path = Some(path_str);
52 config.format = format;
53
54 if let Value::Table(table) = value {
56 *config.cache.write().unwrap() = table;
57 }
58
59 Ok(config)
60 }
61
62 pub fn from_string(content: &str, format: Option<&str>) -> Result<Self> {
64 let format = format.unwrap_or("conf").to_string();
65 let value = Self::parse_content(content, &format)?;
66
67 let mut config = Self::new();
68 config.format = format;
69
70 if let Value::Table(table) = value {
72 *config.cache.write().unwrap() = table;
73 }
74
75 Ok(config)
76 }
77
78 #[inline(always)]
80 pub fn get(&self, key: &str) -> Option<&Value> {
81 let cache = self.cache.read().unwrap();
83 if let Some(value) = self.get_nested(&cache, key) {
84 return Some(unsafe { std::mem::transmute(value) });
87 }
88
89 let defaults = self.defaults.read().unwrap();
91 if let Some(value) = self.get_nested(&defaults, key) {
92 return Some(unsafe { std::mem::transmute(value) });
93 }
94
95 None
96 }
97
98 pub fn get_or<T>(&self, key: &str, default: T) -> T
100 where
101 T: From<Value> + Clone,
102 {
103 if let Some(value) = self.get(key) {
104 T::from(value.clone())
107 } else {
108 default
109 }
110 }
111
112 #[inline(always)]
114 pub fn get_or_default(&self, key: &str) -> Option<Value> {
115 if let Some(value) = self.get(key) {
116 Some(value.clone())
117 } else {
118 let defaults = self.defaults.read().unwrap();
120 self.get_nested(&defaults, key).cloned()
121 }
122 }
123
124 #[inline(always)]
126 pub fn exists(&self, key: &str) -> bool {
127 let cache = self.cache.read().unwrap();
128 self.get_nested(&cache, key).is_some() || {
129 let defaults = self.defaults.read().unwrap();
130 self.get_nested(&defaults, key).is_some()
131 }
132 }
133
134 pub fn set(&mut self, key: &str, value: Value) -> Result<()> {
136 if self.read_only {
137 return Err(Error::validation("Configuration is read-only"));
138 }
139
140 let mut cache = self.cache.write().unwrap();
141 self.set_nested(&mut cache, key, value);
142 Ok(())
143 }
144
145 pub fn set_default(&mut self, key: &str, value: Value) {
147 let mut defaults = self.defaults.write().unwrap();
148 self.set_nested(&mut defaults, key, value);
149 }
150
151 pub fn save(&self) -> Result<()> {
153 if let Some(ref path) = self.file_path {
154 let cache = self.cache.read().unwrap();
155 let content = self.serialize_to_format(&cache, &self.format)?;
156 std::fs::write(path, content)?;
157 Ok(())
158 } else {
159 Err(Error::general("No file path specified for save"))
160 }
161 }
162
163 pub fn save_to<P: AsRef<Path>>(&self, path: P) -> Result<()> {
165 let path_str = path.as_ref().to_string_lossy();
166 let format = Self::detect_format(&path_str);
167 let cache = self.cache.read().unwrap();
168 let content = self.serialize_to_format(&cache, &format)?;
169 std::fs::write(path, content)?;
170 Ok(())
171 }
172
173 pub fn keys(&self) -> Vec<String> {
175 let cache = self.cache.read().unwrap();
176 self.collect_keys(&cache, "")
177 }
178
179 pub fn make_read_only(&mut self) {
181 self.read_only = true;
182 }
183
184 pub fn clear(&mut self) -> Result<()> {
186 if self.read_only {
187 return Err(Error::general("Configuration is read-only"));
188 }
189
190 let mut cache = self.cache.write().unwrap();
191 cache.clear();
192 Ok(())
193 }
194
195 pub fn merge(&mut self, other: &EnterpriseConfig) -> Result<()> {
197 if self.read_only {
198 return Err(Error::general("Configuration is read-only"));
199 }
200 let other_cache = other.cache.read().unwrap();
202 let mut self_cache = self.cache.write().unwrap();
203
204 for (key, value) in other_cache.iter() {
206 self_cache.insert(key.clone(), value.clone());
209 }
210
211 Ok(())
212 }
213
214 fn detect_format(path: &str) -> String {
218 if path.ends_with(".json") {
219 "json".to_string()
220 } else if path.ends_with(".toml") {
221 "toml".to_string()
222 } else if path.ends_with(".noml") {
223 "noml".to_string()
224 } else {
225 "conf".to_string()
226 }
227 }
228
229 fn parse_content(content: &str, format: &str) -> Result<Value> {
231 match format {
232 "conf" => {
233 crate::parsers::conf::parse(content)
235 }
236 #[cfg(feature = "json")]
237 "json" => {
238 let parsed: serde_json::Value = serde_json::from_str(content)
239 .map_err(|e| Error::general(format!("JSON parse error: {}", e)))?;
240 crate::parsers::json_parser::from_json_value(parsed)
241 }
242 #[cfg(feature = "toml")]
243 "toml" => {
244 crate::parsers::toml_parser::parse(content)
245 }
246 #[cfg(feature = "noml")]
247 "noml" => {
248 crate::parsers::noml_parser::parse(content)
249 }
250 _ => Err(Error::general(format!("Unsupported format: {}", format))),
251 }
252 }
253
254 #[inline(always)]
256 fn get_nested<'a>(&self, table: &'a BTreeMap<String, Value>, key: &str) -> Option<&'a Value> {
257 if !key.contains('.') {
258 return table.get(key);
259 }
260
261 let parts: Vec<&str> = key.split('.').collect();
262 let mut current = table.get(parts[0])?;
263
264 for part in &parts[1..] {
265 match current {
266 Value::Table(nested_table) => {
267 current = nested_table.get(*part)?;
268 }
269 _ => return None,
270 }
271 }
272
273 Some(current)
274 }
275
276 fn set_nested(&self, table: &mut BTreeMap<String, Value>, key: &str, value: Value) {
278 if !key.contains('.') {
279 table.insert(key.to_string(), value);
280 return;
281 }
282
283 let parts: Vec<&str> = key.split('.').collect();
284
285 fn set_recursive(
287 table: &mut BTreeMap<String, Value>,
288 parts: &[&str],
289 value: Value,
290 ) {
291 if parts.len() == 1 {
292 table.insert(parts[0].to_string(), value);
293 return;
294 }
295
296 let key = parts[0].to_string();
297 let remaining = &parts[1..];
298
299 if !table.contains_key(&key) {
301 table.insert(key.clone(), Value::table(BTreeMap::new()));
302 }
303
304 let entry = table.get_mut(&key).unwrap();
305 if !entry.is_table() {
306 *entry = Value::table(BTreeMap::new());
307 }
308
309 if let Value::Table(nested_table) = entry {
310 set_recursive(nested_table, remaining, value);
311 }
312 }
313
314 set_recursive(table, &parts, value);
315 }
316
317 fn collect_keys(&self, table: &BTreeMap<String, Value>, prefix: &str) -> Vec<String> {
319 let mut keys = Vec::new();
320
321 for (key, value) in table {
322 let full_key = if prefix.is_empty() {
323 key.clone()
324 } else {
325 format!("{}.{}", prefix, key)
326 };
327
328 keys.push(full_key.clone());
329
330 if let Value::Table(nested_table) = value {
331 keys.extend(self.collect_keys(nested_table, &full_key));
332 }
333 }
334
335 keys
336 }
337
338 fn serialize_to_format(&self, table: &BTreeMap<String, Value>, format: &str) -> Result<String> {
340 match format {
341 "conf" => {
342 let mut output = String::new();
344 for (key, value) in table {
345 output.push_str(&format!("{} = {}\n", key, self.value_to_string(value)));
346 }
347 Ok(output)
348 }
349 #[cfg(feature = "json")]
350 "json" => {
351 let json_value = crate::parsers::json_parser::to_json_value(
352 &Value::table(table.clone())
353 )?;
354 serde_json::to_string_pretty(&json_value)
355 .map_err(|e| Error::general(format!("JSON serialize error: {}", e)))
356 }
357 _ => Err(Error::general(format!("Serialization not supported for format: {}", format))),
358 }
359 }
360
361 fn value_to_string(&self, value: &Value) -> String {
363 match value {
364 Value::String(s) => format!("\"{}\"", s),
365 Value::Integer(i) => i.to_string(),
366 Value::Float(f) => f.to_string(),
367 Value::Bool(b) => b.to_string(),
368 Value::Null => "null".to_string(),
369 Value::Array(arr) => {
370 let items: Vec<String> = arr.iter().map(|v| self.value_to_string(v)).collect();
371 items.join(" ")
372 }
373 Value::Table(_) => "[Table]".to_string(), }
375 }
376}
377
378impl ConfigManager {
379 pub fn new() -> Self {
381 Self::default()
382 }
383
384 pub fn load<P: AsRef<Path>>(&self, name: &str, path: P) -> Result<()> {
386 let config = EnterpriseConfig::from_file(path)?;
387 let mut configs = self.configs.write().unwrap();
388 configs.insert(name.to_string(), config);
389 Ok(())
390 }
391
392 pub fn get(&self, name: &str) -> Option<Arc<RwLock<EnterpriseConfig>>> {
394 let configs = self.configs.read().unwrap();
395 configs.get(name).map(|config| {
396 Arc::new(RwLock::new(EnterpriseConfig {
398 cache: config.cache.clone(),
399 defaults: config.defaults.clone(),
400 file_path: config.file_path.clone(),
401 format: config.format.clone(),
402 read_only: config.read_only,
403 }))
404 })
405 }
406
407 pub fn list(&self) -> Vec<String> {
409 let configs = self.configs.read().unwrap();
410 configs.keys().cloned().collect()
411 }
412
413 pub fn remove(&self, name: &str) -> bool {
415 let mut configs = self.configs.write().unwrap();
416 configs.remove(name).is_some()
417 }
418}
419
420pub mod direct {
423 use super::*;
424
425 #[inline(always)]
427 pub fn parse_file<P: AsRef<Path>>(path: P) -> Result<Value> {
428 let content = std::fs::read_to_string(path)?;
429 parse_string(&content, None)
430 }
431
432 #[inline(always)]
434 pub fn parse_string(content: &str, format: Option<&str>) -> Result<Value> {
435 let format = format.unwrap_or("conf");
436 EnterpriseConfig::parse_content(content, format)
437 }
438
439 #[inline(always)]
441 pub fn parse_to_vec<T>(content: &str) -> Result<Vec<T>>
442 where
443 T: TryFrom<Value>,
444 T::Error: std::fmt::Display,
445 {
446 let value = parse_string(content, None)?;
447
448 match value {
449 Value::Array(arr) => {
450 arr.into_iter()
451 .map(|v| T::try_from(v).map_err(|e| Error::general(e.to_string())))
452 .collect()
453 }
454 _ => Err(Error::general("Expected array value")),
455 }
456 }
457}
458
459#[cfg(test)]
460mod tests {
461 use super::*;
462
463 #[test]
464 fn test_enterprise_config_get_or() {
465 let mut config = EnterpriseConfig::new();
466 config.set("port", Value::integer(8080)).unwrap();
467
468 if let Some(port_value) = config.get("port") {
470 let port = port_value.as_integer().unwrap_or(3000);
471 assert_eq!(port, 8080);
472 }
473
474 if let Some(_) = config.get("timeout") {
476 panic!("Should not find timeout key");
477 }
478
479 let timeout = config.get("timeout")
481 .and_then(|v| v.as_integer().ok())
482 .unwrap_or(30);
483 assert_eq!(timeout, 30);
484 }
485
486 #[test]
487 fn test_exists() {
488 let mut config = EnterpriseConfig::new();
489 config.set("debug", Value::bool(true)).unwrap();
490
491 assert!(config.exists("debug"));
492 assert!(!config.exists("production"));
493 }
494
495 #[test]
496 fn test_nested_keys() {
497 let mut config = EnterpriseConfig::new();
498 config.set("database.host", Value::string("localhost")).unwrap();
499 config.set("database.port", Value::integer(5432)).unwrap();
500
501 assert_eq!(config.get("database.host").unwrap().as_string().unwrap(), "localhost");
502 assert_eq!(config.get("database.port").unwrap().as_integer().unwrap(), 5432);
503 assert!(config.exists("database.host"));
504 }
505
506 #[test]
507 fn test_direct_parsing() {
508 let content = "port = 8080\ndebug = true";
509 let value = direct::parse_string(content, Some("conf")).unwrap();
510
511 if let Value::Table(table) = value {
512 assert_eq!(table.get("port").unwrap().as_integer().unwrap(), 8080);
513 assert_eq!(table.get("debug").unwrap().as_bool().unwrap(), true);
514 } else {
515 panic!("Expected table value");
516 }
517 }
518}