config_lib/
value.rs

1//! Value types and operations for the config library.
2//!
3//! This module provides a flexible value system that can represent various data types
4//! commonly found in configuration files.
5
6use crate::error::{Error, Result};
7use std::collections::BTreeMap;
8use std::fmt;
9
10/// Represents a configuration value.
11#[derive(Debug, Clone, PartialEq)]
12pub enum Value {
13    /// Null/empty value
14    Null,
15    /// Boolean value
16    Bool(bool),
17    /// Integer value
18    Integer(i64),
19    /// Floating point value
20    Float(f64),
21    /// String value
22    String(String),
23    /// Array of values
24    Array(Vec<Value>),
25    /// Table (key-value pairs)
26    Table(BTreeMap<String, Value>),
27}
28
29impl Value {
30    /// Create a new null value
31    pub fn null() -> Self {
32        Value::Null
33    }
34
35    /// Create a new boolean value
36    pub fn bool(value: bool) -> Self {
37        Value::Bool(value)
38    }
39
40    /// Create a new integer value
41    pub fn integer(value: i64) -> Self {
42        Value::Integer(value)
43    }
44
45    /// Create a new float value
46    pub fn float(value: f64) -> Self {
47        Value::Float(value)
48    }
49
50    /// Create a new string value
51    pub fn string<S: Into<String>>(value: S) -> Self {
52        Value::String(value.into())
53    }
54
55    /// Create a new array value
56    pub fn array(values: Vec<Value>) -> Self {
57        Value::Array(values)
58    }
59
60    /// Create a new table value
61    pub fn table(table: BTreeMap<String, Value>) -> Self {
62        Value::Table(table)
63    }
64
65    /// Get the type name of this value
66    pub fn type_name(&self) -> &'static str {
67        match self {
68            Value::Null => "null",
69            Value::Bool(_) => "bool",
70            Value::Integer(_) => "integer",
71            Value::Float(_) => "float",
72            Value::String(_) => "string",
73            Value::Array(_) => "array",
74            Value::Table(_) => "table",
75        }
76    }
77
78    /// Check if this value is null
79    pub fn is_null(&self) -> bool {
80        matches!(self, Value::Null)
81    }
82
83    /// Check if this value is a boolean
84    pub fn is_bool(&self) -> bool {
85        matches!(self, Value::Bool(_))
86    }
87
88    /// Check if this value is an integer
89    pub fn is_integer(&self) -> bool {
90        matches!(self, Value::Integer(_))
91    }
92
93    /// Check if this value is a float
94    pub fn is_float(&self) -> bool {
95        matches!(self, Value::Float(_))
96    }
97
98    /// Check if this value is a string
99    pub fn is_string(&self) -> bool {
100        matches!(self, Value::String(_))
101    }
102
103    /// Check if this value is an array
104    pub fn is_array(&self) -> bool {
105        matches!(self, Value::Array(_))
106    }
107
108    /// Check if this value is a table
109    pub fn is_table(&self) -> bool {
110        matches!(self, Value::Table(_))
111    }
112
113    /// Try to convert this value to a boolean
114    pub fn as_bool(&self) -> Result<bool> {
115        match self {
116            Value::Bool(b) => Ok(*b),
117            Value::String(s) => {
118                match s.to_lowercase().as_str() {
119                    "true" | "yes" | "1" | "on" => Ok(true),
120                    "false" | "no" | "0" | "off" => Ok(false),
121                    _ => Err(Error::type_error(
122                        "Cannot convert to boolean",
123                        "bool",
124                        self.type_name(),
125                    )),
126                }
127            },
128            _ => Err(Error::type_error(
129                "Cannot convert to boolean",
130                "bool",
131                self.type_name(),
132            )),
133        }
134    }
135
136    /// Try to convert this value to an integer
137    pub fn as_integer(&self) -> Result<i64> {
138        match self {
139            Value::Integer(i) => Ok(*i),
140            Value::Float(f) => Ok(*f as i64),
141            Value::String(s) => s.parse::<i64>().map_err(|_| Error::type_error(
142                "Cannot convert to integer",
143                "integer", 
144                self.type_name(),
145            )),
146            _ => Err(Error::type_error(
147                "Cannot convert to integer",
148                "integer",
149                self.type_name(),
150            )),
151        }
152    }
153
154    /// Try to convert this value to a float
155    pub fn as_float(&self) -> Result<f64> {
156        match self {
157            Value::Float(f) => Ok(*f),
158            Value::Integer(i) => Ok(*i as f64),
159            Value::String(s) => s.parse::<f64>().map_err(|_| Error::type_error(
160                "Cannot convert to float",
161                "float",
162                self.type_name(),
163            )),
164            _ => Err(Error::type_error(
165                "Cannot convert to float",
166                "float",
167                self.type_name(),
168            )),
169        }
170    }
171
172    /// Try to convert this value to a string - ZERO-COPY optimized
173    pub fn as_string(&self) -> Result<&str> {
174        match self {
175            Value::String(s) => Ok(s.as_str()),
176            _ => Err(Error::type_error(
177                "Cannot convert to string",
178                "string", 
179                self.type_name(),
180            )),
181        }
182    }
183
184    /// Convert this value to a string representation (allocating)
185    pub fn to_string_representation(&self) -> Result<String> {
186        match self {
187            Value::String(s) => Ok(s.clone()),
188            Value::Integer(i) => Ok(i.to_string()),
189            Value::Float(f) => Ok(f.to_string()),
190            Value::Bool(b) => Ok(b.to_string()),
191            _ => Err(Error::type_error(
192                "Cannot convert to string representation",
193                "string",
194                self.type_name(),
195            )),
196        }
197    }
198
199    /// Try to get this value as an array
200    pub fn as_array(&self) -> Result<&Vec<Value>> {
201        match self {
202            Value::Array(arr) => Ok(arr),
203            _ => Err(Error::type_error(
204                "Cannot convert to array",
205                "array",
206                self.type_name(),
207            )),
208        }
209    }
210
211    /// Try to get this value as a mutable array
212    pub fn as_array_mut(&mut self) -> Result<&mut Vec<Value>> {
213        match self {
214            Value::Array(arr) => Ok(arr),
215            _ => Err(Error::type_error(
216                "Cannot convert to array",
217                "array",
218                self.type_name(),
219            )),
220        }
221    }
222
223    /// Try to get this value as a table
224    pub fn as_table(&self) -> Result<&BTreeMap<String, Value>> {
225        match self {
226            Value::Table(table) => Ok(table),
227            _ => Err(Error::type_error(
228                "Cannot convert to table",
229                "table",
230                self.type_name(),
231            )),
232        }
233    }
234
235    /// Try to get this value as a mutable table
236    pub fn as_table_mut(&mut self) -> Result<&mut BTreeMap<String, Value>> {
237        match self {
238            Value::Table(table) => Ok(table),
239            _ => Err(Error::type_error(
240                "Cannot convert to table",
241                "table",
242                self.type_name(),
243            )),
244        }
245    }
246
247    /// Get a value by path (dot-separated)
248    pub fn get(&self, path: &str) -> Option<&Value> {
249        if path.is_empty() {
250            return Some(self);
251        }
252
253        let parts: Vec<&str> = path.split('.').collect();
254        let mut current = self;
255
256        for part in parts {
257            match current {
258                Value::Table(table) => {
259                    current = table.get(part)?;
260                }
261                _ => return None,
262            }
263        }
264
265        Some(current)
266    }
267
268    /// Get a mutable reference to a value by path (ENTERPRISE ERROR HANDLING)
269    pub fn get_mut_nested(&mut self, path: &str) -> Result<&mut Value> {
270        if path.is_empty() {
271            return Ok(self);
272        }
273
274        let parts: Vec<&str> = path.split('.').collect();
275        if parts.is_empty() {
276            return Err(Error::key_not_found(path));
277        }
278
279        let (last_key, parent_path) = parts.split_last()
280            .ok_or_else(|| Error::key_not_found(path))?;
281
282        // Navigate to parent
283        let mut current = self;
284        for part in parent_path {
285            match current {
286                Value::Table(table) => {
287                    current = table.get_mut(*part)
288                        .ok_or_else(|| Error::key_not_found(*part))?;
289                }
290                _ => return Err(Error::type_error(
291                    format!("Cannot navigate into {} when looking for key '{}'", current.type_name(), part),
292                    "table",
293                    current.type_name(),
294                )),
295            }
296        }
297
298        // Get the final value
299        match current {
300            Value::Table(table) => {
301                table.get_mut(*last_key)
302                    .ok_or_else(|| Error::key_not_found(*last_key))
303            }
304            _ => Err(Error::type_error(
305                format!("Cannot get key '{}' from {}", last_key, current.type_name()),
306                "table",
307                current.type_name(),
308            )),
309        }
310    }
311
312    /// Set a value by path, creating intermediate tables as needed (ZERO-COPY optimized)
313    pub fn set_nested(&mut self, path: &str, value: Value) -> Result<()> {
314        if path.is_empty() {
315            return Err(Error::key_not_found(""));
316        }
317
318        let parts: Vec<&str> = path.split('.').collect();
319        if parts.is_empty() {
320            return Err(Error::key_not_found(path));
321        }
322
323        let (last_key, parent_path) = parts.split_last()
324            .ok_or_else(|| Error::key_not_found(path))?;
325
326        // Navigate to parent, creating tables as needed
327        let mut current = self;
328        for part in parent_path {
329            if let Value::Table(table) = current {
330                // ZERO-COPY: Use entry API to avoid string allocation when possible
331                let entry = table.entry(part.to_string()).or_insert_with(|| {
332                    Value::table(BTreeMap::new())
333                });
334                current = entry;
335            } else {
336                return Err(Error::type_error(
337                    format!("Cannot navigate into {}", current.type_name()),
338                    "table",
339                    current.type_name(),
340                ));
341            }
342        }
343
344        // Set the final value
345        if let Value::Table(table) = current {
346            table.insert(last_key.to_string(), value);
347            Ok(())
348        } else {
349            Err(Error::type_error(
350                format!("Cannot set key in {}", current.type_name()),
351                "table",
352                current.type_name(),
353            ))
354        }
355    }
356
357    /// Remove a value by path (ENTERPRISE ERROR HANDLING)
358    pub fn remove(&mut self, path: &str) -> Result<Option<Value>> {
359        if path.is_empty() {
360            let old = std::mem::replace(self, Value::Null);
361            return Ok(Some(old));
362        }
363
364        let parts: Vec<&str> = path.split('.').collect();
365        if parts.is_empty() {
366            return Err(Error::key_not_found(path));
367        }
368
369        let (last_key, parent_path) = parts.split_last()
370            .ok_or_else(|| Error::key_not_found(path))?;
371
372        // Navigate to parent
373        let mut current = self;
374        for part in parent_path {
375            match current {
376                Value::Table(table) => {
377                    current = table.get_mut(*part)
378                        .ok_or_else(|| Error::key_not_found(*part))?;
379                }
380                _ => return Err(Error::type_error(
381                    format!("Cannot navigate into {} when removing key '{}'", current.type_name(), part),
382                    "table",
383                    current.type_name(),
384                )),
385            }
386        }
387
388        // Remove from parent
389        if let Value::Table(table) = current {
390            Ok(table.remove(*last_key))
391        } else {
392            Err(Error::type_error(
393                format!("Cannot remove key '{}' from {}", last_key, current.type_name()),
394                "table",
395                current.type_name(),
396            ))
397        }
398    }
399
400    /// Get all keys at the current level (for tables only) - ZERO-COPY optimized
401    pub fn keys(&self) -> Result<Vec<&str>> {
402        match self {
403            Value::Table(table) => Ok(table.keys().map(|k| k.as_str()).collect()),
404            _ => Err(Error::type_error(
405                "Cannot get keys from non-table value",
406                "table",
407                self.type_name(),
408            )),
409        }
410    }
411
412    /// Check if a path exists
413    pub fn contains_key(&self, path: &str) -> bool {
414        self.get(path).is_some()
415    }
416
417    /// Set a value by path (backward compatibility alias)
418    pub fn set(&mut self, path: &str, value: Value) -> Result<()> {
419        self.set_nested(path, value)
420    }
421
422    /// Get the length of arrays or tables
423    pub fn len(&self) -> usize {
424        match self {
425            Value::Array(arr) => arr.len(),
426            Value::Table(table) => table.len(),
427            Value::String(s) => s.len(),
428            _ => 0,
429        }
430    }
431
432    /// Check if arrays or tables are empty
433    pub fn is_empty(&self) -> bool {
434        self.len() == 0
435    }
436}
437
438impl fmt::Display for Value {
439    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
440        match self {
441            Value::Null => write!(f, "null"),
442            Value::Bool(b) => write!(f, "{}", b),
443            Value::Integer(i) => write!(f, "{}", i),
444            Value::Float(fl) => write!(f, "{}", fl),
445            Value::String(s) => write!(f, "{}", s),
446            Value::Array(arr) => {
447                write!(f, "[")?;
448                for (i, item) in arr.iter().enumerate() {
449                    if i > 0 {
450                        write!(f, ", ")?;
451                    }
452                    write!(f, "{}", item)?;
453                }
454                write!(f, "]")
455            }
456            Value::Table(table) => {
457                write!(f, "{{")?;
458                for (i, (key, value)) in table.iter().enumerate() {
459                    if i > 0 {
460                        write!(f, ", ")?;
461                    }
462                    write!(f, "{}: {}", key, value)?;
463                }
464                write!(f, "}}")
465            }
466        }
467    }
468}
469
470// ZERO-COPY conversion implementations
471impl From<bool> for Value {
472    fn from(value: bool) -> Self {
473        Value::Bool(value)
474    }
475}
476
477impl From<i32> for Value {
478    fn from(value: i32) -> Self {
479        Value::Integer(value as i64)
480    }
481}
482
483impl From<i64> for Value {
484    fn from(value: i64) -> Self {
485        Value::Integer(value)
486    }
487}
488
489impl From<f32> for Value {
490    fn from(value: f32) -> Self {
491        Value::Float(value as f64)
492    }
493}
494
495impl From<f64> for Value {
496    fn from(value: f64) -> Self {
497        Value::Float(value)
498    }
499}
500
501impl From<String> for Value {
502    fn from(value: String) -> Self {
503        Value::String(value)
504    }
505}
506
507impl From<&str> for Value {
508    fn from(value: &str) -> Self {
509        Value::String(value.to_string())
510    }
511}
512
513impl From<Vec<Value>> for Value {
514    fn from(value: Vec<Value>) -> Self {
515        Value::Array(value)
516    }
517}
518
519impl From<BTreeMap<String, Value>> for Value {
520    fn from(value: BTreeMap<String, Value>) -> Self {
521        Value::Table(value)
522    }
523}
524
525// ENTERPRISE: Helper functions for zero-copy operations
526impl Value {
527    /// Create a string value from a slice without unnecessary allocation
528    pub fn string_from_slice(value: &str) -> Self {
529        Value::String(value.to_string())
530    }
531
532    /// Get string slice without allocation - enterprise optimization
533    pub fn as_str(&self) -> Result<&str> {
534        match self {
535            Value::String(s) => Ok(s.as_str()),
536            _ => Err(Error::type_error(
537                "Value is not a string",
538                "string",
539                self.type_name(),
540            )),
541        }
542    }
543}
544
545#[cfg(test)]
546mod tests {
547    use super::*;
548
549    #[test]
550    fn test_value_creation() {
551        assert_eq!(Value::null(), Value::Null);
552        assert_eq!(Value::bool(true), Value::Bool(true));
553        assert_eq!(Value::integer(42), Value::Integer(42));
554        assert_eq!(Value::float(3.14), Value::Float(3.14));
555        assert_eq!(Value::string("test"), Value::String("test".to_string()));
556    }
557
558    #[test]
559    fn test_type_checking() {
560        let null = Value::null();
561        let bool_val = Value::bool(true);
562        let int_val = Value::integer(42);
563        let float_val = Value::float(3.14);
564        let string_val = Value::string("test");
565        let array_val = Value::array(vec![Value::integer(1), Value::integer(2)]);
566        let table_val = Value::table(BTreeMap::new());
567
568        assert!(null.is_null());
569        assert!(bool_val.is_bool());
570        assert!(int_val.is_integer());
571        assert!(float_val.is_float());
572        assert!(string_val.is_string());
573        assert!(array_val.is_array());
574        assert!(table_val.is_table());
575    }
576
577    #[test]
578    fn test_value_conversion() {
579        let bool_val = Value::bool(true);
580        let int_val = Value::integer(42);
581        let float_val = Value::float(3.14);
582        let string_val = Value::string("test");
583
584        assert_eq!(bool_val.as_bool().unwrap(), true);
585        assert_eq!(int_val.as_integer().unwrap(), 42);
586        assert_eq!(float_val.as_float().unwrap(), 3.14);
587        assert_eq!(string_val.as_string().unwrap(), "test");
588    }
589
590    #[test]
591    fn test_nested_access() {
592        let mut table = BTreeMap::new();
593        let mut inner_table = BTreeMap::new();
594        inner_table.insert("inner_key".to_string(), Value::string("inner_value"));
595        table.insert("outer_key".to_string(), Value::table(inner_table));
596        
597        let value = Value::table(table);
598        
599        assert_eq!(
600            value.get("outer_key.inner_key").unwrap().as_string().unwrap(),
601            "inner_value"
602        );
603    }
604
605    #[test] 
606    fn test_enterprise_error_handling() {
607        let mut value = Value::table(BTreeMap::new());
608        
609        // Test proper error handling instead of panics
610        assert!(value.get_mut_nested("nonexistent.key").is_err());
611        assert!(value.remove("nonexistent.key").is_err());
612        
613        // Test successful operations
614        assert!(value.set_nested("test.key", Value::string("value")).is_ok());
615        assert!(value.get("test.key").is_some());
616    }
617}