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, bytes),
5//! sequences (arrays), or mappings (objects).
6
7use base64::{engine::general_purpose::STANDARD, Engine as _};
8use indexmap::IndexMap;
9use serde::{Deserialize, Deserializer, Serialize, Serializer};
10use std::fmt;
11
12use crate::error::{Error, Result};
13
14/// A configuration value that may contain unresolved interpolations
15///
16/// The `Bytes` variant stores raw binary data and serializes to base64 in YAML/JSON.
17#[derive(Debug, Clone, PartialEq, Default)]
18pub enum Value {
19    /// Null value
20    #[default]
21    Null,
22    /// Boolean value
23    Bool(bool),
24    /// Integer value
25    Integer(i64),
26    /// Floating point value
27    Float(f64),
28    /// String value (may contain interpolations like ${env:VAR})
29    String(String),
30    /// Binary data (serializes to base64)
31    Bytes(Vec<u8>),
32    /// Sequence of values
33    Sequence(Vec<Value>),
34    /// Mapping of string keys to values
35    Mapping(IndexMap<String, Value>),
36}
37
38// Custom Serialize implementation to handle Bytes as base64
39impl Serialize for Value {
40    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
41    where
42        S: Serializer,
43    {
44        match self {
45            Value::Null => serializer.serialize_none(),
46            Value::Bool(b) => serializer.serialize_bool(*b),
47            Value::Integer(i) => serializer.serialize_i64(*i),
48            Value::Float(f) => serializer.serialize_f64(*f),
49            Value::String(s) => serializer.serialize_str(s),
50            Value::Bytes(bytes) => {
51                // Serialize bytes as base64-encoded string
52                let encoded = STANDARD.encode(bytes);
53                serializer.serialize_str(&encoded)
54            }
55            Value::Sequence(seq) => seq.serialize(serializer),
56            Value::Mapping(map) => map.serialize(serializer),
57        }
58    }
59}
60
61// Custom Deserialize implementation (Bytes won't come from YAML/JSON directly)
62impl<'de> Deserialize<'de> for Value {
63    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
64    where
65        D: Deserializer<'de>,
66    {
67        // Use an untagged enum approach for deserialization
68        // Bytes are never deserialized from YAML/JSON - they come from file resolver
69        #[derive(Deserialize)]
70        #[serde(untagged)]
71        enum ValueHelper {
72            Null,
73            Bool(bool),
74            Integer(i64),
75            Float(f64),
76            String(String),
77            Sequence(Vec<Value>),
78            Mapping(IndexMap<String, Value>),
79        }
80
81        match ValueHelper::deserialize(deserializer)? {
82            ValueHelper::Null => Ok(Value::Null),
83            ValueHelper::Bool(b) => Ok(Value::Bool(b)),
84            ValueHelper::Integer(i) => Ok(Value::Integer(i)),
85            ValueHelper::Float(f) => Ok(Value::Float(f)),
86            ValueHelper::String(s) => Ok(Value::String(s)),
87            ValueHelper::Sequence(seq) => Ok(Value::Sequence(seq)),
88            ValueHelper::Mapping(map) => Ok(Value::Mapping(map)),
89        }
90    }
91}
92
93impl Value {
94    /// Check if this value is null
95    pub fn is_null(&self) -> bool {
96        matches!(self, Value::Null)
97    }
98
99    /// Check if this value is a boolean
100    pub fn is_bool(&self) -> bool {
101        matches!(self, Value::Bool(_))
102    }
103
104    /// Check if this value is an integer
105    pub fn is_integer(&self) -> bool {
106        matches!(self, Value::Integer(_))
107    }
108
109    /// Check if this value is a float
110    pub fn is_float(&self) -> bool {
111        matches!(self, Value::Float(_))
112    }
113
114    /// Check if this value is a string
115    pub fn is_string(&self) -> bool {
116        matches!(self, Value::String(_))
117    }
118
119    /// Check if this value is a sequence
120    pub fn is_sequence(&self) -> bool {
121        matches!(self, Value::Sequence(_))
122    }
123
124    /// Check if this value is a mapping
125    pub fn is_mapping(&self) -> bool {
126        matches!(self, Value::Mapping(_))
127    }
128
129    /// Check if this value is bytes
130    pub fn is_bytes(&self) -> bool {
131        matches!(self, Value::Bytes(_))
132    }
133
134    /// Get as boolean if this is a Bool
135    pub fn as_bool(&self) -> Option<bool> {
136        match self {
137            Value::Bool(b) => Some(*b),
138            _ => None,
139        }
140    }
141
142    /// Get as i64 if this is an Integer
143    pub fn as_i64(&self) -> Option<i64> {
144        match self {
145            Value::Integer(i) => Some(*i),
146            _ => None,
147        }
148    }
149
150    /// Get as f64 if this is a Float or Integer
151    pub fn as_f64(&self) -> Option<f64> {
152        match self {
153            Value::Float(f) => Some(*f),
154            Value::Integer(i) => Some(*i as f64),
155            _ => None,
156        }
157    }
158
159    /// Get as str if this is a String
160    pub fn as_str(&self) -> Option<&str> {
161        match self {
162            Value::String(s) => Some(s),
163            _ => None,
164        }
165    }
166
167    /// Get as slice if this is a Sequence
168    pub fn as_sequence(&self) -> Option<&[Value]> {
169        match self {
170            Value::Sequence(s) => Some(s),
171            _ => None,
172        }
173    }
174
175    /// Get as mapping if this is a Mapping
176    pub fn as_mapping(&self) -> Option<&IndexMap<String, Value>> {
177        match self {
178            Value::Mapping(m) => Some(m),
179            _ => None,
180        }
181    }
182
183    /// Get as bytes if this is Bytes
184    pub fn as_bytes(&self) -> Option<&[u8]> {
185        match self {
186            Value::Bytes(b) => Some(b),
187            _ => None,
188        }
189    }
190
191    /// Get a value by path (e.g., "database.host" or "servers[0].name")
192    pub fn get_path(&self, path: &str) -> Result<&Value> {
193        if path.is_empty() {
194            return Ok(self);
195        }
196
197        let segments = parse_path(path)?;
198        let mut current = self;
199
200        for segment in &segments {
201            current = match segment {
202                PathSegment::Key(key) => match current {
203                    Value::Mapping(map) => map
204                        .get(key.as_str())
205                        .ok_or_else(|| Error::path_not_found(path))?,
206                    _ => return Err(Error::path_not_found(path)),
207                },
208                PathSegment::Index(idx) => match current {
209                    Value::Sequence(seq) => {
210                        seq.get(*idx).ok_or_else(|| Error::path_not_found(path))?
211                    }
212                    _ => return Err(Error::path_not_found(path)),
213                },
214            };
215        }
216
217        Ok(current)
218    }
219
220    /// Get a mutable value by path
221    pub fn get_path_mut(&mut self, path: &str) -> Result<&mut Value> {
222        if path.is_empty() {
223            return Ok(self);
224        }
225
226        let segments = parse_path(path)?;
227        let mut current = self;
228
229        for segment in segments {
230            current = match segment {
231                PathSegment::Key(key) => match current {
232                    Value::Mapping(map) => map
233                        .get_mut(&key)
234                        .ok_or_else(|| Error::path_not_found(path))?,
235                    _ => return Err(Error::path_not_found(path)),
236                },
237                PathSegment::Index(idx) => match current {
238                    Value::Sequence(seq) => seq
239                        .get_mut(idx)
240                        .ok_or_else(|| Error::path_not_found(path))?,
241                    _ => return Err(Error::path_not_found(path)),
242                },
243            };
244        }
245
246        Ok(current)
247    }
248
249    /// Set a value at a path, creating intermediate mappings as needed
250    pub fn set_path(&mut self, path: &str, value: Value) -> Result<()> {
251        if path.is_empty() {
252            *self = value;
253            return Ok(());
254        }
255
256        let segments = parse_path(path)?;
257        let mut current = self;
258
259        for (i, segment) in segments.iter().enumerate() {
260            let is_last = i == segments.len() - 1;
261
262            if is_last {
263                match segment {
264                    PathSegment::Key(key) => {
265                        if let Value::Mapping(map) = current {
266                            map.insert(key.clone(), value);
267                            return Ok(());
268                        }
269                        return Err(Error::path_not_found(path));
270                    }
271                    PathSegment::Index(idx) => {
272                        if let Value::Sequence(seq) = current {
273                            if *idx < seq.len() {
274                                seq[*idx] = value;
275                                return Ok(());
276                            }
277                        }
278                        return Err(Error::path_not_found(path));
279                    }
280                }
281            }
282
283            // Navigate to next level, creating mappings if needed
284            current = match segment {
285                PathSegment::Key(key) => {
286                    if let Value::Mapping(map) = current {
287                        // Check what the next segment expects
288                        let next_is_index = segments
289                            .get(i + 1)
290                            .map(|s| matches!(s, PathSegment::Index(_)))
291                            .unwrap_or(false);
292
293                        if !map.contains_key(key) {
294                            let new_value = if next_is_index {
295                                Value::Sequence(vec![])
296                            } else {
297                                Value::Mapping(IndexMap::new())
298                            };
299                            map.insert(key.clone(), new_value);
300                        }
301                        map.get_mut(key).unwrap()
302                    } else {
303                        return Err(Error::path_not_found(path));
304                    }
305                }
306                PathSegment::Index(idx) => {
307                    if let Value::Sequence(seq) = current {
308                        seq.get_mut(*idx)
309                            .ok_or_else(|| Error::path_not_found(path))?
310                    } else {
311                        return Err(Error::path_not_found(path));
312                    }
313                }
314            };
315        }
316
317        Ok(())
318    }
319
320    /// Returns the type name of this value
321    pub fn type_name(&self) -> &'static str {
322        match self {
323            Value::Null => "null",
324            Value::Bool(_) => "boolean",
325            Value::Integer(_) => "integer",
326            Value::Float(_) => "float",
327            Value::String(_) => "string",
328            Value::Bytes(_) => "bytes",
329            Value::Sequence(_) => "sequence",
330            Value::Mapping(_) => "mapping",
331        }
332    }
333
334    /// Merge another value into this one (deep merge per ADR-004)
335    ///
336    /// Merge semantics:
337    /// - Mappings: Deep merge recursively
338    /// - Scalars: `other` wins (last-writer-wins)
339    /// - Sequences: `other` replaces entirely
340    /// - Null in other: Removes the key (handled by caller)
341    /// - Type mismatch: `other` wins
342    pub fn merge(&mut self, other: Value) {
343        match (self, other) {
344            // Both are mappings: deep merge
345            (Value::Mapping(base), Value::Mapping(overlay)) => {
346                for (key, overlay_value) in overlay {
347                    if overlay_value.is_null() {
348                        // Null removes the key
349                        base.shift_remove(&key);
350                    } else if let Some(base_value) = base.get_mut(&key) {
351                        // Key exists in both: recursive merge
352                        base_value.merge(overlay_value);
353                    } else {
354                        // Key only in overlay: add it
355                        base.insert(key, overlay_value);
356                    }
357                }
358            }
359            // Any other combination: overlay wins (replacement)
360            (this, other) => {
361                *this = other;
362            }
363        }
364    }
365
366    /// Create a merged value from two values (non-mutating)
367    pub fn merged(mut self, other: Value) -> Value {
368        self.merge(other);
369        self
370    }
371
372    /// Merge another value into this one while tracking source files
373    ///
374    /// Like `merge()`, but also records which file each leaf value came from.
375    /// The `sources` map is updated to reflect the final source of each path.
376    pub fn merge_tracking_sources(
377        &mut self,
378        other: Value,
379        source: &str,
380        path_prefix: &str,
381        sources: &mut std::collections::HashMap<String, String>,
382    ) {
383        match (self, other) {
384            // Both are mappings: deep merge
385            (Value::Mapping(base), Value::Mapping(overlay)) => {
386                for (key, overlay_value) in overlay {
387                    let full_path = if path_prefix.is_empty() {
388                        key.clone()
389                    } else {
390                        format!("{}.{}", path_prefix, key)
391                    };
392
393                    if overlay_value.is_null() {
394                        // Null removes the key - remove from sources too
395                        base.shift_remove(&key);
396                        remove_sources_with_prefix(sources, &full_path);
397                    } else if let Some(base_value) = base.get_mut(&key) {
398                        // Key exists in both: recursive merge
399                        base_value.merge_tracking_sources(
400                            overlay_value,
401                            source,
402                            &full_path,
403                            sources,
404                        );
405                    } else {
406                        // Key only in overlay: add it and record all its leaf paths
407                        collect_leaf_paths(&overlay_value, &full_path, source, sources);
408                        base.insert(key, overlay_value);
409                    }
410                }
411            }
412            // Any other combination: overlay wins (replacement)
413            (this, other) => {
414                // Remove old sources for this path prefix
415                remove_sources_with_prefix(sources, path_prefix);
416                // Record new leaf paths from the replacement value
417                collect_leaf_paths(&other, path_prefix, source, sources);
418                *this = other;
419            }
420        }
421    }
422
423    /// Collect all leaf paths from this value and record their source
424    pub fn collect_leaf_paths(
425        &self,
426        path_prefix: &str,
427        source: &str,
428        sources: &mut std::collections::HashMap<String, String>,
429    ) {
430        collect_leaf_paths(self, path_prefix, source, sources);
431    }
432}
433
434/// Collect all leaf paths from a value and record their source file
435fn collect_leaf_paths(
436    value: &Value,
437    path_prefix: &str,
438    source: &str,
439    sources: &mut std::collections::HashMap<String, String>,
440) {
441    match value {
442        Value::Mapping(map) => {
443            for (key, val) in map {
444                let full_path = if path_prefix.is_empty() {
445                    key.clone()
446                } else {
447                    format!("{}.{}", path_prefix, key)
448                };
449                collect_leaf_paths(val, &full_path, source, sources);
450            }
451        }
452        Value::Sequence(seq) => {
453            for (i, val) in seq.iter().enumerate() {
454                let full_path = format!("{}[{}]", path_prefix, i);
455                collect_leaf_paths(val, &full_path, source, sources);
456            }
457        }
458        // Leaf values: record the source
459        _ => {
460            if !path_prefix.is_empty() {
461                sources.insert(path_prefix.to_string(), source.to_string());
462            }
463        }
464    }
465}
466
467/// Remove all sources that start with the given prefix
468fn remove_sources_with_prefix(
469    sources: &mut std::collections::HashMap<String, String>,
470    prefix: &str,
471) {
472    if prefix.is_empty() {
473        sources.clear();
474        return;
475    }
476    // Remove exact match and any children (prefix. or prefix[)
477    sources.retain(|path, _| {
478        path != prefix
479            && !path.starts_with(&format!("{}.", prefix))
480            && !path.starts_with(&format!("{}[", prefix))
481    });
482}
483
484impl fmt::Display for Value {
485    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
486        match self {
487            Value::Null => write!(f, "null"),
488            Value::Bool(b) => write!(f, "{}", b),
489            Value::Integer(i) => write!(f, "{}", i),
490            Value::Float(n) => write!(f, "{}", n),
491            Value::String(s) => write!(f, "{}", s),
492            Value::Bytes(bytes) => write!(f, "<bytes: {} bytes>", bytes.len()),
493            Value::Sequence(seq) => {
494                write!(f, "[")?;
495                for (i, v) in seq.iter().enumerate() {
496                    if i > 0 {
497                        write!(f, ", ")?;
498                    }
499                    write!(f, "{}", v)?;
500                }
501                write!(f, "]")
502            }
503            Value::Mapping(map) => {
504                write!(f, "{{")?;
505                for (i, (k, v)) in map.iter().enumerate() {
506                    if i > 0 {
507                        write!(f, ", ")?;
508                    }
509                    write!(f, "{}: {}", k, v)?;
510                }
511                write!(f, "}}")
512            }
513        }
514    }
515}
516
517// Convenient From implementations
518impl From<bool> for Value {
519    fn from(b: bool) -> Self {
520        Value::Bool(b)
521    }
522}
523
524impl From<i64> for Value {
525    fn from(i: i64) -> Self {
526        Value::Integer(i)
527    }
528}
529
530impl From<i32> for Value {
531    fn from(i: i32) -> Self {
532        Value::Integer(i as i64)
533    }
534}
535
536impl From<f64> for Value {
537    fn from(f: f64) -> Self {
538        Value::Float(f)
539    }
540}
541
542impl From<String> for Value {
543    fn from(s: String) -> Self {
544        Value::String(s)
545    }
546}
547
548impl From<&str> for Value {
549    fn from(s: &str) -> Self {
550        Value::String(s.to_string())
551    }
552}
553
554impl<T: Into<Value>> From<Vec<T>> for Value {
555    fn from(v: Vec<T>) -> Self {
556        Value::Sequence(v.into_iter().map(Into::into).collect())
557    }
558}
559
560impl From<IndexMap<String, Value>> for Value {
561    fn from(m: IndexMap<String, Value>) -> Self {
562        Value::Mapping(m)
563    }
564}
565
566impl From<Vec<u8>> for Value {
567    fn from(bytes: Vec<u8>) -> Self {
568        Value::Bytes(bytes)
569    }
570}
571
572/// A segment in a path expression
573#[derive(Debug, Clone, PartialEq)]
574enum PathSegment {
575    /// A key in a mapping (e.g., "database" in "database.host")
576    Key(String),
577    /// An index in a sequence (e.g., 0 in "servers[0]")
578    Index(usize),
579}
580
581/// Parse a path string into segments
582/// Supports: "key", "key.subkey", "key[0]", "key[0].subkey"
583fn parse_path(path: &str) -> Result<Vec<PathSegment>> {
584    let mut segments = Vec::new();
585    let mut current_key = String::new();
586    let mut chars = path.chars().peekable();
587
588    while let Some(c) = chars.next() {
589        match c {
590            '.' => {
591                if !current_key.is_empty() {
592                    segments.push(PathSegment::Key(current_key.clone()));
593                    current_key.clear();
594                }
595            }
596            '[' => {
597                if !current_key.is_empty() {
598                    segments.push(PathSegment::Key(current_key.clone()));
599                    current_key.clear();
600                }
601                // Parse index
602                let mut index_str = String::new();
603                while let Some(&c) = chars.peek() {
604                    if c == ']' {
605                        chars.next();
606                        break;
607                    }
608                    index_str.push(chars.next().unwrap());
609                }
610                let idx: usize = index_str.parse().map_err(|_| {
611                    Error::parse(format!("Invalid array index in path: {}", index_str))
612                })?;
613                segments.push(PathSegment::Index(idx));
614            }
615            ']' => {
616                return Err(Error::parse("Unexpected ']' in path"));
617            }
618            _ => {
619                current_key.push(c);
620            }
621        }
622    }
623
624    if !current_key.is_empty() {
625        segments.push(PathSegment::Key(current_key));
626    }
627
628    Ok(segments)
629}
630
631#[cfg(test)]
632mod tests {
633    use super::*;
634
635    #[test]
636    fn test_parse_simple_path() {
637        let segments = parse_path("database").unwrap();
638        assert_eq!(segments, vec![PathSegment::Key("database".into())]);
639    }
640
641    #[test]
642    fn test_parse_dotted_path() {
643        let segments = parse_path("database.host").unwrap();
644        assert_eq!(
645            segments,
646            vec![
647                PathSegment::Key("database".into()),
648                PathSegment::Key("host".into())
649            ]
650        );
651    }
652
653    #[test]
654    fn test_parse_array_path() {
655        let segments = parse_path("servers[0]").unwrap();
656        assert_eq!(
657            segments,
658            vec![PathSegment::Key("servers".into()), PathSegment::Index(0)]
659        );
660    }
661
662    #[test]
663    fn test_parse_complex_path() {
664        let segments = parse_path("servers[0].host").unwrap();
665        assert_eq!(
666            segments,
667            vec![
668                PathSegment::Key("servers".into()),
669                PathSegment::Index(0),
670                PathSegment::Key("host".into())
671            ]
672        );
673    }
674
675    #[test]
676    fn test_value_get_path() {
677        let mut map = IndexMap::new();
678        let mut db = IndexMap::new();
679        db.insert("host".into(), Value::String("localhost".into()));
680        db.insert("port".into(), Value::Integer(5432));
681        map.insert("database".into(), Value::Mapping(db));
682
683        let value = Value::Mapping(map);
684
685        assert_eq!(
686            value.get_path("database.host").unwrap().as_str(),
687            Some("localhost")
688        );
689        assert_eq!(
690            value.get_path("database.port").unwrap().as_i64(),
691            Some(5432)
692        );
693    }
694
695    #[test]
696    fn test_value_get_path_array() {
697        let mut map = IndexMap::new();
698        map.insert(
699            "servers".into(),
700            Value::Sequence(vec![
701                Value::String("server1".into()),
702                Value::String("server2".into()),
703            ]),
704        );
705
706        let value = Value::Mapping(map);
707
708        assert_eq!(
709            value.get_path("servers[0]").unwrap().as_str(),
710            Some("server1")
711        );
712        assert_eq!(
713            value.get_path("servers[1]").unwrap().as_str(),
714            Some("server2")
715        );
716    }
717
718    #[test]
719    fn test_value_get_path_not_found() {
720        let map = IndexMap::new();
721        let value = Value::Mapping(map);
722
723        assert!(value.get_path("nonexistent").is_err());
724    }
725
726    #[test]
727    fn test_value_type_checks() {
728        assert!(Value::Null.is_null());
729        assert!(Value::Bool(true).is_bool());
730        assert!(Value::Integer(42).is_integer());
731        assert!(Value::Float(2.5).is_float());
732        assert!(Value::String("hello".into()).is_string());
733        assert!(Value::Sequence(vec![]).is_sequence());
734        assert!(Value::Mapping(IndexMap::new()).is_mapping());
735    }
736
737    #[test]
738    fn test_value_conversions() {
739        assert_eq!(Value::Bool(true).as_bool(), Some(true));
740        assert_eq!(Value::Integer(42).as_i64(), Some(42));
741        assert_eq!(Value::Float(2.5).as_f64(), Some(2.5));
742        assert_eq!(Value::Integer(42).as_f64(), Some(42.0));
743        assert_eq!(Value::String("hello".into()).as_str(), Some("hello"));
744    }
745
746    #[test]
747    fn test_merge_scalars() {
748        let mut base = Value::String("base".into());
749        base.merge(Value::String("overlay".into()));
750        assert_eq!(base.as_str(), Some("overlay"));
751    }
752
753    #[test]
754    fn test_merge_deep() {
755        // Create base: { database: { host: "localhost", port: 5432 } }
756        let mut db_base = IndexMap::new();
757        db_base.insert("host".into(), Value::String("localhost".into()));
758        db_base.insert("port".into(), Value::Integer(5432));
759        let mut base = IndexMap::new();
760        base.insert("database".into(), Value::Mapping(db_base));
761        let mut base = Value::Mapping(base);
762
763        // Create overlay: { database: { host: "prod-db" } }
764        let mut db_overlay = IndexMap::new();
765        db_overlay.insert("host".into(), Value::String("prod-db".into()));
766        let mut overlay = IndexMap::new();
767        overlay.insert("database".into(), Value::Mapping(db_overlay));
768        let overlay = Value::Mapping(overlay);
769
770        base.merge(overlay);
771
772        // Result should have both host (overwritten) and port (preserved)
773        assert_eq!(
774            base.get_path("database.host").unwrap().as_str(),
775            Some("prod-db")
776        );
777        assert_eq!(base.get_path("database.port").unwrap().as_i64(), Some(5432));
778    }
779
780    #[test]
781    fn test_merge_null_removes_key() {
782        // Create base: { feature: { enabled: true, config: "value" } }
783        let mut feature = IndexMap::new();
784        feature.insert("enabled".into(), Value::Bool(true));
785        feature.insert("config".into(), Value::String("value".into()));
786        let mut base = IndexMap::new();
787        base.insert("feature".into(), Value::Mapping(feature));
788        let mut base = Value::Mapping(base);
789
790        // Create overlay: { feature: { config: null } }
791        let mut feature_overlay = IndexMap::new();
792        feature_overlay.insert("config".into(), Value::Null);
793        let mut overlay = IndexMap::new();
794        overlay.insert("feature".into(), Value::Mapping(feature_overlay));
795        let overlay = Value::Mapping(overlay);
796
797        base.merge(overlay);
798
799        // config should be removed, enabled preserved
800        assert_eq!(
801            base.get_path("feature.enabled").unwrap().as_bool(),
802            Some(true)
803        );
804        assert!(base.get_path("feature.config").is_err());
805    }
806
807    #[test]
808    fn test_merge_array_replaces() {
809        // Create base: { servers: ["a", "b"] }
810        let mut base = IndexMap::new();
811        base.insert(
812            "servers".into(),
813            Value::Sequence(vec![Value::String("a".into()), Value::String("b".into())]),
814        );
815        let mut base = Value::Mapping(base);
816
817        // Create overlay: { servers: ["c"] }
818        let mut overlay = IndexMap::new();
819        overlay.insert(
820            "servers".into(),
821            Value::Sequence(vec![Value::String("c".into())]),
822        );
823        let overlay = Value::Mapping(overlay);
824
825        base.merge(overlay);
826
827        // Array should be replaced, not concatenated
828        let servers = base.get_path("servers").unwrap().as_sequence().unwrap();
829        assert_eq!(servers.len(), 1);
830        assert_eq!(servers[0].as_str(), Some("c"));
831    }
832
833    #[test]
834    fn test_merge_type_mismatch() {
835        // Create base: { database: { host: "localhost" } }
836        let mut db = IndexMap::new();
837        db.insert("host".into(), Value::String("localhost".into()));
838        let mut base = IndexMap::new();
839        base.insert("database".into(), Value::Mapping(db));
840        let mut base = Value::Mapping(base);
841
842        // Create overlay: { database: "connection-string" }
843        let mut overlay = IndexMap::new();
844        overlay.insert("database".into(), Value::String("connection-string".into()));
845        let overlay = Value::Mapping(overlay);
846
847        base.merge(overlay);
848
849        // Scalar should replace mapping
850        assert_eq!(
851            base.get_path("database").unwrap().as_str(),
852            Some("connection-string")
853        );
854    }
855
856    #[test]
857    fn test_merge_adds_new_keys() {
858        // Create base: { a: 1 }
859        let mut base = IndexMap::new();
860        base.insert("a".into(), Value::Integer(1));
861        let mut base = Value::Mapping(base);
862
863        // Create overlay: { b: 2 }
864        let mut overlay = IndexMap::new();
865        overlay.insert("b".into(), Value::Integer(2));
866        let overlay = Value::Mapping(overlay);
867
868        base.merge(overlay);
869
870        // Both keys should exist
871        assert_eq!(base.get_path("a").unwrap().as_i64(), Some(1));
872        assert_eq!(base.get_path("b").unwrap().as_i64(), Some(2));
873    }
874
875    // Additional tests for improved coverage
876
877    #[test]
878    fn test_from_i32() {
879        let v: Value = 42i32.into();
880        assert_eq!(v, Value::Integer(42));
881    }
882
883    #[test]
884    fn test_from_vec_values() {
885        let vec: Vec<Value> = vec![Value::Integer(1), Value::Integer(2)];
886        let v: Value = vec.into();
887        assert!(v.is_sequence());
888        assert_eq!(v.as_sequence().unwrap().len(), 2);
889    }
890
891    #[test]
892    fn test_from_vec_strings() {
893        let vec: Vec<&str> = vec!["a", "b", "c"];
894        let v: Value = vec.into();
895        assert!(v.is_sequence());
896        assert_eq!(v.as_sequence().unwrap().len(), 3);
897    }
898
899    #[test]
900    fn test_from_indexmap() {
901        let mut map = IndexMap::new();
902        map.insert("key".to_string(), Value::Integer(42));
903        let v: Value = map.into();
904        assert!(v.is_mapping());
905    }
906
907    #[test]
908    fn test_display_null() {
909        assert_eq!(format!("{}", Value::Null), "null");
910    }
911
912    #[test]
913    fn test_display_bool() {
914        assert_eq!(format!("{}", Value::Bool(true)), "true");
915        assert_eq!(format!("{}", Value::Bool(false)), "false");
916    }
917
918    #[test]
919    fn test_display_integer() {
920        assert_eq!(format!("{}", Value::Integer(42)), "42");
921        assert_eq!(format!("{}", Value::Integer(-123)), "-123");
922    }
923
924    #[test]
925    fn test_display_float() {
926        let display = format!("{}", Value::Float(1.5));
927        assert!(display.starts_with("1.5"));
928    }
929
930    #[test]
931    fn test_display_string() {
932        assert_eq!(format!("{}", Value::String("hello".into())), "hello");
933    }
934
935    #[test]
936    fn test_display_sequence() {
937        let seq = Value::Sequence(vec![
938            Value::Integer(1),
939            Value::Integer(2),
940            Value::Integer(3),
941        ]);
942        assert_eq!(format!("{}", seq), "[1, 2, 3]");
943    }
944
945    #[test]
946    fn test_display_empty_sequence() {
947        let seq = Value::Sequence(vec![]);
948        assert_eq!(format!("{}", seq), "[]");
949    }
950
951    #[test]
952    fn test_display_mapping() {
953        let mut map = IndexMap::new();
954        map.insert("a".to_string(), Value::Integer(1));
955        let mapping = Value::Mapping(map);
956        assert_eq!(format!("{}", mapping), "{a: 1}");
957    }
958
959    #[test]
960    fn test_display_empty_mapping() {
961        let mapping = Value::Mapping(IndexMap::new());
962        assert_eq!(format!("{}", mapping), "{}");
963    }
964
965    #[test]
966    fn test_as_str_non_string() {
967        assert!(Value::Integer(42).as_str().is_none());
968        assert!(Value::Bool(true).as_str().is_none());
969        assert!(Value::Null.as_str().is_none());
970    }
971
972    #[test]
973    fn test_as_bool_non_bool() {
974        assert!(Value::Integer(42).as_bool().is_none());
975        assert!(Value::String("true".into()).as_bool().is_none());
976    }
977
978    #[test]
979    fn test_as_i64_non_integer() {
980        assert!(Value::String("42".into()).as_i64().is_none());
981        assert!(Value::Float(42.0).as_i64().is_none());
982    }
983
984    #[test]
985    fn test_as_f64_non_numeric() {
986        assert!(Value::String("3.14".into()).as_f64().is_none());
987        assert!(Value::Bool(true).as_f64().is_none());
988    }
989
990    #[test]
991    fn test_as_sequence_non_sequence() {
992        assert!(Value::Integer(42).as_sequence().is_none());
993        assert!(Value::Mapping(IndexMap::new()).as_sequence().is_none());
994    }
995
996    #[test]
997    fn test_as_mapping_non_mapping() {
998        assert!(Value::Integer(42).as_mapping().is_none());
999        assert!(Value::Sequence(vec![]).as_mapping().is_none());
1000    }
1001
1002    #[test]
1003    fn test_type_name() {
1004        assert_eq!(Value::Null.type_name(), "null");
1005        assert_eq!(Value::Bool(true).type_name(), "boolean");
1006        assert_eq!(Value::Integer(42).type_name(), "integer");
1007        assert_eq!(Value::Float(1.23).type_name(), "float");
1008        assert_eq!(Value::String("s".into()).type_name(), "string");
1009        assert_eq!(Value::Sequence(vec![]).type_name(), "sequence");
1010        assert_eq!(Value::Mapping(IndexMap::new()).type_name(), "mapping");
1011    }
1012
1013    #[test]
1014    fn test_default() {
1015        let v: Value = Default::default();
1016        assert!(v.is_null());
1017    }
1018
1019    #[test]
1020    fn test_get_path_empty() {
1021        let v = Value::Integer(42);
1022        assert_eq!(v.get_path("").unwrap(), &Value::Integer(42));
1023    }
1024
1025    #[test]
1026    fn test_get_path_on_non_mapping() {
1027        let v = Value::Integer(42);
1028        assert!(v.get_path("key").is_err());
1029    }
1030
1031    #[test]
1032    fn test_get_path_array_out_of_bounds() {
1033        let mut map = IndexMap::new();
1034        map.insert(
1035            "items".to_string(),
1036            Value::Sequence(vec![Value::Integer(1)]),
1037        );
1038        let v = Value::Mapping(map);
1039        assert!(v.get_path("items[99]").is_err());
1040    }
1041
1042    #[test]
1043    fn test_get_path_array_on_non_sequence() {
1044        let mut map = IndexMap::new();
1045        map.insert("key".to_string(), Value::Integer(42));
1046        let v = Value::Mapping(map);
1047        assert!(v.get_path("key[0]").is_err());
1048    }
1049
1050    #[test]
1051    fn test_get_path_mut_empty() {
1052        let mut v = Value::Integer(42);
1053        let result = v.get_path_mut("").unwrap();
1054        *result = Value::Integer(100);
1055        assert_eq!(v, Value::Integer(100));
1056    }
1057
1058    #[test]
1059    fn test_get_path_mut_modify() {
1060        let mut map = IndexMap::new();
1061        map.insert("key".to_string(), Value::Integer(42));
1062        let mut v = Value::Mapping(map);
1063
1064        *v.get_path_mut("key").unwrap() = Value::Integer(100);
1065        assert_eq!(v.get_path("key").unwrap().as_i64(), Some(100));
1066    }
1067
1068    #[test]
1069    fn test_get_path_mut_not_found() {
1070        let mut map = IndexMap::new();
1071        map.insert("key".to_string(), Value::Integer(42));
1072        let mut v = Value::Mapping(map);
1073        assert!(v.get_path_mut("nonexistent").is_err());
1074    }
1075
1076    #[test]
1077    fn test_get_path_mut_on_non_mapping() {
1078        let mut v = Value::Integer(42);
1079        assert!(v.get_path_mut("key").is_err());
1080    }
1081
1082    #[test]
1083    fn test_get_path_mut_array() {
1084        let mut map = IndexMap::new();
1085        map.insert(
1086            "items".to_string(),
1087            Value::Sequence(vec![Value::Integer(1), Value::Integer(2)]),
1088        );
1089        let mut v = Value::Mapping(map);
1090
1091        *v.get_path_mut("items[1]").unwrap() = Value::Integer(99);
1092        assert_eq!(v.get_path("items[1]").unwrap().as_i64(), Some(99));
1093    }
1094
1095    #[test]
1096    fn test_get_path_mut_array_out_of_bounds() {
1097        let mut map = IndexMap::new();
1098        map.insert(
1099            "items".to_string(),
1100            Value::Sequence(vec![Value::Integer(1)]),
1101        );
1102        let mut v = Value::Mapping(map);
1103        assert!(v.get_path_mut("items[99]").is_err());
1104    }
1105
1106    #[test]
1107    fn test_get_path_mut_array_on_non_sequence() {
1108        let mut map = IndexMap::new();
1109        map.insert("key".to_string(), Value::Integer(42));
1110        let mut v = Value::Mapping(map);
1111        assert!(v.get_path_mut("key[0]").is_err());
1112    }
1113
1114    #[test]
1115    fn test_set_path_empty() {
1116        let mut v = Value::Integer(42);
1117        v.set_path("", Value::Integer(100)).unwrap();
1118        assert_eq!(v, Value::Integer(100));
1119    }
1120
1121    #[test]
1122    fn test_set_path_simple() {
1123        let mut map = IndexMap::new();
1124        map.insert("key".to_string(), Value::Integer(42));
1125        let mut v = Value::Mapping(map);
1126
1127        v.set_path("key", Value::Integer(100)).unwrap();
1128        assert_eq!(v.get_path("key").unwrap().as_i64(), Some(100));
1129    }
1130
1131    #[test]
1132    fn test_set_path_new_key() {
1133        let mut map = IndexMap::new();
1134        map.insert("existing".to_string(), Value::Integer(1));
1135        let mut v = Value::Mapping(map);
1136
1137        v.set_path("new_key", Value::Integer(42)).unwrap();
1138        assert_eq!(v.get_path("new_key").unwrap().as_i64(), Some(42));
1139    }
1140
1141    #[test]
1142    fn test_set_path_creates_intermediate_mappings() {
1143        let mut v = Value::Mapping(IndexMap::new());
1144        v.set_path("a.b.c", Value::Integer(42)).unwrap();
1145        assert_eq!(v.get_path("a.b.c").unwrap().as_i64(), Some(42));
1146    }
1147
1148    #[test]
1149    fn test_set_path_array_element() {
1150        let mut map = IndexMap::new();
1151        map.insert(
1152            "items".to_string(),
1153            Value::Sequence(vec![Value::Integer(1), Value::Integer(2)]),
1154        );
1155        let mut v = Value::Mapping(map);
1156
1157        v.set_path("items[0]", Value::Integer(99)).unwrap();
1158        assert_eq!(v.get_path("items[0]").unwrap().as_i64(), Some(99));
1159    }
1160
1161    #[test]
1162    fn test_set_path_array_out_of_bounds() {
1163        let mut map = IndexMap::new();
1164        map.insert(
1165            "items".to_string(),
1166            Value::Sequence(vec![Value::Integer(1)]),
1167        );
1168        let mut v = Value::Mapping(map);
1169        assert!(v.set_path("items[99]", Value::Integer(42)).is_err());
1170    }
1171
1172    #[test]
1173    fn test_set_path_on_non_mapping() {
1174        let mut v = Value::Integer(42);
1175        assert!(v.set_path("key", Value::Integer(1)).is_err());
1176    }
1177
1178    #[test]
1179    fn test_set_path_key_on_non_mapping_intermediate() {
1180        let mut map = IndexMap::new();
1181        map.insert("key".to_string(), Value::Integer(42));
1182        let mut v = Value::Mapping(map);
1183        // Trying to set "key.subkey" when "key" is an integer
1184        assert!(v.set_path("key.subkey", Value::Integer(1)).is_err());
1185    }
1186
1187    #[test]
1188    fn test_set_path_index_on_non_sequence() {
1189        let mut map = IndexMap::new();
1190        map.insert("key".to_string(), Value::Integer(42));
1191        let mut v = Value::Mapping(map);
1192        assert!(v.set_path("key[0]", Value::Integer(1)).is_err());
1193    }
1194
1195    #[test]
1196    fn test_set_path_intermediate_index_out_of_bounds() {
1197        let mut map = IndexMap::new();
1198        map.insert(
1199            "items".to_string(),
1200            Value::Sequence(vec![Value::Mapping(IndexMap::new())]),
1201        );
1202        let mut v = Value::Mapping(map);
1203        assert!(v.set_path("items[99].key", Value::Integer(1)).is_err());
1204    }
1205
1206    #[test]
1207    fn test_merged_non_mutating() {
1208        let mut base_map = IndexMap::new();
1209        base_map.insert("a".to_string(), Value::Integer(1));
1210        let base = Value::Mapping(base_map);
1211
1212        let mut overlay_map = IndexMap::new();
1213        overlay_map.insert("b".to_string(), Value::Integer(2));
1214        let overlay = Value::Mapping(overlay_map);
1215
1216        let result = base.merged(overlay);
1217
1218        assert_eq!(result.get_path("a").unwrap().as_i64(), Some(1));
1219        assert_eq!(result.get_path("b").unwrap().as_i64(), Some(2));
1220    }
1221
1222    #[test]
1223    fn test_parse_path_invalid_index() {
1224        assert!(parse_path("items[abc]").is_err());
1225    }
1226
1227    #[test]
1228    fn test_parse_path_unexpected_bracket() {
1229        assert!(parse_path("items]").is_err());
1230    }
1231
1232    #[test]
1233    fn test_is_type_negative_cases() {
1234        let integer = Value::Integer(42);
1235        assert!(!integer.is_null());
1236        assert!(!integer.is_bool());
1237        assert!(!integer.is_float());
1238        assert!(!integer.is_string());
1239        assert!(!integer.is_sequence());
1240        assert!(!integer.is_mapping());
1241        assert!(!integer.is_bytes());
1242    }
1243
1244    // Tests for Value::Bytes
1245
1246    #[test]
1247    fn test_bytes_is_bytes() {
1248        let bytes = Value::Bytes(vec![0x48, 0x65, 0x6c, 0x6c, 0x6f]); // "Hello"
1249        assert!(bytes.is_bytes());
1250        assert!(!bytes.is_string());
1251        assert!(!bytes.is_null());
1252    }
1253
1254    #[test]
1255    fn test_bytes_as_bytes() {
1256        let bytes = Value::Bytes(vec![1, 2, 3, 4, 5]);
1257        assert_eq!(bytes.as_bytes(), Some(&[1u8, 2, 3, 4, 5][..]));
1258    }
1259
1260    #[test]
1261    fn test_bytes_as_bytes_non_bytes() {
1262        assert!(Value::String("hello".into()).as_bytes().is_none());
1263        assert!(Value::Integer(42).as_bytes().is_none());
1264    }
1265
1266    #[test]
1267    fn test_bytes_type_name() {
1268        assert_eq!(Value::Bytes(vec![]).type_name(), "bytes");
1269    }
1270
1271    #[test]
1272    fn test_bytes_display() {
1273        let bytes = Value::Bytes(vec![1, 2, 3, 4, 5]);
1274        assert_eq!(format!("{}", bytes), "<bytes: 5 bytes>");
1275
1276        let empty = Value::Bytes(vec![]);
1277        assert_eq!(format!("{}", empty), "<bytes: 0 bytes>");
1278    }
1279
1280    #[test]
1281    fn test_bytes_from_vec() {
1282        let v: Value = vec![0x48u8, 0x65, 0x6c, 0x6c, 0x6f].into();
1283        assert!(v.is_bytes());
1284        assert_eq!(v.as_bytes(), Some(&[0x48u8, 0x65, 0x6c, 0x6c, 0x6f][..]));
1285    }
1286
1287    #[test]
1288    fn test_bytes_serialize_to_base64() {
1289        let bytes = Value::Bytes(vec![0x48, 0x65, 0x6c, 0x6c, 0x6f]); // "Hello"
1290        let yaml = serde_yaml::to_string(&bytes).unwrap();
1291        // Base64 of "Hello" is "SGVsbG8="
1292        assert!(yaml.contains("SGVsbG8="));
1293    }
1294
1295    #[test]
1296    fn test_bytes_serialize_empty() {
1297        let bytes = Value::Bytes(vec![]);
1298        let json = serde_json::to_string(&bytes).unwrap();
1299        // Base64 of empty is ""
1300        assert_eq!(json, "\"\"");
1301    }
1302
1303    #[test]
1304    fn test_bytes_equality() {
1305        let a = Value::Bytes(vec![1, 2, 3]);
1306        let b = Value::Bytes(vec![1, 2, 3]);
1307        let c = Value::Bytes(vec![1, 2, 4]);
1308
1309        assert_eq!(a, b);
1310        assert_ne!(a, c);
1311    }
1312
1313    #[test]
1314    fn test_bytes_clone() {
1315        let original = Value::Bytes(vec![1, 2, 3, 4, 5]);
1316        let cloned = original.clone();
1317        assert_eq!(original, cloned);
1318    }
1319}