Skip to main content

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