config_lib/
enterprise.rs

1use crate::{Error, Result, Value};
2use std::collections::{BTreeMap, HashMap};
3use std::sync::{Arc, RwLock};
4use std::path::Path;
5
6/// Enterprise-grade configuration manager with caching and access control
7#[derive(Debug)]
8pub struct EnterpriseConfig {
9    /// In-memory cache for ultra-fast access
10    cache: Arc<RwLock<BTreeMap<String, Value>>>,
11    /// Default values for missing keys
12    defaults: Arc<RwLock<BTreeMap<String, Value>>>,
13    /// Original file path for save operations
14    file_path: Option<String>,
15    /// Format type for serialization
16    format: String,
17    /// Access control flag
18    read_only: bool,
19}
20
21/// Configuration manager for multiple instances
22#[derive(Debug, Default)]
23pub struct ConfigManager {
24    /// Named configuration instances
25    configs: Arc<RwLock<HashMap<String, EnterpriseConfig>>>,
26}
27
28impl EnterpriseConfig {
29    /// Create new config with defaults
30    #[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    /// Load configuration from file with caching
42    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        // Detect format from extension
47        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        // Cache the parsed data
55        if let Value::Table(table) = value {
56            *config.cache.write().unwrap() = table;
57        }
58        
59        Ok(config)
60    }
61    
62    /// Load configuration from string with caching
63    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        // Cache the parsed data
71        if let Value::Table(table) = value {
72            *config.cache.write().unwrap() = table;
73        }
74        
75        Ok(config)
76    }
77    
78    /// Get value with default fallback - enterprise API
79    #[inline(always)]
80    pub fn get(&self, key: &str) -> Option<&Value> {
81        // First check cache
82        let cache = self.cache.read().unwrap();
83        if let Some(value) = self.get_nested(&cache, key) {
84            // SAFETY: This is safe because we're extending the lifetime
85            // The cache is behind an Arc<RwLock> so it won't be dropped
86            return Some(unsafe { std::mem::transmute(value) });
87        }
88        
89        // Then check defaults
90        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    /// Get a value or return a default (ZERO-COPY optimized)
99    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            // ENTERPRISE: Avoid clone where possible - this still needs clone for T::from
105            // TODO: Consider implementing From<&Value> trait bounds for zero-copy
106            T::from(value.clone())
107        } else {
108            default
109        }
110    }
111    
112    /// Get with default value from defaults table
113    #[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            // Check defaults
119            let defaults = self.defaults.read().unwrap();
120            self.get_nested(&defaults, key).cloned()
121        }
122    }
123    
124    /// Check if key exists (enterprise API)
125    #[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    /// Set value (enterprise API) 
135    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    /// Set default value for key
146    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    /// Save configuration to file (format-preserving when possible)
152    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    /// Save to specific file
164    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    /// Get all keys (for debugging/inspection)
174    pub fn keys(&self) -> Vec<String> {
175        let cache = self.cache.read().unwrap();
176        self.collect_keys(&cache, "")
177    }
178    
179    /// Make config read-only for security
180    pub fn make_read_only(&mut self) {
181        self.read_only = true;
182    }
183    
184    /// Clear cache (enterprise operation)
185    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    /// Merge another config into this one
196    pub fn merge(&mut self, other: &EnterpriseConfig) -> Result<()> {
197        if self.read_only {
198            return Err(Error::general("Configuration is read-only"));
199        }
200        // ENTERPRISE: Optimized cache merge - minimize clones
201        let other_cache = other.cache.read().unwrap();
202        let mut self_cache = self.cache.write().unwrap();
203        
204        // ZERO-COPY: Use Arc/Rc for values to avoid cloning large data structures
205        for (key, value) in other_cache.iter() {
206            // Note: Key must be cloned for ownership, Value clone is still needed
207            // TODO: Consider Arc<Value> for cache storage to eliminate value clones
208            self_cache.insert(key.clone(), value.clone());
209        }
210        
211        Ok(())
212    }
213    
214    // --- PRIVATE HELPERS ---
215    
216    /// Detect format from file extension
217    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    /// Parse content based on format
230    fn parse_content(content: &str, format: &str) -> Result<Value> {
231        match format {
232            "conf" => {
233                // Use the regular conf parser for now
234                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    /// Get nested value using dot notation (zero-copy when possible)
255    #[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    /// Set nested value using dot notation
277    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        // Recursive helper function to avoid borrow checker issues
286        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            // Ensure the key exists and is a table
300            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    /// Collect all keys recursively
318    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    /// Serialize to specific format
339    fn serialize_to_format(&self, table: &BTreeMap<String, Value>, format: &str) -> Result<String> {
340        match format {
341            "conf" => {
342                // Basic CONF serialization (you can enhance this)
343                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    /// Convert value to string representation
362    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(), // Simplified for now
374        }
375    }
376}
377
378impl ConfigManager {
379    /// Create new config manager
380    pub fn new() -> Self {
381        Self::default()
382    }
383    
384    /// Load named configuration
385    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    /// Get named configuration
393    pub fn get(&self, name: &str) -> Option<Arc<RwLock<EnterpriseConfig>>> {
394        let configs = self.configs.read().unwrap();
395        configs.get(name).map(|config| {
396            // Return a reference wrapped in Arc for thread safety
397            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    /// List all configuration names
408    pub fn list(&self) -> Vec<String> {
409        let configs = self.configs.read().unwrap();
410        configs.keys().cloned().collect()
411    }
412    
413    /// Remove named configuration
414    pub fn remove(&self, name: &str) -> bool {
415        let mut configs = self.configs.write().unwrap();
416        configs.remove(name).is_some()
417    }
418}
419
420/// Direct parsing functions for maximum performance
421/// These bypass the caching layer for one-time parsing
422pub mod direct {
423    use super::*;
424    
425    /// Parse file directly to Value (no caching)
426    #[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    /// Parse string directly to Value (no caching)
433    #[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    /// Parse to array/vector for direct use
440    #[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        // Test existing value with manual extraction
469        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        // Test default value 
475        if let Some(_) = config.get("timeout") {
476            panic!("Should not find timeout key");
477        }
478        
479        // Test default behavior
480        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}