holoconf_core/
value.rs

1//! Configuration value types
2//!
3//! Represents parsed configuration values before resolution.
4//! Values can be scalars (string, int, float, bool, null),
5//! sequences (arrays), or mappings (objects).
6
7use indexmap::IndexMap;
8use serde::{Deserialize, Serialize};
9use std::fmt;
10
11use crate::error::{Error, Result};
12
13/// A configuration value that may contain unresolved interpolations
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15#[serde(untagged)]
16#[derive(Default)]
17pub enum Value {
18    /// Null value
19    #[default]
20    Null,
21    /// Boolean value
22    Bool(bool),
23    /// Integer value
24    Integer(i64),
25    /// Floating point value
26    Float(f64),
27    /// String value (may contain interpolations like ${env:VAR})
28    String(String),
29    /// Sequence of values
30    Sequence(Vec<Value>),
31    /// Mapping of string keys to values
32    Mapping(IndexMap<String, Value>),
33}
34
35impl Value {
36    /// Check if this value is null
37    pub fn is_null(&self) -> bool {
38        matches!(self, Value::Null)
39    }
40
41    /// Check if this value is a boolean
42    pub fn is_bool(&self) -> bool {
43        matches!(self, Value::Bool(_))
44    }
45
46    /// Check if this value is an integer
47    pub fn is_integer(&self) -> bool {
48        matches!(self, Value::Integer(_))
49    }
50
51    /// Check if this value is a float
52    pub fn is_float(&self) -> bool {
53        matches!(self, Value::Float(_))
54    }
55
56    /// Check if this value is a string
57    pub fn is_string(&self) -> bool {
58        matches!(self, Value::String(_))
59    }
60
61    /// Check if this value is a sequence
62    pub fn is_sequence(&self) -> bool {
63        matches!(self, Value::Sequence(_))
64    }
65
66    /// Check if this value is a mapping
67    pub fn is_mapping(&self) -> bool {
68        matches!(self, Value::Mapping(_))
69    }
70
71    /// Get as boolean if this is a Bool
72    pub fn as_bool(&self) -> Option<bool> {
73        match self {
74            Value::Bool(b) => Some(*b),
75            _ => None,
76        }
77    }
78
79    /// Get as i64 if this is an Integer
80    pub fn as_i64(&self) -> Option<i64> {
81        match self {
82            Value::Integer(i) => Some(*i),
83            _ => None,
84        }
85    }
86
87    /// Get as f64 if this is a Float or Integer
88    pub fn as_f64(&self) -> Option<f64> {
89        match self {
90            Value::Float(f) => Some(*f),
91            Value::Integer(i) => Some(*i as f64),
92            _ => None,
93        }
94    }
95
96    /// Get as str if this is a String
97    pub fn as_str(&self) -> Option<&str> {
98        match self {
99            Value::String(s) => Some(s),
100            _ => None,
101        }
102    }
103
104    /// Get as slice if this is a Sequence
105    pub fn as_sequence(&self) -> Option<&[Value]> {
106        match self {
107            Value::Sequence(s) => Some(s),
108            _ => None,
109        }
110    }
111
112    /// Get as mapping if this is a Mapping
113    pub fn as_mapping(&self) -> Option<&IndexMap<String, Value>> {
114        match self {
115            Value::Mapping(m) => Some(m),
116            _ => None,
117        }
118    }
119
120    /// Get a value by path (e.g., "database.host" or "servers[0].name")
121    pub fn get_path(&self, path: &str) -> Result<&Value> {
122        if path.is_empty() {
123            return Ok(self);
124        }
125
126        let segments = parse_path(path)?;
127        let mut current = self;
128
129        for segment in &segments {
130            current = match segment {
131                PathSegment::Key(key) => match current {
132                    Value::Mapping(map) => map
133                        .get(key.as_str())
134                        .ok_or_else(|| Error::path_not_found(path))?,
135                    _ => return Err(Error::path_not_found(path)),
136                },
137                PathSegment::Index(idx) => match current {
138                    Value::Sequence(seq) => {
139                        seq.get(*idx).ok_or_else(|| Error::path_not_found(path))?
140                    }
141                    _ => return Err(Error::path_not_found(path)),
142                },
143            };
144        }
145
146        Ok(current)
147    }
148
149    /// Get a mutable value by path
150    pub fn get_path_mut(&mut self, path: &str) -> Result<&mut Value> {
151        if path.is_empty() {
152            return Ok(self);
153        }
154
155        let segments = parse_path(path)?;
156        let mut current = self;
157
158        for segment in segments {
159            current = match segment {
160                PathSegment::Key(key) => match current {
161                    Value::Mapping(map) => map
162                        .get_mut(&key)
163                        .ok_or_else(|| Error::path_not_found(path))?,
164                    _ => return Err(Error::path_not_found(path)),
165                },
166                PathSegment::Index(idx) => match current {
167                    Value::Sequence(seq) => seq
168                        .get_mut(idx)
169                        .ok_or_else(|| Error::path_not_found(path))?,
170                    _ => return Err(Error::path_not_found(path)),
171                },
172            };
173        }
174
175        Ok(current)
176    }
177
178    /// Set a value at a path, creating intermediate mappings as needed
179    pub fn set_path(&mut self, path: &str, value: Value) -> Result<()> {
180        if path.is_empty() {
181            *self = value;
182            return Ok(());
183        }
184
185        let segments = parse_path(path)?;
186        let mut current = self;
187
188        for (i, segment) in segments.iter().enumerate() {
189            let is_last = i == segments.len() - 1;
190
191            if is_last {
192                match segment {
193                    PathSegment::Key(key) => {
194                        if let Value::Mapping(map) = current {
195                            map.insert(key.clone(), value);
196                            return Ok(());
197                        }
198                        return Err(Error::path_not_found(path));
199                    }
200                    PathSegment::Index(idx) => {
201                        if let Value::Sequence(seq) = current {
202                            if *idx < seq.len() {
203                                seq[*idx] = value;
204                                return Ok(());
205                            }
206                        }
207                        return Err(Error::path_not_found(path));
208                    }
209                }
210            }
211
212            // Navigate to next level, creating mappings if needed
213            current = match segment {
214                PathSegment::Key(key) => {
215                    if let Value::Mapping(map) = current {
216                        // Check what the next segment expects
217                        let next_is_index = segments
218                            .get(i + 1)
219                            .map(|s| matches!(s, PathSegment::Index(_)))
220                            .unwrap_or(false);
221
222                        if !map.contains_key(key) {
223                            let new_value = if next_is_index {
224                                Value::Sequence(vec![])
225                            } else {
226                                Value::Mapping(IndexMap::new())
227                            };
228                            map.insert(key.clone(), new_value);
229                        }
230                        map.get_mut(key).unwrap()
231                    } else {
232                        return Err(Error::path_not_found(path));
233                    }
234                }
235                PathSegment::Index(idx) => {
236                    if let Value::Sequence(seq) = current {
237                        seq.get_mut(*idx)
238                            .ok_or_else(|| Error::path_not_found(path))?
239                    } else {
240                        return Err(Error::path_not_found(path));
241                    }
242                }
243            };
244        }
245
246        Ok(())
247    }
248
249    /// Returns the type name of this value
250    pub fn type_name(&self) -> &'static str {
251        match self {
252            Value::Null => "null",
253            Value::Bool(_) => "boolean",
254            Value::Integer(_) => "integer",
255            Value::Float(_) => "float",
256            Value::String(_) => "string",
257            Value::Sequence(_) => "sequence",
258            Value::Mapping(_) => "mapping",
259        }
260    }
261
262    /// Merge another value into this one (deep merge per ADR-004)
263    ///
264    /// Merge semantics:
265    /// - Mappings: Deep merge recursively
266    /// - Scalars: `other` wins (last-writer-wins)
267    /// - Sequences: `other` replaces entirely
268    /// - Null in other: Removes the key (handled by caller)
269    /// - Type mismatch: `other` wins
270    pub fn merge(&mut self, other: Value) {
271        match (self, other) {
272            // Both are mappings: deep merge
273            (Value::Mapping(base), Value::Mapping(overlay)) => {
274                for (key, overlay_value) in overlay {
275                    if overlay_value.is_null() {
276                        // Null removes the key
277                        base.shift_remove(&key);
278                    } else if let Some(base_value) = base.get_mut(&key) {
279                        // Key exists in both: recursive merge
280                        base_value.merge(overlay_value);
281                    } else {
282                        // Key only in overlay: add it
283                        base.insert(key, overlay_value);
284                    }
285                }
286            }
287            // Any other combination: overlay wins (replacement)
288            (this, other) => {
289                *this = other;
290            }
291        }
292    }
293
294    /// Create a merged value from two values (non-mutating)
295    pub fn merged(mut self, other: Value) -> Value {
296        self.merge(other);
297        self
298    }
299}
300
301impl fmt::Display for Value {
302    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303        match self {
304            Value::Null => write!(f, "null"),
305            Value::Bool(b) => write!(f, "{}", b),
306            Value::Integer(i) => write!(f, "{}", i),
307            Value::Float(n) => write!(f, "{}", n),
308            Value::String(s) => write!(f, "{}", s),
309            Value::Sequence(seq) => {
310                write!(f, "[")?;
311                for (i, v) in seq.iter().enumerate() {
312                    if i > 0 {
313                        write!(f, ", ")?;
314                    }
315                    write!(f, "{}", v)?;
316                }
317                write!(f, "]")
318            }
319            Value::Mapping(map) => {
320                write!(f, "{{")?;
321                for (i, (k, v)) in map.iter().enumerate() {
322                    if i > 0 {
323                        write!(f, ", ")?;
324                    }
325                    write!(f, "{}: {}", k, v)?;
326                }
327                write!(f, "}}")
328            }
329        }
330    }
331}
332
333// Convenient From implementations
334impl From<bool> for Value {
335    fn from(b: bool) -> Self {
336        Value::Bool(b)
337    }
338}
339
340impl From<i64> for Value {
341    fn from(i: i64) -> Self {
342        Value::Integer(i)
343    }
344}
345
346impl From<i32> for Value {
347    fn from(i: i32) -> Self {
348        Value::Integer(i as i64)
349    }
350}
351
352impl From<f64> for Value {
353    fn from(f: f64) -> Self {
354        Value::Float(f)
355    }
356}
357
358impl From<String> for Value {
359    fn from(s: String) -> Self {
360        Value::String(s)
361    }
362}
363
364impl From<&str> for Value {
365    fn from(s: &str) -> Self {
366        Value::String(s.to_string())
367    }
368}
369
370impl<T: Into<Value>> From<Vec<T>> for Value {
371    fn from(v: Vec<T>) -> Self {
372        Value::Sequence(v.into_iter().map(Into::into).collect())
373    }
374}
375
376impl From<IndexMap<String, Value>> for Value {
377    fn from(m: IndexMap<String, Value>) -> Self {
378        Value::Mapping(m)
379    }
380}
381
382/// A segment in a path expression
383#[derive(Debug, Clone, PartialEq)]
384enum PathSegment {
385    /// A key in a mapping (e.g., "database" in "database.host")
386    Key(String),
387    /// An index in a sequence (e.g., 0 in "servers[0]")
388    Index(usize),
389}
390
391/// Parse a path string into segments
392/// Supports: "key", "key.subkey", "key[0]", "key[0].subkey"
393fn parse_path(path: &str) -> Result<Vec<PathSegment>> {
394    let mut segments = Vec::new();
395    let mut current_key = String::new();
396    let mut chars = path.chars().peekable();
397
398    while let Some(c) = chars.next() {
399        match c {
400            '.' => {
401                if !current_key.is_empty() {
402                    segments.push(PathSegment::Key(current_key.clone()));
403                    current_key.clear();
404                }
405            }
406            '[' => {
407                if !current_key.is_empty() {
408                    segments.push(PathSegment::Key(current_key.clone()));
409                    current_key.clear();
410                }
411                // Parse index
412                let mut index_str = String::new();
413                while let Some(&c) = chars.peek() {
414                    if c == ']' {
415                        chars.next();
416                        break;
417                    }
418                    index_str.push(chars.next().unwrap());
419                }
420                let idx: usize = index_str.parse().map_err(|_| {
421                    Error::parse(format!("Invalid array index in path: {}", index_str))
422                })?;
423                segments.push(PathSegment::Index(idx));
424            }
425            ']' => {
426                return Err(Error::parse("Unexpected ']' in path"));
427            }
428            _ => {
429                current_key.push(c);
430            }
431        }
432    }
433
434    if !current_key.is_empty() {
435        segments.push(PathSegment::Key(current_key));
436    }
437
438    Ok(segments)
439}
440
441#[cfg(test)]
442mod tests {
443    use super::*;
444
445    #[test]
446    fn test_parse_simple_path() {
447        let segments = parse_path("database").unwrap();
448        assert_eq!(segments, vec![PathSegment::Key("database".into())]);
449    }
450
451    #[test]
452    fn test_parse_dotted_path() {
453        let segments = parse_path("database.host").unwrap();
454        assert_eq!(
455            segments,
456            vec![
457                PathSegment::Key("database".into()),
458                PathSegment::Key("host".into())
459            ]
460        );
461    }
462
463    #[test]
464    fn test_parse_array_path() {
465        let segments = parse_path("servers[0]").unwrap();
466        assert_eq!(
467            segments,
468            vec![PathSegment::Key("servers".into()), PathSegment::Index(0)]
469        );
470    }
471
472    #[test]
473    fn test_parse_complex_path() {
474        let segments = parse_path("servers[0].host").unwrap();
475        assert_eq!(
476            segments,
477            vec![
478                PathSegment::Key("servers".into()),
479                PathSegment::Index(0),
480                PathSegment::Key("host".into())
481            ]
482        );
483    }
484
485    #[test]
486    fn test_value_get_path() {
487        let mut map = IndexMap::new();
488        let mut db = IndexMap::new();
489        db.insert("host".into(), Value::String("localhost".into()));
490        db.insert("port".into(), Value::Integer(5432));
491        map.insert("database".into(), Value::Mapping(db));
492
493        let value = Value::Mapping(map);
494
495        assert_eq!(
496            value.get_path("database.host").unwrap().as_str(),
497            Some("localhost")
498        );
499        assert_eq!(
500            value.get_path("database.port").unwrap().as_i64(),
501            Some(5432)
502        );
503    }
504
505    #[test]
506    fn test_value_get_path_array() {
507        let mut map = IndexMap::new();
508        map.insert(
509            "servers".into(),
510            Value::Sequence(vec![
511                Value::String("server1".into()),
512                Value::String("server2".into()),
513            ]),
514        );
515
516        let value = Value::Mapping(map);
517
518        assert_eq!(
519            value.get_path("servers[0]").unwrap().as_str(),
520            Some("server1")
521        );
522        assert_eq!(
523            value.get_path("servers[1]").unwrap().as_str(),
524            Some("server2")
525        );
526    }
527
528    #[test]
529    fn test_value_get_path_not_found() {
530        let map = IndexMap::new();
531        let value = Value::Mapping(map);
532
533        assert!(value.get_path("nonexistent").is_err());
534    }
535
536    #[test]
537    fn test_value_type_checks() {
538        assert!(Value::Null.is_null());
539        assert!(Value::Bool(true).is_bool());
540        assert!(Value::Integer(42).is_integer());
541        assert!(Value::Float(2.5).is_float());
542        assert!(Value::String("hello".into()).is_string());
543        assert!(Value::Sequence(vec![]).is_sequence());
544        assert!(Value::Mapping(IndexMap::new()).is_mapping());
545    }
546
547    #[test]
548    fn test_value_conversions() {
549        assert_eq!(Value::Bool(true).as_bool(), Some(true));
550        assert_eq!(Value::Integer(42).as_i64(), Some(42));
551        assert_eq!(Value::Float(2.5).as_f64(), Some(2.5));
552        assert_eq!(Value::Integer(42).as_f64(), Some(42.0));
553        assert_eq!(Value::String("hello".into()).as_str(), Some("hello"));
554    }
555
556    #[test]
557    fn test_merge_scalars() {
558        let mut base = Value::String("base".into());
559        base.merge(Value::String("overlay".into()));
560        assert_eq!(base.as_str(), Some("overlay"));
561    }
562
563    #[test]
564    fn test_merge_deep() {
565        // Create base: { database: { host: "localhost", port: 5432 } }
566        let mut db_base = IndexMap::new();
567        db_base.insert("host".into(), Value::String("localhost".into()));
568        db_base.insert("port".into(), Value::Integer(5432));
569        let mut base = IndexMap::new();
570        base.insert("database".into(), Value::Mapping(db_base));
571        let mut base = Value::Mapping(base);
572
573        // Create overlay: { database: { host: "prod-db" } }
574        let mut db_overlay = IndexMap::new();
575        db_overlay.insert("host".into(), Value::String("prod-db".into()));
576        let mut overlay = IndexMap::new();
577        overlay.insert("database".into(), Value::Mapping(db_overlay));
578        let overlay = Value::Mapping(overlay);
579
580        base.merge(overlay);
581
582        // Result should have both host (overwritten) and port (preserved)
583        assert_eq!(
584            base.get_path("database.host").unwrap().as_str(),
585            Some("prod-db")
586        );
587        assert_eq!(base.get_path("database.port").unwrap().as_i64(), Some(5432));
588    }
589
590    #[test]
591    fn test_merge_null_removes_key() {
592        // Create base: { feature: { enabled: true, config: "value" } }
593        let mut feature = IndexMap::new();
594        feature.insert("enabled".into(), Value::Bool(true));
595        feature.insert("config".into(), Value::String("value".into()));
596        let mut base = IndexMap::new();
597        base.insert("feature".into(), Value::Mapping(feature));
598        let mut base = Value::Mapping(base);
599
600        // Create overlay: { feature: { config: null } }
601        let mut feature_overlay = IndexMap::new();
602        feature_overlay.insert("config".into(), Value::Null);
603        let mut overlay = IndexMap::new();
604        overlay.insert("feature".into(), Value::Mapping(feature_overlay));
605        let overlay = Value::Mapping(overlay);
606
607        base.merge(overlay);
608
609        // config should be removed, enabled preserved
610        assert_eq!(
611            base.get_path("feature.enabled").unwrap().as_bool(),
612            Some(true)
613        );
614        assert!(base.get_path("feature.config").is_err());
615    }
616
617    #[test]
618    fn test_merge_array_replaces() {
619        // Create base: { servers: ["a", "b"] }
620        let mut base = IndexMap::new();
621        base.insert(
622            "servers".into(),
623            Value::Sequence(vec![Value::String("a".into()), Value::String("b".into())]),
624        );
625        let mut base = Value::Mapping(base);
626
627        // Create overlay: { servers: ["c"] }
628        let mut overlay = IndexMap::new();
629        overlay.insert(
630            "servers".into(),
631            Value::Sequence(vec![Value::String("c".into())]),
632        );
633        let overlay = Value::Mapping(overlay);
634
635        base.merge(overlay);
636
637        // Array should be replaced, not concatenated
638        let servers = base.get_path("servers").unwrap().as_sequence().unwrap();
639        assert_eq!(servers.len(), 1);
640        assert_eq!(servers[0].as_str(), Some("c"));
641    }
642
643    #[test]
644    fn test_merge_type_mismatch() {
645        // Create base: { database: { host: "localhost" } }
646        let mut db = IndexMap::new();
647        db.insert("host".into(), Value::String("localhost".into()));
648        let mut base = IndexMap::new();
649        base.insert("database".into(), Value::Mapping(db));
650        let mut base = Value::Mapping(base);
651
652        // Create overlay: { database: "connection-string" }
653        let mut overlay = IndexMap::new();
654        overlay.insert("database".into(), Value::String("connection-string".into()));
655        let overlay = Value::Mapping(overlay);
656
657        base.merge(overlay);
658
659        // Scalar should replace mapping
660        assert_eq!(
661            base.get_path("database").unwrap().as_str(),
662            Some("connection-string")
663        );
664    }
665
666    #[test]
667    fn test_merge_adds_new_keys() {
668        // Create base: { a: 1 }
669        let mut base = IndexMap::new();
670        base.insert("a".into(), Value::Integer(1));
671        let mut base = Value::Mapping(base);
672
673        // Create overlay: { b: 2 }
674        let mut overlay = IndexMap::new();
675        overlay.insert("b".into(), Value::Integer(2));
676        let overlay = Value::Mapping(overlay);
677
678        base.merge(overlay);
679
680        // Both keys should exist
681        assert_eq!(base.get_path("a").unwrap().as_i64(), Some(1));
682        assert_eq!(base.get_path("b").unwrap().as_i64(), Some(2));
683    }
684
685    // Additional tests for improved coverage
686
687    #[test]
688    fn test_from_i32() {
689        let v: Value = 42i32.into();
690        assert_eq!(v, Value::Integer(42));
691    }
692
693    #[test]
694    fn test_from_vec_values() {
695        let vec: Vec<Value> = vec![Value::Integer(1), Value::Integer(2)];
696        let v: Value = vec.into();
697        assert!(v.is_sequence());
698        assert_eq!(v.as_sequence().unwrap().len(), 2);
699    }
700
701    #[test]
702    fn test_from_vec_strings() {
703        let vec: Vec<&str> = vec!["a", "b", "c"];
704        let v: Value = vec.into();
705        assert!(v.is_sequence());
706        assert_eq!(v.as_sequence().unwrap().len(), 3);
707    }
708
709    #[test]
710    fn test_from_indexmap() {
711        let mut map = IndexMap::new();
712        map.insert("key".to_string(), Value::Integer(42));
713        let v: Value = map.into();
714        assert!(v.is_mapping());
715    }
716
717    #[test]
718    fn test_display_null() {
719        assert_eq!(format!("{}", Value::Null), "null");
720    }
721
722    #[test]
723    fn test_display_bool() {
724        assert_eq!(format!("{}", Value::Bool(true)), "true");
725        assert_eq!(format!("{}", Value::Bool(false)), "false");
726    }
727
728    #[test]
729    fn test_display_integer() {
730        assert_eq!(format!("{}", Value::Integer(42)), "42");
731        assert_eq!(format!("{}", Value::Integer(-123)), "-123");
732    }
733
734    #[test]
735    fn test_display_float() {
736        let display = format!("{}", Value::Float(1.5));
737        assert!(display.starts_with("1.5"));
738    }
739
740    #[test]
741    fn test_display_string() {
742        assert_eq!(format!("{}", Value::String("hello".into())), "hello");
743    }
744
745    #[test]
746    fn test_display_sequence() {
747        let seq = Value::Sequence(vec![
748            Value::Integer(1),
749            Value::Integer(2),
750            Value::Integer(3),
751        ]);
752        assert_eq!(format!("{}", seq), "[1, 2, 3]");
753    }
754
755    #[test]
756    fn test_display_empty_sequence() {
757        let seq = Value::Sequence(vec![]);
758        assert_eq!(format!("{}", seq), "[]");
759    }
760
761    #[test]
762    fn test_display_mapping() {
763        let mut map = IndexMap::new();
764        map.insert("a".to_string(), Value::Integer(1));
765        let mapping = Value::Mapping(map);
766        assert_eq!(format!("{}", mapping), "{a: 1}");
767    }
768
769    #[test]
770    fn test_display_empty_mapping() {
771        let mapping = Value::Mapping(IndexMap::new());
772        assert_eq!(format!("{}", mapping), "{}");
773    }
774
775    #[test]
776    fn test_as_str_non_string() {
777        assert!(Value::Integer(42).as_str().is_none());
778        assert!(Value::Bool(true).as_str().is_none());
779        assert!(Value::Null.as_str().is_none());
780    }
781
782    #[test]
783    fn test_as_bool_non_bool() {
784        assert!(Value::Integer(42).as_bool().is_none());
785        assert!(Value::String("true".into()).as_bool().is_none());
786    }
787
788    #[test]
789    fn test_as_i64_non_integer() {
790        assert!(Value::String("42".into()).as_i64().is_none());
791        assert!(Value::Float(42.0).as_i64().is_none());
792    }
793
794    #[test]
795    fn test_as_f64_non_numeric() {
796        assert!(Value::String("3.14".into()).as_f64().is_none());
797        assert!(Value::Bool(true).as_f64().is_none());
798    }
799
800    #[test]
801    fn test_as_sequence_non_sequence() {
802        assert!(Value::Integer(42).as_sequence().is_none());
803        assert!(Value::Mapping(IndexMap::new()).as_sequence().is_none());
804    }
805
806    #[test]
807    fn test_as_mapping_non_mapping() {
808        assert!(Value::Integer(42).as_mapping().is_none());
809        assert!(Value::Sequence(vec![]).as_mapping().is_none());
810    }
811
812    #[test]
813    fn test_type_name() {
814        assert_eq!(Value::Null.type_name(), "null");
815        assert_eq!(Value::Bool(true).type_name(), "boolean");
816        assert_eq!(Value::Integer(42).type_name(), "integer");
817        assert_eq!(Value::Float(1.23).type_name(), "float");
818        assert_eq!(Value::String("s".into()).type_name(), "string");
819        assert_eq!(Value::Sequence(vec![]).type_name(), "sequence");
820        assert_eq!(Value::Mapping(IndexMap::new()).type_name(), "mapping");
821    }
822
823    #[test]
824    fn test_default() {
825        let v: Value = Default::default();
826        assert!(v.is_null());
827    }
828
829    #[test]
830    fn test_get_path_empty() {
831        let v = Value::Integer(42);
832        assert_eq!(v.get_path("").unwrap(), &Value::Integer(42));
833    }
834
835    #[test]
836    fn test_get_path_on_non_mapping() {
837        let v = Value::Integer(42);
838        assert!(v.get_path("key").is_err());
839    }
840
841    #[test]
842    fn test_get_path_array_out_of_bounds() {
843        let mut map = IndexMap::new();
844        map.insert(
845            "items".to_string(),
846            Value::Sequence(vec![Value::Integer(1)]),
847        );
848        let v = Value::Mapping(map);
849        assert!(v.get_path("items[99]").is_err());
850    }
851
852    #[test]
853    fn test_get_path_array_on_non_sequence() {
854        let mut map = IndexMap::new();
855        map.insert("key".to_string(), Value::Integer(42));
856        let v = Value::Mapping(map);
857        assert!(v.get_path("key[0]").is_err());
858    }
859
860    #[test]
861    fn test_get_path_mut_empty() {
862        let mut v = Value::Integer(42);
863        let result = v.get_path_mut("").unwrap();
864        *result = Value::Integer(100);
865        assert_eq!(v, Value::Integer(100));
866    }
867
868    #[test]
869    fn test_get_path_mut_modify() {
870        let mut map = IndexMap::new();
871        map.insert("key".to_string(), Value::Integer(42));
872        let mut v = Value::Mapping(map);
873
874        *v.get_path_mut("key").unwrap() = Value::Integer(100);
875        assert_eq!(v.get_path("key").unwrap().as_i64(), Some(100));
876    }
877
878    #[test]
879    fn test_get_path_mut_not_found() {
880        let mut map = IndexMap::new();
881        map.insert("key".to_string(), Value::Integer(42));
882        let mut v = Value::Mapping(map);
883        assert!(v.get_path_mut("nonexistent").is_err());
884    }
885
886    #[test]
887    fn test_get_path_mut_on_non_mapping() {
888        let mut v = Value::Integer(42);
889        assert!(v.get_path_mut("key").is_err());
890    }
891
892    #[test]
893    fn test_get_path_mut_array() {
894        let mut map = IndexMap::new();
895        map.insert(
896            "items".to_string(),
897            Value::Sequence(vec![Value::Integer(1), Value::Integer(2)]),
898        );
899        let mut v = Value::Mapping(map);
900
901        *v.get_path_mut("items[1]").unwrap() = Value::Integer(99);
902        assert_eq!(v.get_path("items[1]").unwrap().as_i64(), Some(99));
903    }
904
905    #[test]
906    fn test_get_path_mut_array_out_of_bounds() {
907        let mut map = IndexMap::new();
908        map.insert(
909            "items".to_string(),
910            Value::Sequence(vec![Value::Integer(1)]),
911        );
912        let mut v = Value::Mapping(map);
913        assert!(v.get_path_mut("items[99]").is_err());
914    }
915
916    #[test]
917    fn test_get_path_mut_array_on_non_sequence() {
918        let mut map = IndexMap::new();
919        map.insert("key".to_string(), Value::Integer(42));
920        let mut v = Value::Mapping(map);
921        assert!(v.get_path_mut("key[0]").is_err());
922    }
923
924    #[test]
925    fn test_set_path_empty() {
926        let mut v = Value::Integer(42);
927        v.set_path("", Value::Integer(100)).unwrap();
928        assert_eq!(v, Value::Integer(100));
929    }
930
931    #[test]
932    fn test_set_path_simple() {
933        let mut map = IndexMap::new();
934        map.insert("key".to_string(), Value::Integer(42));
935        let mut v = Value::Mapping(map);
936
937        v.set_path("key", Value::Integer(100)).unwrap();
938        assert_eq!(v.get_path("key").unwrap().as_i64(), Some(100));
939    }
940
941    #[test]
942    fn test_set_path_new_key() {
943        let mut map = IndexMap::new();
944        map.insert("existing".to_string(), Value::Integer(1));
945        let mut v = Value::Mapping(map);
946
947        v.set_path("new_key", Value::Integer(42)).unwrap();
948        assert_eq!(v.get_path("new_key").unwrap().as_i64(), Some(42));
949    }
950
951    #[test]
952    fn test_set_path_creates_intermediate_mappings() {
953        let mut v = Value::Mapping(IndexMap::new());
954        v.set_path("a.b.c", Value::Integer(42)).unwrap();
955        assert_eq!(v.get_path("a.b.c").unwrap().as_i64(), Some(42));
956    }
957
958    #[test]
959    fn test_set_path_array_element() {
960        let mut map = IndexMap::new();
961        map.insert(
962            "items".to_string(),
963            Value::Sequence(vec![Value::Integer(1), Value::Integer(2)]),
964        );
965        let mut v = Value::Mapping(map);
966
967        v.set_path("items[0]", Value::Integer(99)).unwrap();
968        assert_eq!(v.get_path("items[0]").unwrap().as_i64(), Some(99));
969    }
970
971    #[test]
972    fn test_set_path_array_out_of_bounds() {
973        let mut map = IndexMap::new();
974        map.insert(
975            "items".to_string(),
976            Value::Sequence(vec![Value::Integer(1)]),
977        );
978        let mut v = Value::Mapping(map);
979        assert!(v.set_path("items[99]", Value::Integer(42)).is_err());
980    }
981
982    #[test]
983    fn test_set_path_on_non_mapping() {
984        let mut v = Value::Integer(42);
985        assert!(v.set_path("key", Value::Integer(1)).is_err());
986    }
987
988    #[test]
989    fn test_set_path_key_on_non_mapping_intermediate() {
990        let mut map = IndexMap::new();
991        map.insert("key".to_string(), Value::Integer(42));
992        let mut v = Value::Mapping(map);
993        // Trying to set "key.subkey" when "key" is an integer
994        assert!(v.set_path("key.subkey", Value::Integer(1)).is_err());
995    }
996
997    #[test]
998    fn test_set_path_index_on_non_sequence() {
999        let mut map = IndexMap::new();
1000        map.insert("key".to_string(), Value::Integer(42));
1001        let mut v = Value::Mapping(map);
1002        assert!(v.set_path("key[0]", Value::Integer(1)).is_err());
1003    }
1004
1005    #[test]
1006    fn test_set_path_intermediate_index_out_of_bounds() {
1007        let mut map = IndexMap::new();
1008        map.insert(
1009            "items".to_string(),
1010            Value::Sequence(vec![Value::Mapping(IndexMap::new())]),
1011        );
1012        let mut v = Value::Mapping(map);
1013        assert!(v.set_path("items[99].key", Value::Integer(1)).is_err());
1014    }
1015
1016    #[test]
1017    fn test_merged_non_mutating() {
1018        let mut base_map = IndexMap::new();
1019        base_map.insert("a".to_string(), Value::Integer(1));
1020        let base = Value::Mapping(base_map);
1021
1022        let mut overlay_map = IndexMap::new();
1023        overlay_map.insert("b".to_string(), Value::Integer(2));
1024        let overlay = Value::Mapping(overlay_map);
1025
1026        let result = base.merged(overlay);
1027
1028        assert_eq!(result.get_path("a").unwrap().as_i64(), Some(1));
1029        assert_eq!(result.get_path("b").unwrap().as_i64(), Some(2));
1030    }
1031
1032    #[test]
1033    fn test_parse_path_invalid_index() {
1034        assert!(parse_path("items[abc]").is_err());
1035    }
1036
1037    #[test]
1038    fn test_parse_path_unexpected_bracket() {
1039        assert!(parse_path("items]").is_err());
1040    }
1041
1042    #[test]
1043    fn test_is_type_negative_cases() {
1044        let integer = Value::Integer(42);
1045        assert!(!integer.is_null());
1046        assert!(!integer.is_bool());
1047        assert!(!integer.is_float());
1048        assert!(!integer.is_string());
1049        assert!(!integer.is_sequence());
1050        assert!(!integer.is_mapping());
1051    }
1052}