Skip to main content

libconfig/
value.rs

1//! A dynamically-typed representation of libconfig data
2//!
3//! This module provides the `Value` enum which represents any valid libconfig value.
4//! It can be used when you don't know the structure of the data at compile time.
5
6use crate::de::{set_list_context, take_list_context};
7use crate::error::{Error, ErrorKind, Result};
8use crate::ser::{escape_str_into, write_f64};
9use indexmap::IndexMap;
10use serde::de::{self, Deserialize, Deserializer, IntoDeserializer, MapAccess, SeqAccess, Visitor};
11use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer};
12use std::fmt;
13use std::ops::Index;
14use std::path::Path;
15
16/// A dynamically-typed value that can represent any libconfig data
17///
18/// # Examples
19///
20/// ```
21/// use libconfig::Value;
22///
23/// let config = r#"
24///     name = "My App";
25///     version = 1;
26///     enabled = true;
27///     ports = [8080, 8081, 8082];
28/// "#;
29///
30/// let v = Value::from_str(config).unwrap();
31///
32/// assert_eq!(v["name"], "My App");
33/// assert_eq!(v["version"], 1);
34/// assert_eq!(v["enabled"], true);
35/// assert_eq!(v["ports"][0], 8080);
36/// ```
37#[derive(Debug, Clone, PartialEq)]
38pub enum Value {
39    /// Represents a boolean value
40    Bool(bool),
41
42    /// Represents a 32-bit integer
43    Integer(i32),
44
45    /// Represents a 64-bit integer
46    Integer64(i64),
47
48    /// Represents a floating point number
49    Float(f64),
50
51    /// Represents a string
52    String(String),
53
54    /// Represents an array (homogeneous sequence)
55    Array(Vec<Value>),
56
57    /// Represents a list (heterogeneous sequence)
58    List(Vec<Value>),
59
60    /// Represents a group (object/map)
61    Group(Map),
62}
63
64/// A map of string keys to `Value`s, representing a libconfig group
65/// Uses IndexMap to preserve insertion order
66pub type Map = IndexMap<String, Value>;
67
68/// A single segment of a libconfig path.
69#[derive(Debug, Clone, PartialEq)]
70enum PathSegment<'a> {
71    /// A named key for indexing into a Group
72    Key(&'a str),
73    /// A numeric index for indexing into an Array or List
74    Index(usize),
75}
76
77/// Parses a libconfig path string into a sequence of segments.
78///
79/// Supports:
80///   - Dot-separated keys: `"application.window.title"`
81///   - Bracket indices: `"items.[0].title"` (libconfig spec)
82///   - Bare numeric indices: `"items.0"` (backward compat)
83///
84/// Returns `None` if the path is empty or contains malformed segments.
85fn parse_path(path: &str) -> Option<Vec<PathSegment<'_>>> {
86    if path.is_empty() {
87        return None;
88    }
89
90    let mut segments = Vec::new();
91    for part in path.split('.') {
92        if part.is_empty() {
93            return None;
94        }
95
96        if part.starts_with('[') && part.ends_with(']') {
97            let inner = &part[1..part.len() - 1];
98            let index: usize = inner.parse().ok()?;
99            segments.push(PathSegment::Index(index));
100        } else if let Ok(index) = part.parse::<usize>() {
101            segments.push(PathSegment::Index(index));
102        } else {
103            segments.push(PathSegment::Key(part));
104        }
105    }
106
107    if segments.is_empty() {
108        None
109    } else {
110        Some(segments)
111    }
112}
113
114impl Value {
115    /// Returns true if the value is a boolean
116    pub fn is_bool(&self) -> bool {
117        matches!(self, Value::Bool(_))
118    }
119
120    /// Returns true if the value is an integer (32-bit or 64-bit)
121    pub fn is_integer(&self) -> bool {
122        matches!(self, Value::Integer(_) | Value::Integer64(_))
123    }
124
125    /// Returns true if the value is a 32-bit integer
126    pub fn is_i32(&self) -> bool {
127        matches!(self, Value::Integer(_))
128    }
129
130    /// Returns true if the value is a 64-bit integer
131    pub fn is_i64(&self) -> bool {
132        matches!(self, Value::Integer64(_))
133    }
134
135    /// Returns true if the value is a float
136    pub fn is_float(&self) -> bool {
137        matches!(self, Value::Float(_))
138    }
139
140    /// Returns true if the value is a number (integer or float)
141    pub fn is_number(&self) -> bool {
142        matches!(
143            self,
144            Value::Integer(_) | Value::Integer64(_) | Value::Float(_)
145        )
146    }
147
148    /// Returns true if the value is a string
149    pub fn is_string(&self) -> bool {
150        matches!(self, Value::String(_))
151    }
152
153    /// Returns true if the value is an array
154    pub fn is_array(&self) -> bool {
155        matches!(self, Value::Array(_))
156    }
157
158    /// Returns true if the value is a list
159    pub fn is_list(&self) -> bool {
160        matches!(self, Value::List(_))
161    }
162
163    /// Returns true if the value is a group (object)
164    pub fn is_group(&self) -> bool {
165        matches!(self, Value::Group(_))
166    }
167
168    /// If the value is a boolean, returns it. Returns None otherwise.
169    pub fn as_bool(&self) -> Option<bool> {
170        match self {
171            Value::Bool(b) => Some(*b),
172            _ => None,
173        }
174    }
175
176    /// If the value is an integer, returns it as i32. Returns None otherwise.
177    pub fn as_i32(&self) -> Option<i32> {
178        match self {
179            Value::Integer(i) => Some(*i),
180            Value::Integer64(i) if *i >= i32::MIN as i64 && *i <= i32::MAX as i64 => {
181                Some(*i as i32)
182            }
183            _ => None,
184        }
185    }
186
187    /// If the value is an integer, returns it as i64. Returns None otherwise.
188    pub fn as_i64(&self) -> Option<i64> {
189        match self {
190            Value::Integer(i) => Some(*i as i64),
191            Value::Integer64(i) => Some(*i),
192            _ => None,
193        }
194    }
195
196    /// If the value is a float, returns it. Integers are converted to their
197    /// float value (a lossless promotion for `Integer`; `Integer64` values
198    /// beyond 2^53 lose precision). Returns None for non-numeric values.
199    pub fn as_f64(&self) -> Option<f64> {
200        match self {
201            Value::Float(f) => Some(*f),
202            Value::Integer(i) => Some(*i as f64),
203            Value::Integer64(i) => Some(*i as f64),
204            _ => None,
205        }
206    }
207
208    /// If the value is a string, returns a reference to it. Returns None otherwise.
209    pub fn as_str(&self) -> Option<&str> {
210        match self {
211            Value::String(s) => Some(s),
212            _ => None,
213        }
214    }
215
216    /// If the value is an array **or a list**, returns a reference to its
217    /// elements. Use [`Value::is_array`]/[`Value::is_list`] to distinguish
218    /// the two aggregate kinds. Returns None otherwise.
219    pub fn as_array(&self) -> Option<&Vec<Value>> {
220        match self {
221            Value::Array(arr) | Value::List(arr) => Some(arr),
222            _ => None,
223        }
224    }
225
226    /// If the value is an array or a list, returns a mutable reference to
227    /// its elements. Returns None otherwise.
228    pub fn as_array_mut(&mut self) -> Option<&mut Vec<Value>> {
229        match self {
230            Value::Array(arr) | Value::List(arr) => Some(arr),
231            _ => None,
232        }
233    }
234
235    /// If the value is a group, returns a reference to the map. Returns None otherwise.
236    pub fn as_group(&self) -> Option<&Map> {
237        match self {
238            Value::Group(map) => Some(map),
239            _ => None,
240        }
241    }
242
243    /// If the value is a group, returns a mutable reference to the map.
244    /// Returns None otherwise.
245    ///
246    /// Note: keys inserted directly into the map are not validated; keys
247    /// that are not grammar-valid setting names will fail at serialization
248    /// time. Prefer [`Value::set`], which validates.
249    pub fn as_group_mut(&mut self) -> Option<&mut Map> {
250        match self {
251            Value::Group(map) => Some(map),
252            _ => None,
253        }
254    }
255
256    /// Gets a reference to a value in a group by key
257    ///
258    /// Returns None if the value is not a group or the key doesn't exist.
259    pub fn get<S: AsRef<str>>(&self, key: S) -> Option<&Value> {
260        match self {
261            Value::Group(map) => map.get(key.as_ref()),
262            _ => None,
263        }
264    }
265
266    /// Gets a mutable reference to a value in a group by key
267    ///
268    /// Returns None if the value is not a group or the key doesn't exist.
269    pub fn get_mut<S: AsRef<str>>(&mut self, key: S) -> Option<&mut Value> {
270        match self {
271            Value::Group(map) => map.get_mut(key.as_ref()),
272            _ => None,
273        }
274    }
275
276    /// Gets a reference to a value in an array/list by index
277    ///
278    /// Returns None if the value is not an array/list or the index is out of bounds.
279    pub fn get_index(&self, index: usize) -> Option<&Value> {
280        match self {
281            Value::Array(arr) | Value::List(arr) => arr.get(index),
282            _ => None,
283        }
284    }
285
286    /// Gets a mutable reference to a value in an array/list by index
287    ///
288    /// Returns None if the value is not an array/list or the index is out of bounds.
289    pub fn get_index_mut(&mut self, index: usize) -> Option<&mut Value> {
290        match self {
291            Value::Array(arr) | Value::List(arr) => arr.get_mut(index),
292            _ => None,
293        }
294    }
295
296    /// Looks up a value by a dotted path like "application.window.title"
297    ///
298    /// Returns None if any part of the path doesn't exist.
299    ///
300    /// # Examples
301    ///
302    /// ```
303    /// use libconfig::Value;
304    ///
305    /// let config = r#"
306    ///     application = {
307    ///         window = {
308    ///             title = "My App";
309    ///         };
310    ///     };
311    /// "#;
312    ///
313    /// let v = Value::from_str(config).unwrap();
314    /// assert_eq!(v.lookup("application.window.title").and_then(|v| v.as_str()), Some("My App"));
315    /// ```
316    pub fn lookup(&self, path: &str) -> Option<&Value> {
317        let segments = parse_path(path)?;
318        let mut current = self;
319        for segment in &segments {
320            match segment {
321                PathSegment::Key(key) => current = current.get(*key)?,
322                PathSegment::Index(index) => current = current.get_index(*index)?,
323            }
324        }
325        Some(current)
326    }
327
328    /// Looks up a mutable reference to a value by a dotted path like "application.window.title"
329    ///
330    /// Returns None if any part of the path doesn't exist.
331    /// Supports both bracket notation (`items.[0]`) and bare numeric (`items.0`).
332    ///
333    /// # Examples
334    ///
335    /// ```
336    /// use libconfig::Value;
337    ///
338    /// let config = r#"
339    ///     ports = [80, 443];
340    /// "#;
341    ///
342    /// let mut v = Value::from_str(config).unwrap();
343    /// *v.lookup_mut("ports.[0]").unwrap() = Value::Integer(8080);
344    /// assert_eq!(v["ports.0"], 8080);
345    /// ```
346    pub fn lookup_mut(&mut self, path: &str) -> Option<&mut Value> {
347        let segments = parse_path(path)?;
348        let mut current = self;
349        for segment in &segments {
350            match segment {
351                PathSegment::Key(key) => match current {
352                    Value::Group(map) => current = map.get_mut(*key)?,
353                    _ => return None,
354                },
355                PathSegment::Index(index) => match current {
356                    Value::Array(arr) | Value::List(arr) => current = arr.get_mut(*index)?,
357                    _ => return None,
358                },
359            }
360        }
361        Some(current)
362    }
363
364    /// Sets a value at the given path, creating intermediate `Group` nodes as needed.
365    ///
366    /// The value may be anything convertible into a [`Value`] (integers,
367    /// floats, booleans, strings, or a `Value` itself). Array/list indices
368    /// must refer to existing elements (no auto-extension).
369    ///
370    /// # Errors
371    ///
372    /// - [`ErrorKind::InvalidPath`] if the path is malformed
373    /// - [`ErrorKind::InvalidSettingName`] if a key segment is not a valid
374    ///   setting name (`[A-Za-z*][-A-Za-z0-9_*]*`)
375    /// - [`ErrorKind::PathTraversal`] if an intermediate value has the wrong
376    ///   type or an index is out of bounds
377    ///
378    /// # Examples
379    ///
380    /// ```
381    /// use libconfig::{Map, Value};
382    ///
383    /// let mut v = Value::Group(Map::new());
384    /// v.set("a.b.c", 42).unwrap();
385    /// assert_eq!(v.lookup("a.b.c").unwrap().as_i32(), Some(42));
386    /// ```
387    pub fn set(&mut self, path: &str, value: impl Into<Value>) -> Result<()> {
388        let segments = parse_path(path)
389            .ok_or_else(|| Error::from(ErrorKind::InvalidPath(path.to_string())))?;
390
391        // Key segments become setting names and must be grammar-valid,
392        // otherwise the document could not be serialized back out
393        for segment in &segments {
394            if let PathSegment::Key(key) = segment {
395                if !crate::ser::is_valid_name(key) {
396                    return Err(ErrorKind::InvalidSettingName(key.to_string()).into());
397                }
398            }
399        }
400
401        let mut current = self;
402
403        // Navigate to the parent, creating intermediate Groups as needed
404        for segment in &segments[..segments.len() - 1] {
405            match segment {
406                PathSegment::Key(key) => match current {
407                    Value::Group(map) => {
408                        current = map
409                            .entry(key.to_string())
410                            .or_insert_with(|| Value::Group(IndexMap::new()));
411                    }
412                    _ => {
413                        return Err(ErrorKind::PathTraversal(format!(
414                            "'{}' in '{}' is not a group",
415                            key, path
416                        ))
417                        .into());
418                    }
419                },
420                PathSegment::Index(index) => match current {
421                    Value::Array(arr) | Value::List(arr) => {
422                        let len = arr.len();
423                        current = arr.get_mut(*index).ok_or_else(|| {
424                            Error::from(ErrorKind::PathTraversal(format!(
425                                "index {} out of bounds (length {}) in '{}'",
426                                index, len, path
427                            )))
428                        })?;
429                    }
430                    _ => {
431                        return Err(ErrorKind::PathTraversal(format!(
432                            "cannot index into a non-array value in '{}'",
433                            path
434                        ))
435                        .into());
436                    }
437                },
438            }
439        }
440
441        // Apply the final segment
442        match segments.last().unwrap() {
443            PathSegment::Key(key) => match current {
444                Value::Group(map) => {
445                    map.insert(key.to_string(), value.into());
446                    Ok(())
447                }
448                _ => Err(ErrorKind::PathTraversal(format!(
449                    "cannot set '{}': parent is not a group",
450                    path
451                ))
452                .into()),
453            },
454            PathSegment::Index(index) => match current {
455                Value::Array(arr) | Value::List(arr) => {
456                    if *index < arr.len() {
457                        arr[*index] = value.into();
458                        Ok(())
459                    } else {
460                        Err(ErrorKind::PathTraversal(format!(
461                            "index {} out of bounds (length {}) in '{}'",
462                            index,
463                            arr.len(),
464                            path
465                        ))
466                        .into())
467                    }
468                }
469                _ => Err(ErrorKind::PathTraversal(format!(
470                    "cannot set '{}': parent is not an array or list",
471                    path
472                ))
473                .into()),
474            },
475        }
476    }
477
478    /// Removes a value at the given path and returns it.
479    ///
480    /// For group members, the key-value pair is removed.
481    /// For array/list elements, the element is removed and subsequent elements shift down.
482    ///
483    /// Returns `None` if the path is invalid or the target doesn't exist.
484    ///
485    /// # Examples
486    ///
487    /// ```
488    /// use libconfig::Value;
489    ///
490    /// let mut v = Value::from_str(r#"a = 1; b = 2; c = 3;"#).unwrap();
491    /// let removed = v.remove("b");
492    /// assert_eq!(removed, Some(Value::Integer(2)));
493    /// assert!(v.lookup("b").is_none());
494    /// ```
495    pub fn remove(&mut self, path: &str) -> Option<Value> {
496        let segments = parse_path(path)?;
497        if segments.is_empty() {
498            return None;
499        }
500
501        let mut current = self;
502
503        // Navigate to the parent
504        for segment in &segments[..segments.len() - 1] {
505            match segment {
506                PathSegment::Key(key) => match current {
507                    Value::Group(map) => current = map.get_mut(*key)?,
508                    _ => return None,
509                },
510                PathSegment::Index(index) => match current {
511                    Value::Array(arr) | Value::List(arr) => current = arr.get_mut(*index)?,
512                    _ => return None,
513                },
514            }
515        }
516
517        // Remove at the final segment
518        match segments.last().unwrap() {
519            PathSegment::Key(key) => match current {
520                Value::Group(map) => map.shift_remove(*key),
521                _ => None,
522            },
523            PathSegment::Index(index) => match current {
524                Value::Array(arr) | Value::List(arr) => {
525                    if *index < arr.len() {
526                        Some(arr.remove(*index))
527                    } else {
528                        None
529                    }
530                }
531                _ => None,
532            },
533        }
534    }
535
536    /// Parse a libconfig string into a `Value`.
537    ///
538    /// # Examples
539    ///
540    /// ```
541    /// use libconfig::Value;
542    ///
543    /// let v = Value::from_str("name = \"test\"; version = 1;").unwrap();
544    /// assert_eq!(v["name"], "test");
545    /// assert_eq!(v["version"], 1);
546    /// ```
547    #[allow(clippy::should_implement_trait)] // std::str::FromStr is also implemented
548    pub fn from_str(s: &str) -> Result<Value> {
549        crate::from_str(s)
550    }
551
552}
553
554impl std::str::FromStr for Value {
555    type Err = Error;
556
557    fn from_str(s: &str) -> Result<Value> {
558        crate::from_str(s)
559    }
560}
561
562// Conversions from native types, for ergonomic construction and `set()`
563impl From<bool> for Value {
564    fn from(v: bool) -> Value {
565        Value::Bool(v)
566    }
567}
568
569impl From<i8> for Value {
570    fn from(v: i8) -> Value {
571        Value::Integer(i32::from(v))
572    }
573}
574
575impl From<i16> for Value {
576    fn from(v: i16) -> Value {
577        Value::Integer(i32::from(v))
578    }
579}
580
581impl From<i32> for Value {
582    fn from(v: i32) -> Value {
583        Value::Integer(v)
584    }
585}
586
587impl From<i64> for Value {
588    fn from(v: i64) -> Value {
589        Value::Integer64(v)
590    }
591}
592
593impl From<u8> for Value {
594    fn from(v: u8) -> Value {
595        Value::Integer(i32::from(v))
596    }
597}
598
599impl From<u16> for Value {
600    fn from(v: u16) -> Value {
601        Value::Integer(i32::from(v))
602    }
603}
604
605impl From<u32> for Value {
606    fn from(v: u32) -> Value {
607        // Values beyond i32 range are 64-bit integers (manual section 2.6)
608        if v > i32::MAX as u32 {
609            Value::Integer64(i64::from(v))
610        } else {
611            Value::Integer(v as i32)
612        }
613    }
614}
615
616impl From<f32> for Value {
617    fn from(v: f32) -> Value {
618        Value::Float(f64::from(v))
619    }
620}
621
622impl From<f64> for Value {
623    fn from(v: f64) -> Value {
624        Value::Float(v)
625    }
626}
627
628impl From<&str> for Value {
629    fn from(v: &str) -> Value {
630        Value::String(v.to_string())
631    }
632}
633
634impl From<String> for Value {
635    fn from(v: String) -> Value {
636        Value::String(v)
637    }
638}
639
640impl From<Map> for Value {
641    fn from(v: Map) -> Value {
642        Value::Group(v)
643    }
644}
645
646/// Indexing by dotted path (e.g. `value["misc.location"]`), with the same
647/// path syntax as [`Value::lookup`].
648///
649/// # Panics
650///
651/// Panics if the path does not resolve. Use [`Value::lookup`] for a
652/// non-panicking alternative.
653impl Index<&str> for Value {
654    type Output = Value;
655
656    fn index(&self, key: &str) -> &Self::Output {
657        self.lookup(key)
658            .unwrap_or_else(|| panic!("path '{}' not found", key))
659    }
660}
661
662/// Indexing into arrays and lists by element position.
663///
664/// # Panics
665///
666/// Panics if the value is not an array/list or the index is out of bounds.
667/// Use [`Value::get_index`] for a non-panicking alternative.
668impl Index<usize> for Value {
669    type Output = Value;
670
671    fn index(&self, index: usize) -> &Self::Output {
672        self.get_index(index)
673            .unwrap_or_else(|| panic!("index {} out of bounds", index))
674    }
675}
676
677// Implement PartialEq for comparing with native types
678impl PartialEq<bool> for Value {
679    fn eq(&self, other: &bool) -> bool {
680        self.as_bool() == Some(*other)
681    }
682}
683
684impl PartialEq<i32> for Value {
685    fn eq(&self, other: &i32) -> bool {
686        self.as_i32() == Some(*other)
687    }
688}
689
690impl PartialEq<i64> for Value {
691    fn eq(&self, other: &i64) -> bool {
692        self.as_i64() == Some(*other)
693    }
694}
695
696impl PartialEq<f64> for Value {
697    fn eq(&self, other: &f64) -> bool {
698        self.as_f64() == Some(*other)
699    }
700}
701
702impl PartialEq<str> for Value {
703    fn eq(&self, other: &str) -> bool {
704        self.as_str() == Some(other)
705    }
706}
707
708impl PartialEq<&str> for Value {
709    fn eq(&self, other: &&str) -> bool {
710        self.as_str() == Some(*other)
711    }
712}
713
714impl PartialEq<String> for Value {
715    fn eq(&self, other: &String) -> bool {
716        self.as_str() == Some(other.as_str())
717    }
718}
719
720// Reverse comparisons, so `42 == value` works like `value == 42`
721impl PartialEq<Value> for bool {
722    fn eq(&self, other: &Value) -> bool {
723        other == self
724    }
725}
726
727impl PartialEq<Value> for i32 {
728    fn eq(&self, other: &Value) -> bool {
729        other == self
730    }
731}
732
733impl PartialEq<Value> for i64 {
734    fn eq(&self, other: &Value) -> bool {
735        other == self
736    }
737}
738
739impl PartialEq<Value> for f64 {
740    fn eq(&self, other: &Value) -> bool {
741        other == self
742    }
743}
744
745impl PartialEq<Value> for str {
746    fn eq(&self, other: &Value) -> bool {
747        other == self
748    }
749}
750
751impl PartialEq<Value> for &str {
752    fn eq(&self, other: &Value) -> bool {
753        other == *self
754    }
755}
756
757impl PartialEq<Value> for String {
758    fn eq(&self, other: &Value) -> bool {
759        other == self
760    }
761}
762
763// Implement Display for compact single-line printing. The output is valid
764// libconfig syntax (strings are escaped; groups use `key = value;` settings).
765impl fmt::Display for Value {
766    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
767        match self {
768            Value::Bool(b) => write!(f, "{}", b),
769            Value::Integer(i) => write!(f, "{}", i),
770            Value::Integer64(i) => write!(f, "{}L", i),
771            Value::Float(fl) => {
772                let mut s = String::new();
773                // Fall back to the bare Display output for non-finite values,
774                // which have no libconfig representation
775                match write_f64(&mut s, *fl, &crate::WriteOptions::default()) {
776                    Ok(()) => f.write_str(&s),
777                    Err(_) => write!(f, "{}", fl),
778                }
779            }
780            Value::String(s) => {
781                let mut escaped = String::with_capacity(s.len() + 2);
782                escape_str_into(&mut escaped, s);
783                f.write_str(&escaped)
784            }
785            Value::Array(arr) => {
786                write!(f, "[")?;
787                for (i, v) in arr.iter().enumerate() {
788                    if i > 0 {
789                        write!(f, ", ")?;
790                    }
791                    write!(f, "{}", v)?;
792                }
793                write!(f, "]")
794            }
795            Value::List(arr) => {
796                write!(f, "(")?;
797                for (i, v) in arr.iter().enumerate() {
798                    if i > 0 {
799                        write!(f, ", ")?;
800                    }
801                    write!(f, "{}", v)?;
802                }
803                write!(f, ")")
804            }
805            Value::Group(map) => {
806                write!(f, "{{")?;
807                for (k, v) in map.iter() {
808                    write!(f, " {} = {};", k, v)?;
809                }
810                write!(f, " }}")
811            }
812        }
813    }
814}
815
816// Implement Serialize for Value
817impl Serialize for Value {
818    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
819    where
820        S: Serializer,
821    {
822        match self {
823            Value::Bool(b) => serializer.serialize_bool(*b),
824            Value::Integer(i) => serializer.serialize_i32(*i),
825            Value::Integer64(i) => serializer.serialize_i64(*i),
826            Value::Float(f) => serializer.serialize_f64(*f),
827            Value::String(s) => serializer.serialize_str(s),
828            Value::Array(arr) => {
829                let mut seq = serializer.serialize_seq(Some(arr.len()))?;
830                for element in arr {
831                    seq.serialize_element(element)?;
832                }
833                seq.end()
834            }
835            Value::List(arr) => {
836                use serde::ser::SerializeTuple;
837                let mut tup = serializer.serialize_tuple(arr.len())?;
838                for element in arr {
839                    tup.serialize_element(element)?;
840                }
841                tup.end()
842            }
843            Value::Group(map) => {
844                let mut map_ser = serializer.serialize_map(Some(map.len()))?;
845                for (k, v) in map {
846                    map_ser.serialize_entry(k, v)?;
847                }
848                map_ser.end()
849            }
850        }
851    }
852}
853
854// Implement Deserialize for Value
855impl<'de> Deserialize<'de> for Value {
856    fn deserialize<D>(deserializer: D) -> std::result::Result<Value, D::Error>
857    where
858        D: Deserializer<'de>,
859    {
860        deserializer.deserialize_any(ValueVisitor)
861    }
862}
863
864/// Returns the scalar "type family" of a value for array-homogeneity
865/// purposes. Integer and Integer64 are one family (manual section 2.6);
866/// aggregates return None.
867pub(crate) fn scalar_family(v: &Value) -> Option<u8> {
868    match v {
869        Value::Bool(_) => Some(0),
870        Value::Integer(_) | Value::Integer64(_) => Some(1),
871        Value::Float(_) => Some(2),
872        Value::String(_) => Some(3),
873        _ => None,
874    }
875}
876
877/// Returns true if all values are scalars of a single type family, i.e. the
878/// sequence qualifies as an array per the manual (section 2.3).
879pub(crate) fn is_homogeneous_scalar_seq(values: &[Value]) -> bool {
880    let mut family = None;
881    for v in values {
882        let Some(f) = scalar_family(v) else {
883            return false;
884        };
885        match family {
886            None => family = Some(f),
887            Some(prev) if prev != f => return false,
888            _ => {}
889        }
890    }
891    true
892}
893
894struct ValueVisitor;
895
896impl<'de> Visitor<'de> for ValueVisitor {
897    type Value = Value;
898
899    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
900        formatter.write_str("any valid libconfig value")
901    }
902
903    fn visit_bool<E>(self, value: bool) -> std::result::Result<Value, E>
904    where
905        E: de::Error,
906    {
907        Ok(Value::Bool(value))
908    }
909
910    fn visit_i8<E>(self, value: i8) -> std::result::Result<Value, E>
911    where
912        E: de::Error,
913    {
914        Ok(Value::Integer(value as i32))
915    }
916
917    fn visit_i16<E>(self, value: i16) -> std::result::Result<Value, E>
918    where
919        E: de::Error,
920    {
921        Ok(Value::Integer(value as i32))
922    }
923
924    fn visit_i32<E>(self, value: i32) -> std::result::Result<Value, E>
925    where
926        E: de::Error,
927    {
928        Ok(Value::Integer(value))
929    }
930
931    fn visit_i64<E>(self, value: i64) -> std::result::Result<Value, E>
932    where
933        E: de::Error,
934    {
935        Ok(Value::Integer64(value))
936    }
937
938    fn visit_u8<E>(self, value: u8) -> std::result::Result<Value, E>
939    where
940        E: de::Error,
941    {
942        Ok(Value::Integer(value as i32))
943    }
944
945    fn visit_u16<E>(self, value: u16) -> std::result::Result<Value, E>
946    where
947        E: de::Error,
948    {
949        Ok(Value::Integer(value as i32))
950    }
951
952    fn visit_u32<E>(self, value: u32) -> std::result::Result<Value, E>
953    where
954        E: de::Error,
955    {
956        if value <= i32::MAX as u32 {
957            Ok(Value::Integer(value as i32))
958        } else {
959            Ok(Value::Integer64(value as i64))
960        }
961    }
962
963    fn visit_u64<E>(self, value: u64) -> std::result::Result<Value, E>
964    where
965        E: de::Error,
966    {
967        if value <= i32::MAX as u64 {
968            Ok(Value::Integer(value as i32))
969        } else {
970            Ok(Value::Integer64(value as i64))
971        }
972    }
973
974    fn visit_f32<E>(self, value: f32) -> std::result::Result<Value, E>
975    where
976        E: de::Error,
977    {
978        Ok(Value::Float(value as f64))
979    }
980
981    fn visit_f64<E>(self, value: f64) -> std::result::Result<Value, E>
982    where
983        E: de::Error,
984    {
985        Ok(Value::Float(value))
986    }
987
988    fn visit_str<E>(self, value: &str) -> std::result::Result<Value, E>
989    where
990        E: de::Error,
991    {
992        Ok(Value::String(value.to_string()))
993    }
994
995    fn visit_borrowed_str<E>(self, value: &'de str) -> std::result::Result<Value, E>
996    where
997        E: de::Error,
998    {
999        Ok(Value::String(value.to_string()))
1000    }
1001
1002    fn visit_string<E>(self, value: String) -> std::result::Result<Value, E>
1003    where
1004        E: de::Error,
1005    {
1006        Ok(Value::String(value))
1007    }
1008
1009    fn visit_seq<A>(self, mut seq: A) -> std::result::Result<Value, A::Error>
1010    where
1011        A: SeqAccess<'de>,
1012    {
1013        // Some(kind) when driven by this crate's deserializers; None when a
1014        // foreign format (e.g. serde_json) is deserializing into Value
1015        let context = take_list_context();
1016        let mut values = Vec::new();
1017        while let Some(value) = seq.next_element()? {
1018            values.push(value);
1019        }
1020        match context {
1021            Some(true) => Ok(Value::List(values)),
1022            Some(false) => {
1023                // Arrays must be homogeneous scalars per the manual (section
1024                // 2.3): "the elements must all be scalar values of the same
1025                // type". Integer and Integer64 count as one type (section 2.6).
1026                if !is_homogeneous_scalar_seq(&values) {
1027                    return Err(de::Error::custom(
1028                        "array elements must all be scalar values of the same type",
1029                    ));
1030                }
1031                Ok(Value::Array(values))
1032            }
1033            // Foreign formats have no array/list distinction: use an array
1034            // when the elements qualify, otherwise a list
1035            None => {
1036                if is_homogeneous_scalar_seq(&values) {
1037                    Ok(Value::Array(values))
1038                } else {
1039                    Ok(Value::List(values))
1040                }
1041            }
1042        }
1043    }
1044
1045    fn visit_map<A>(self, mut map: A) -> std::result::Result<Value, A::Error>
1046    where
1047        A: MapAccess<'de>,
1048    {
1049        let mut values = Map::new();
1050        while let Some((key, value)) = map.next_entry::<String, Value>()? {
1051            // Duplicate names are rejected per the manual: "each setting must
1052            // have a unique name within the group" (overrides are opt-in in
1053            // the C library and off by default)
1054            match values.entry(key) {
1055                indexmap::map::Entry::Occupied(entry) => {
1056                    return Err(de::Error::custom(format!(
1057                        "duplicate setting name '{}'",
1058                        entry.key()
1059                    )));
1060                }
1061                indexmap::map::Entry::Vacant(entry) => {
1062                    entry.insert(value);
1063                }
1064            }
1065        }
1066        Ok(Value::Group(values))
1067    }
1068}
1069
1070/// Convert any serializable type into a [`Value`].
1071///
1072/// This is the inverse of [`from_value`]. The same representation rules as
1073/// text serialization apply: structs and maps become groups, sequences
1074/// become arrays (homogeneous scalars) or lists, enum unit variants become
1075/// strings, and data-carrying variants become single-setting groups.
1076///
1077/// # Errors
1078///
1079/// Returns an error for values with no libconfig representation: bare
1080/// `None`, unit, `u64` beyond `i64::MAX`, or invalid setting names.
1081/// Non-finite floats are representable in a `Value` and only rejected when
1082/// written out as text.
1083///
1084/// # Examples
1085///
1086/// ```
1087/// use libconfig::{to_value, Value};
1088///
1089/// #[derive(serde::Serialize)]
1090/// struct S { x: i32 }
1091///
1092/// let v = to_value(&S { x: 7 }).unwrap();
1093/// assert_eq!(v["x"], 7);
1094/// ```
1095pub fn to_value<T>(value: &T) -> Result<Value>
1096where
1097    T: ?Sized + Serialize,
1098{
1099    value.serialize(ValueSerializer)
1100}
1101
1102/// Serde serializer that produces a `Value` tree.
1103struct ValueSerializer;
1104
1105impl Serializer for ValueSerializer {
1106    type Ok = Value;
1107    type Error = Error;
1108
1109    type SerializeSeq = SerializeValueSeq;
1110    type SerializeTuple = SerializeValueList;
1111    type SerializeTupleStruct = SerializeValueList;
1112    type SerializeTupleVariant = SerializeValueTupleVariant;
1113    type SerializeMap = SerializeValueMap;
1114    type SerializeStruct = SerializeValueMap;
1115    type SerializeStructVariant = SerializeValueStructVariant;
1116
1117    fn serialize_bool(self, v: bool) -> Result<Value> {
1118        Ok(Value::Bool(v))
1119    }
1120
1121    fn serialize_i8(self, v: i8) -> Result<Value> {
1122        Ok(Value::Integer(i32::from(v)))
1123    }
1124
1125    fn serialize_i16(self, v: i16) -> Result<Value> {
1126        Ok(Value::Integer(i32::from(v)))
1127    }
1128
1129    fn serialize_i32(self, v: i32) -> Result<Value> {
1130        Ok(Value::Integer(v))
1131    }
1132
1133    fn serialize_i64(self, v: i64) -> Result<Value> {
1134        Ok(Value::Integer64(v))
1135    }
1136
1137    fn serialize_u8(self, v: u8) -> Result<Value> {
1138        Ok(Value::Integer(i32::from(v)))
1139    }
1140
1141    fn serialize_u16(self, v: u16) -> Result<Value> {
1142        Ok(Value::Integer(i32::from(v)))
1143    }
1144
1145    fn serialize_u32(self, v: u32) -> Result<Value> {
1146        Ok(Value::from(v))
1147    }
1148
1149    fn serialize_u64(self, v: u64) -> Result<Value> {
1150        i64::try_from(v)
1151            .map(Value::Integer64)
1152            .map_err(|_| ErrorKind::IntegerOutOfRange.into())
1153    }
1154
1155    fn serialize_f32(self, v: f32) -> Result<Value> {
1156        Ok(Value::Float(f64::from(v)))
1157    }
1158
1159    fn serialize_f64(self, v: f64) -> Result<Value> {
1160        Ok(Value::Float(v))
1161    }
1162
1163    fn serialize_char(self, v: char) -> Result<Value> {
1164        Ok(Value::String(v.to_string()))
1165    }
1166
1167    fn serialize_str(self, v: &str) -> Result<Value> {
1168        Ok(Value::String(v.to_string()))
1169    }
1170
1171    fn serialize_bytes(self, v: &[u8]) -> Result<Value> {
1172        Ok(Value::Array(
1173            v.iter().map(|b| Value::Integer(i32::from(*b))).collect(),
1174        ))
1175    }
1176
1177    fn serialize_none(self) -> Result<Value> {
1178        Err(ErrorKind::TypeNotSupported("Option::None".to_string()).into())
1179    }
1180
1181    fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Value> {
1182        value.serialize(self)
1183    }
1184
1185    fn serialize_unit(self) -> Result<Value> {
1186        Err(ErrorKind::UnitNotSupported.into())
1187    }
1188
1189    fn serialize_unit_struct(self, _name: &'static str) -> Result<Value> {
1190        self.serialize_unit()
1191    }
1192
1193    fn serialize_unit_variant(
1194        self,
1195        _name: &'static str,
1196        _variant_index: u32,
1197        variant: &'static str,
1198    ) -> Result<Value> {
1199        Ok(Value::String(variant.to_string()))
1200    }
1201
1202    fn serialize_newtype_struct<T: ?Sized + Serialize>(
1203        self,
1204        _name: &'static str,
1205        value: &T,
1206    ) -> Result<Value> {
1207        value.serialize(self)
1208    }
1209
1210    fn serialize_newtype_variant<T: ?Sized + Serialize>(
1211        self,
1212        _name: &'static str,
1213        _variant_index: u32,
1214        variant: &'static str,
1215        value: &T,
1216    ) -> Result<Value> {
1217        validate_variant_name(variant)?;
1218        let mut map = Map::new();
1219        map.insert(variant.to_string(), value.serialize(ValueSerializer)?);
1220        Ok(Value::Group(map))
1221    }
1222
1223    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
1224        Ok(SerializeValueSeq {
1225            items: Vec::with_capacity(len.unwrap_or(0)),
1226        })
1227    }
1228
1229    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
1230        Ok(SerializeValueList {
1231            items: Vec::with_capacity(len),
1232        })
1233    }
1234
1235    fn serialize_tuple_struct(
1236        self,
1237        _name: &'static str,
1238        len: usize,
1239    ) -> Result<Self::SerializeTupleStruct> {
1240        self.serialize_tuple(len)
1241    }
1242
1243    fn serialize_tuple_variant(
1244        self,
1245        _name: &'static str,
1246        _variant_index: u32,
1247        variant: &'static str,
1248        len: usize,
1249    ) -> Result<Self::SerializeTupleVariant> {
1250        validate_variant_name(variant)?;
1251        Ok(SerializeValueTupleVariant {
1252            variant,
1253            items: Vec::with_capacity(len),
1254        })
1255    }
1256
1257    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
1258        Ok(SerializeValueMap {
1259            map: Map::new(),
1260            pending_key: None,
1261        })
1262    }
1263
1264    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
1265        Ok(SerializeValueMap {
1266            map: Map::new(),
1267            pending_key: None,
1268        })
1269    }
1270
1271    fn serialize_struct_variant(
1272        self,
1273        _name: &'static str,
1274        _variant_index: u32,
1275        variant: &'static str,
1276        _len: usize,
1277    ) -> Result<Self::SerializeStructVariant> {
1278        validate_variant_name(variant)?;
1279        Ok(SerializeValueStructVariant {
1280            variant,
1281            map: Map::new(),
1282        })
1283    }
1284}
1285
1286fn validate_variant_name(name: &str) -> Result<()> {
1287    if crate::ser::is_valid_name(name) {
1288        Ok(())
1289    } else {
1290        Err(ErrorKind::InvalidSettingName(name.to_string()).into())
1291    }
1292}
1293
1294struct SerializeValueSeq {
1295    items: Vec<Value>,
1296}
1297
1298impl serde::ser::SerializeSeq for SerializeValueSeq {
1299    type Ok = Value;
1300    type Error = Error;
1301
1302    fn serialize_element<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<()> {
1303        self.items.push(value.serialize(ValueSerializer)?);
1304        Ok(())
1305    }
1306
1307    fn end(self) -> Result<Value> {
1308        // Same rule as text serialization: homogeneous scalars form an
1309        // array, anything else forms a list
1310        if is_homogeneous_scalar_seq(&self.items) {
1311            Ok(Value::Array(self.items))
1312        } else {
1313            Ok(Value::List(self.items))
1314        }
1315    }
1316}
1317
1318struct SerializeValueList {
1319    items: Vec<Value>,
1320}
1321
1322impl serde::ser::SerializeTuple for SerializeValueList {
1323    type Ok = Value;
1324    type Error = Error;
1325
1326    fn serialize_element<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<()> {
1327        self.items.push(value.serialize(ValueSerializer)?);
1328        Ok(())
1329    }
1330
1331    fn end(self) -> Result<Value> {
1332        Ok(Value::List(self.items))
1333    }
1334}
1335
1336impl serde::ser::SerializeTupleStruct for SerializeValueList {
1337    type Ok = Value;
1338    type Error = Error;
1339
1340    fn serialize_field<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<()> {
1341        self.items.push(value.serialize(ValueSerializer)?);
1342        Ok(())
1343    }
1344
1345    fn end(self) -> Result<Value> {
1346        Ok(Value::List(self.items))
1347    }
1348}
1349
1350struct SerializeValueTupleVariant {
1351    variant: &'static str,
1352    items: Vec<Value>,
1353}
1354
1355impl serde::ser::SerializeTupleVariant for SerializeValueTupleVariant {
1356    type Ok = Value;
1357    type Error = Error;
1358
1359    fn serialize_field<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<()> {
1360        self.items.push(value.serialize(ValueSerializer)?);
1361        Ok(())
1362    }
1363
1364    fn end(self) -> Result<Value> {
1365        let mut map = Map::new();
1366        map.insert(self.variant.to_string(), Value::List(self.items));
1367        Ok(Value::Group(map))
1368    }
1369}
1370
1371struct SerializeValueMap {
1372    map: Map,
1373    pending_key: Option<String>,
1374}
1375
1376impl serde::ser::SerializeMap for SerializeValueMap {
1377    type Ok = Value;
1378    type Error = Error;
1379
1380    fn serialize_key<T: ?Sized + Serialize>(&mut self, key: &T) -> Result<()> {
1381        let mut key_capture = crate::ser::KeyCapture { key: String::new() };
1382        key.serialize(&mut key_capture)?;
1383        self.pending_key = Some(key_capture.key);
1384        Ok(())
1385    }
1386
1387    fn serialize_value<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<()> {
1388        let key = self.pending_key.take().ok_or_else(|| {
1389            Error::from(ErrorKind::Message(
1390                "serialize_value called without serialize_key".to_string(),
1391            ))
1392        })?;
1393        self.map.insert(key, value.serialize(ValueSerializer)?);
1394        Ok(())
1395    }
1396
1397    fn end(self) -> Result<Value> {
1398        Ok(Value::Group(self.map))
1399    }
1400}
1401
1402impl serde::ser::SerializeStruct for SerializeValueMap {
1403    type Ok = Value;
1404    type Error = Error;
1405
1406    fn serialize_field<T: ?Sized + Serialize>(
1407        &mut self,
1408        key: &'static str,
1409        value: &T,
1410    ) -> Result<()> {
1411        validate_variant_name(key)?;
1412        self.map.insert(key.to_string(), value.serialize(ValueSerializer)?);
1413        Ok(())
1414    }
1415
1416    fn end(self) -> Result<Value> {
1417        Ok(Value::Group(self.map))
1418    }
1419}
1420
1421struct SerializeValueStructVariant {
1422    variant: &'static str,
1423    map: Map,
1424}
1425
1426impl serde::ser::SerializeStructVariant for SerializeValueStructVariant {
1427    type Ok = Value;
1428    type Error = Error;
1429
1430    fn serialize_field<T: ?Sized + Serialize>(
1431        &mut self,
1432        key: &'static str,
1433        value: &T,
1434    ) -> Result<()> {
1435        validate_variant_name(key)?;
1436        self.map.insert(key.to_string(), value.serialize(ValueSerializer)?);
1437        Ok(())
1438    }
1439
1440    fn end(self) -> Result<Value> {
1441        let mut inner = Map::new();
1442        inner.insert(self.variant.to_string(), Value::Group(self.map));
1443        Ok(Value::Group(inner))
1444    }
1445}
1446
1447/// Convert a `Value` to a specific type `T`
1448///
1449/// # Examples
1450///
1451/// ```
1452/// use libconfig::Value;
1453///
1454/// let v = Value::from_str("42").unwrap();
1455/// let num: i32 = libconfig::from_value(v).unwrap();
1456/// assert_eq!(num, 42);
1457/// ```
1458pub fn from_value<T>(value: Value) -> Result<T>
1459where
1460    T: de::DeserializeOwned,
1461{
1462    let result = T::deserialize(value);
1463    crate::de::clear_list_context();
1464    result
1465}
1466
1467/// Deserializer implementation for Value
1468impl<'de> Deserializer<'de> for Value {
1469    type Error = Error;
1470
1471    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
1472    where
1473        V: Visitor<'de>,
1474    {
1475        match self {
1476            Value::Bool(b) => visitor.visit_bool(b),
1477            Value::Integer(i) => visitor.visit_i32(i),
1478            Value::Integer64(i) => visitor.visit_i64(i),
1479            Value::Float(f) => visitor.visit_f64(f),
1480            Value::String(s) => visitor.visit_string(s),
1481            Value::Array(arr) => {
1482                // Communicate the sequence kind to ValueVisitor explicitly so
1483                // Value -> Value round-trips don't depend on stale state left
1484                // behind by an earlier parse on this thread
1485                set_list_context(false);
1486                visitor.visit_seq(SeqDeserializer::new(arr.into_iter()))
1487            }
1488            Value::List(arr) => {
1489                set_list_context(true);
1490                visitor.visit_seq(SeqDeserializer::new(arr.into_iter()))
1491            }
1492            Value::Group(map) => visitor.visit_map(MapDeserializer::new(map.into_iter())),
1493        }
1494    }
1495
1496    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
1497    where
1498        V: Visitor<'de>,
1499    {
1500        visitor.visit_some(self)
1501    }
1502
1503    fn deserialize_enum<V>(
1504        self,
1505        _name: &'static str,
1506        _variants: &'static [&'static str],
1507        visitor: V,
1508    ) -> Result<V::Value>
1509    where
1510        V: Visitor<'de>,
1511    {
1512        match self {
1513            // Unit variant: "VariantName"
1514            Value::String(s) => visitor.visit_enum(s.into_deserializer()),
1515            // Data-carrying variant: a group with a single setting
1516            Value::Group(map) => {
1517                if map.len() != 1 {
1518                    return Err(ErrorKind::Message(
1519                        "expected a group with a single setting for an enum variant".to_string(),
1520                    ).into());
1521                }
1522                let (variant, value) = map.into_iter().next().unwrap();
1523                visitor.visit_enum(ValueEnumAccess { variant, value })
1524            }
1525            _ => Err(ErrorKind::Message(
1526                "expected a string or group for an enum".to_string(),
1527            ).into()),
1528        }
1529    }
1530
1531    serde::forward_to_deserialize_any! {
1532        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string
1533        bytes byte_buf unit unit_struct newtype_struct seq tuple
1534        tuple_struct map struct identifier ignored_any
1535    }
1536}
1537
1538struct ValueEnumAccess {
1539    variant: String,
1540    value: Value,
1541}
1542
1543impl<'de> de::EnumAccess<'de> for ValueEnumAccess {
1544    type Error = Error;
1545    type Variant = ValueVariantAccess;
1546
1547    fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)>
1548    where
1549        V: de::DeserializeSeed<'de>,
1550    {
1551        let variant_de: de::value::StringDeserializer<Error> = self.variant.into_deserializer();
1552        let variant = seed.deserialize(variant_de)?;
1553        Ok((variant, ValueVariantAccess { value: self.value }))
1554    }
1555}
1556
1557struct ValueVariantAccess {
1558    value: Value,
1559}
1560
1561impl<'de> de::VariantAccess<'de> for ValueVariantAccess {
1562    type Error = Error;
1563
1564    fn unit_variant(self) -> Result<()> {
1565        Err(ErrorKind::Message(
1566            "unit enum variants are represented as strings, not groups".to_string(),
1567        ).into())
1568    }
1569
1570    fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>
1571    where
1572        T: de::DeserializeSeed<'de>,
1573    {
1574        seed.deserialize(self.value)
1575    }
1576
1577    fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value>
1578    where
1579        V: Visitor<'de>,
1580    {
1581        match self.value {
1582            Value::Array(arr) | Value::List(arr) => {
1583                visitor.visit_seq(SeqDeserializer::new(arr.into_iter()))
1584            }
1585            _ => Err(ErrorKind::Message(
1586                "expected a list for a tuple enum variant".to_string(),
1587            ).into()),
1588        }
1589    }
1590
1591    fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
1592    where
1593        V: Visitor<'de>,
1594    {
1595        match self.value {
1596            Value::Group(map) => visitor.visit_map(MapDeserializer::new(map.into_iter())),
1597            _ => Err(ErrorKind::Message(
1598                "expected a group for a struct enum variant".to_string(),
1599            ).into()),
1600        }
1601    }
1602}
1603
1604struct SeqDeserializer {
1605    iter: std::vec::IntoIter<Value>,
1606}
1607
1608impl SeqDeserializer {
1609    fn new(iter: std::vec::IntoIter<Value>) -> Self {
1610        SeqDeserializer { iter }
1611    }
1612}
1613
1614impl<'de> SeqAccess<'de> for SeqDeserializer {
1615    type Error = Error;
1616
1617    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
1618    where
1619        T: de::DeserializeSeed<'de>,
1620    {
1621        match self.iter.next() {
1622            Some(value) => seed.deserialize(value).map(Some),
1623            None => Ok(None),
1624        }
1625    }
1626}
1627
1628struct MapDeserializer {
1629    iter: indexmap::map::IntoIter<String, Value>,
1630    value: Option<Value>,
1631}
1632
1633impl MapDeserializer {
1634    fn new(iter: indexmap::map::IntoIter<String, Value>) -> Self {
1635        MapDeserializer { iter, value: None }
1636    }
1637}
1638
1639impl<'de> MapAccess<'de> for MapDeserializer {
1640    type Error = Error;
1641
1642    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
1643    where
1644        K: de::DeserializeSeed<'de>,
1645    {
1646        match self.iter.next() {
1647            Some((key, value)) => {
1648                self.value = Some(value);
1649                seed.deserialize(Value::String(key)).map(Some)
1650            }
1651            None => Ok(None),
1652        }
1653    }
1654
1655    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
1656    where
1657        V: de::DeserializeSeed<'de>,
1658    {
1659        match self.value.take() {
1660            Some(value) => seed.deserialize(value),
1661            None => Err(ErrorKind::Message("value is missing".to_string()).into()),
1662        }
1663    }
1664}
1665
1666/// Maximum `@include` nesting depth, per the libconfig manual (section 2.11):
1667/// "Include files may be nested to a maximum of 10 levels."
1668const MAX_INCLUDE_DEPTH: usize = 10;
1669
1670/// Expands `@include "path"` directives by inlining the named files, per
1671/// section 2.11 of the libconfig manual. Directives must appear on their own
1672/// line and are recognized outside of block comments. Relative paths are
1673/// resolved against `include_dir` if given, otherwise against the current
1674/// working directory.
1675/// A resolver for `@include` directives: maps the directive's path string
1676/// to the ordered list of files whose contents are inlined (the equivalent
1677/// of the C API's `config_set_include_func`).
1678type IncludeResolver<'a> = dyn Fn(&str) -> Result<Vec<std::path::PathBuf>> + 'a;
1679
1680/// The default resolver, per the manual: prepend the include directory (if
1681/// any) to relative paths, then treat the result as a single literal file
1682/// path.
1683fn default_include_resolver(
1684    include_dir: Option<&Path>,
1685) -> impl Fn(&str) -> Result<Vec<std::path::PathBuf>> + '_ {
1686    move |path: &str| {
1687        let mut resolved = std::path::PathBuf::from(path);
1688        if resolved.is_relative() {
1689            if let Some(dir) = include_dir {
1690                resolved = dir.join(resolved);
1691            }
1692        }
1693        Ok(vec![resolved])
1694    }
1695}
1696
1697/// Expands `@include "path"` directives by inlining the files chosen by
1698/// `resolver`, per section 2.11 of the libconfig manual. Directives must
1699/// appear on their own line and are recognized outside of block comments.
1700fn resolve_includes(input: &str, resolver: &IncludeResolver, depth: usize) -> Result<String> {
1701    if depth > MAX_INCLUDE_DEPTH {
1702        return Err(ErrorKind::Include(format!(
1703            "include files nested too deeply (maximum {} levels)",
1704            MAX_INCLUDE_DEPTH
1705        ))
1706        .into());
1707    }
1708
1709    let mut out = String::with_capacity(input.len());
1710    let mut in_block_comment = false;
1711
1712    for line in input.lines() {
1713        let trimmed = line.trim_start();
1714        if !in_block_comment && trimmed.starts_with("@include") {
1715            let path_str = parse_include_path(&trimmed["@include".len()..])?;
1716            for path in resolver(&path_str)? {
1717                let contents = std::fs::read_to_string(&path).map_err(|e| {
1718                    ErrorKind::Include(format!(
1719                        "failed to read include file '{}': {}",
1720                        path.display(),
1721                        e
1722                    ))
1723                })?;
1724                out.push_str(&resolve_includes(&contents, resolver, depth + 1)?);
1725                out.push('\n');
1726            }
1727        } else {
1728            update_block_comment_state(line, &mut in_block_comment);
1729            out.push_str(line);
1730            out.push('\n');
1731        }
1732    }
1733
1734    Ok(out)
1735}
1736
1737/// Extracts the quoted path from the remainder of an `@include` line.
1738/// Backslashes and double quotes in the path are escaped as `\\` and `\"`.
1739fn parse_include_path(rest: &str) -> Result<String> {
1740    let rest = rest.trim_start();
1741    let mut chars = rest.chars();
1742    if chars.next() != Some('"') {
1743        return Err(ErrorKind::Include(
1744            "expected a quoted path after @include".to_string(),
1745        ).into());
1746    }
1747
1748    let mut path = String::new();
1749    let mut escaped = false;
1750    let mut closed = false;
1751    for ch in chars.by_ref() {
1752        if escaped {
1753            match ch {
1754                '\\' => path.push('\\'),
1755                '"' => path.push('"'),
1756                _ => {
1757                    return Err(ErrorKind::Include(format!(
1758                        "invalid escape '\\{}' in @include path",
1759                        ch
1760                    )).into());
1761                }
1762            }
1763            escaped = false;
1764        } else if ch == '\\' {
1765            escaped = true;
1766        } else if ch == '"' {
1767            closed = true;
1768            break;
1769        } else {
1770            path.push(ch);
1771        }
1772    }
1773    if !closed {
1774        return Err(ErrorKind::Include(
1775            "unterminated path in @include directive".to_string(),
1776        ).into());
1777    }
1778
1779    // The directive must be on its own line; only trailing comments allowed
1780    let remainder = chars.as_str().trim_start();
1781    if !(remainder.is_empty() || remainder.starts_with('#') || remainder.starts_with("//")) {
1782        return Err(ErrorKind::Include(
1783            "unexpected content after @include directive".to_string(),
1784        ).into());
1785    }
1786
1787    Ok(path)
1788}
1789
1790/// Tracks whether a `/* ... */` comment remains open at the end of `line`,
1791/// ignoring comment markers inside quoted strings and line comments.
1792fn update_block_comment_state(line: &str, in_block_comment: &mut bool) {
1793    let bytes = line.as_bytes();
1794    let mut i = 0;
1795    let mut in_string = false;
1796    while i < bytes.len() {
1797        if *in_block_comment {
1798            if bytes[i] == b'*' && bytes.get(i + 1) == Some(&b'/') {
1799                *in_block_comment = false;
1800                i += 2;
1801            } else {
1802                i += 1;
1803            }
1804        } else if in_string {
1805            match bytes[i] {
1806                b'\\' => i += 2,
1807                b'"' => {
1808                    in_string = false;
1809                    i += 1;
1810                }
1811                _ => i += 1,
1812            }
1813        } else {
1814            match bytes[i] {
1815                b'"' => {
1816                    in_string = true;
1817                    i += 1;
1818                }
1819                b'#' => break,
1820                b'/' if bytes.get(i + 1) == Some(&b'/') => break,
1821                b'/' if bytes.get(i + 1) == Some(&b'*') => {
1822                    *in_block_comment = true;
1823                    i += 2;
1824                }
1825                _ => i += 1,
1826            }
1827        }
1828    }
1829}
1830
1831/// A libconfig configuration document.
1832///
1833/// `Config` wraps the root `Value::Group` of a document. Per the grammar,
1834/// a configuration is a list of settings — the top level is never wrapped
1835/// in braces; braces delimit nested groups only.
1836///
1837/// Create an empty config with [`Config::new`], parse from a string with
1838/// [`Config::from_str`], or wrap an existing group `Value` with `Config::try_from(value)`.
1839///
1840/// `Config` derefs to `Value`, so all `Value` methods (indexing, lookup,
1841/// set, remove) work directly on a `Config`.
1842///
1843/// # Examples
1844///
1845/// ```
1846/// use libconfig::{Config, Value};
1847///
1848/// let mut config = Config::new();
1849/// config.set("name", Value::String("test".into()));
1850/// config.set("version", Value::Integer(42));
1851///
1852/// assert_eq!(config["name"], "test");
1853/// assert_eq!(config["version"], 42);
1854///
1855/// let output = config.to_string().unwrap();
1856/// assert_eq!(output, "name = \"test\";\nversion = 42;");
1857/// ```
1858#[derive(Debug, Clone, PartialEq)]
1859pub struct Config {
1860    /// The root value; always a `Value::Group` when constructed through
1861    /// this type's constructors ([`Config::to_string`] re-checks)
1862    value: Value,
1863}
1864
1865impl Config {
1866    /// Create a new empty Config.
1867    pub fn new() -> Self {
1868        Config {
1869            value: Value::Group(Map::new()),
1870        }
1871    }
1872
1873    /// Consumes the config and returns its root value.
1874    pub fn into_value(self) -> Value {
1875        self.value
1876    }
1877
1878    /// Parse a libconfig string into a `Config`.
1879    ///
1880    /// The document root must be a setting list (or empty); wrapping the
1881    /// top level in braces is a syntax error per the grammar.
1882    ///
1883    /// # Examples
1884    ///
1885    /// ```
1886    /// use libconfig::Config;
1887    ///
1888    /// let config = Config::from_str("a = 1; b = 2;").unwrap();
1889    /// assert_eq!(config["a"], 1);
1890    ///
1891    /// // Braces around the top level are not part of the grammar
1892    /// assert!(Config::from_str("{ a = 1; }").is_err());
1893    /// ```
1894    #[allow(clippy::should_implement_trait)] // std::str::FromStr is also implemented
1895    pub fn from_str(s: &str) -> Result<Config> {
1896        Config::parse(s, &default_include_resolver(None))
1897    }
1898
1899    /// Like [`Config::from_str`], but relative paths in `@include` directives
1900    /// are resolved against `include_dir` (the equivalent of the C library's
1901    /// `config_set_include_dir()`).
1902    pub fn from_str_with_include_dir<P: AsRef<Path>>(s: &str, include_dir: P) -> Result<Config> {
1903        Config::parse(s, &default_include_resolver(Some(include_dir.as_ref())))
1904    }
1905
1906    /// Like [`Config::from_str`], but `@include` directives are resolved by
1907    /// `resolver`: it receives the directive's path string and returns the
1908    /// ordered list of files to inline (the equivalent of the C library's
1909    /// `config_set_include_func()`). This enables wildcard expansion,
1910    /// variable substitution, or search paths:
1911    ///
1912    /// ```no_run
1913    /// use libconfig::Config;
1914    ///
1915    /// // Resolve includes against a search path
1916    /// let dirs = ["/etc/myapp", "/usr/share/myapp"];
1917    /// let config = Config::from_str_with_include_fn(
1918    ///     "@include \"extra.cfg\"\n",
1919    ///     |path| {
1920    ///         dirs.iter()
1921    ///             .map(|d| std::path::Path::new(d).join(path))
1922    ///             .find(|p| p.exists())
1923    ///             .map(|p| vec![p])
1924    ///             .ok_or_else(|| {
1925    ///                 libconfig::ErrorKind::Include(format!("'{path}' not found")).into()
1926    ///             })
1927    ///     },
1928    /// );
1929    /// ```
1930    ///
1931    /// Files returned by the resolver are scanned for nested `@include`
1932    /// directives (resolved by the same function), up to the manual's limit
1933    /// of 10 levels.
1934    pub fn from_str_with_include_fn<F>(s: &str, resolver: F) -> Result<Config>
1935    where
1936        F: Fn(&str) -> Result<Vec<std::path::PathBuf>>,
1937    {
1938        Config::parse(s, &resolver)
1939    }
1940
1941    fn parse(s: &str, resolver: &IncludeResolver) -> Result<Config> {
1942        let processed;
1943        let text: &str = if s.contains("@include") {
1944            processed = resolve_includes(s, resolver, 0)?;
1945            &processed
1946        } else {
1947            s
1948        };
1949        let value: Value = crate::from_str(text)?;
1950        // A configuration document is a group of settings (grammar:
1951        // <configuration> ::= <setting-list> | <empty>); bare scalar values
1952        // are only valid through the serde-level from_str
1953        if !value.is_group() {
1954            return Err(ErrorKind::ExpectedGroup.into());
1955        }
1956        Ok(Config { value })
1957    }
1958
1959    /// Read and parse a libconfig file into a `Config`, automatically
1960    /// detecting whether the input uses an implicit or explicit group.
1961    ///
1962    /// `@include` directives are processed while reading; as in the C
1963    /// library, relative include paths are resolved against the current
1964    /// working directory unless an include directory is set with
1965    /// [`Config::from_file_with_include_dir`].
1966    ///
1967    /// # Examples
1968    ///
1969    /// ```no_run
1970    /// use libconfig::Config;
1971    ///
1972    /// let config = Config::from_file("settings.cfg").unwrap();
1973    /// println!("{}", config["name"]);
1974    /// ```
1975    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Config> {
1976        let contents = std::fs::read_to_string(path)
1977            .map_err(|e| ErrorKind::Message(format!("failed to read file: {}", e)))?;
1978        Config::from_str(&contents)
1979    }
1980
1981    /// Like [`Config::from_file`], but relative paths in `@include` directives
1982    /// are resolved against `include_dir`.
1983    pub fn from_file_with_include_dir<P: AsRef<Path>, Q: AsRef<Path>>(
1984        path: P,
1985        include_dir: Q,
1986    ) -> Result<Config> {
1987        let contents = std::fs::read_to_string(path)
1988            .map_err(|e| ErrorKind::Message(format!("failed to read file: {}", e)))?;
1989        Config::from_str_with_include_dir(&contents, include_dir)
1990    }
1991
1992    /// Like [`Config::from_file`], but `@include` directives are resolved by
1993    /// `resolver` (see [`Config::from_str_with_include_fn`]).
1994    pub fn from_file_with_include_fn<P: AsRef<Path>, F>(path: P, resolver: F) -> Result<Config>
1995    where
1996        F: Fn(&str) -> Result<Vec<std::path::PathBuf>>,
1997    {
1998        let contents = std::fs::read_to_string(path)
1999            .map_err(|e| ErrorKind::Message(format!("failed to read file: {}", e)))?;
2000        Config::parse(&contents, &resolver)
2001    }
2002
2003    /// Serialize this config to a libconfig string. The top level is an
2004    /// implicit setting list; nested groups are brace-delimited.
2005    ///
2006    /// # Errors
2007    ///
2008    /// Returns an error if the root is not a group (possible via `DerefMut`
2009    /// mutation), if a setting name is not grammar-valid, or if a value has
2010    /// no libconfig representation (e.g. a non-finite float).
2011    pub fn to_string(&self) -> Result<String> {
2012        self.to_string_with_options(&crate::WriteOptions::default())
2013    }
2014
2015    /// Like [`Config::to_string`], but formatted per the given
2016    /// [`crate::WriteOptions`].
2017    pub fn to_string_with_options(&self, options: &crate::WriteOptions) -> Result<String> {
2018        if !self.value.is_group() {
2019            return Err(ErrorKind::ExpectedGroup.into());
2020        }
2021        crate::ser::to_string_with_options(&self.value, options)
2022    }
2023
2024    /// Serialize this config and write it to the file at `path`, replacing
2025    /// any existing contents. A trailing newline is appended.
2026    ///
2027    /// # Errors
2028    ///
2029    /// Returns an error if serialization fails (see [`Config::to_string`])
2030    /// or the file cannot be written ([`ErrorKind::Io`]).
2031    pub fn to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
2032        self.to_file_with_options(path, &crate::WriteOptions::default())
2033    }
2034
2035    /// Like [`Config::to_file`], but formatted per the given
2036    /// [`crate::WriteOptions`].
2037    pub fn to_file_with_options<P: AsRef<Path>>(
2038        &self,
2039        path: P,
2040        options: &crate::WriteOptions,
2041    ) -> Result<()> {
2042        let mut contents = self.to_string_with_options(options)?;
2043        if !contents.is_empty() {
2044            contents.push('\n');
2045        }
2046        std::fs::write(path, contents).map_err(|e| ErrorKind::Io(e.to_string()))?;
2047        Ok(())
2048    }
2049}
2050
2051impl Default for Config {
2052    fn default() -> Self {
2053        Config::new()
2054    }
2055}
2056
2057impl std::str::FromStr for Config {
2058    type Err = Error;
2059
2060    fn from_str(s: &str) -> Result<Config> {
2061        Config::from_str(s)
2062    }
2063}
2064
2065impl TryFrom<Value> for Config {
2066    type Error = Error;
2067
2068    /// Wraps a `Value` as a configuration document.
2069    ///
2070    /// # Errors
2071    ///
2072    /// Returns [`ErrorKind::ExpectedGroup`] if the value is not a
2073    /// `Value::Group` — a configuration document is a group of settings.
2074    fn try_from(value: Value) -> Result<Config> {
2075        if value.is_group() {
2076            Ok(Config { value })
2077        } else {
2078            Err(ErrorKind::ExpectedGroup.into())
2079        }
2080    }
2081}
2082
2083impl<'de> Deserialize<'de> for Config {
2084    fn deserialize<D>(deserializer: D) -> std::result::Result<Config, D::Error>
2085    where
2086        D: Deserializer<'de>,
2087    {
2088        let value = Value::deserialize(deserializer)?;
2089        if value.is_group() {
2090            Ok(Config { value })
2091        } else {
2092            Err(de::Error::custom(
2093                "configuration root must be a group of settings",
2094            ))
2095        }
2096    }
2097}
2098
2099impl std::ops::Deref for Config {
2100    type Target = Value;
2101
2102    fn deref(&self) -> &Value {
2103        &self.value
2104    }
2105}
2106
2107impl std::ops::DerefMut for Config {
2108    fn deref_mut(&mut self) -> &mut Value {
2109        &mut self.value
2110    }
2111}
2112
2113impl fmt::Display for Config {
2114    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2115        match self.to_string() {
2116            Ok(s) => write!(f, "{}", s),
2117            Err(e) => write!(f, "<error: {}>", e),
2118        }
2119    }
2120}
2121
2122impl Serialize for Config {
2123    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
2124    where
2125        S: Serializer,
2126    {
2127        self.value.serialize(serializer)
2128    }
2129}
2130
2131#[cfg(test)]
2132mod tests {
2133    use super::*;
2134    use crate::from_str;
2135
2136    #[test]
2137    fn test_value_types() {
2138        let v = Value::Bool(true);
2139        assert!(v.is_bool());
2140        assert_eq!(v.as_bool(), Some(true));
2141
2142        let v = Value::Integer(42);
2143        assert!(v.is_i32());
2144        assert_eq!(v.as_i32(), Some(42));
2145
2146        let v = Value::Float(2.25);
2147        assert!(v.is_float());
2148        assert_eq!(v.as_f64(), Some(2.25));
2149
2150        let v = Value::String("hello".to_string());
2151        assert!(v.is_string());
2152        assert_eq!(v.as_str(), Some("hello"));
2153    }
2154
2155    #[test]
2156    fn test_value_indexing() {
2157        let config = r#"
2158                name = "Test";
2159                count = 42;
2160                items = [1, 2, 3];
2161        "#;
2162
2163        let v: Value = from_str(config).unwrap();
2164        assert_eq!(v["name"], "Test");
2165        assert_eq!(v["count"], 42);
2166        assert_eq!(v["items"][0], 1);
2167        assert_eq!(v["items"][1], 2);
2168    }
2169
2170    #[test]
2171    fn test_value_lookup() {
2172        let config = r#"
2173                application = {
2174                    window = {
2175                        title = "My App";
2176                        size = { w = 640; h = 480; };
2177                    };
2178                };
2179        "#;
2180
2181        let v: Value = from_str(config).unwrap();
2182        assert_eq!(
2183            v.lookup("application.window.title")
2184                .and_then(|v| v.as_str()),
2185            Some("My App")
2186        );
2187        assert_eq!(
2188            v.lookup("application.window.size")
2189                .and_then(|v| v.get("w"))
2190                .and_then(|v| v.as_i32()),
2191            Some(640)
2192        );
2193    }
2194
2195    #[test]
2196    fn test_value_lookup_with_array_index() {
2197        let config = r#"
2198                name = "Test";
2199                items = [10, 20, 30];
2200                nested = {
2201                    ports = [80, 443];
2202                };
2203        "#;
2204
2205        let v: Value = from_str(config).unwrap();
2206
2207        // Direct array index
2208        assert_eq!(v.lookup("items.0").and_then(|v| v.as_i32()), Some(10));
2209        assert_eq!(v.lookup("items.1").and_then(|v| v.as_i32()), Some(20));
2210        assert_eq!(v.lookup("items.2").and_then(|v| v.as_i32()), Some(30));
2211
2212        // Nested group then array index
2213        assert_eq!(
2214            v.lookup("nested.ports.0").and_then(|v| v.as_i32()),
2215            Some(80)
2216        );
2217        assert_eq!(
2218            v.lookup("nested.ports.1").and_then(|v| v.as_i32()),
2219            Some(443)
2220        );
2221
2222        // Out of bounds returns None
2223        assert!(v.lookup("items.5").is_none());
2224
2225        // Index on non-array returns None
2226        assert!(v.lookup("name.0").is_none());
2227
2228        // Index via [] operator
2229        assert_eq!(v["items.0"], 10);
2230        assert_eq!(v["nested.ports.1"], 443);
2231    }
2232
2233    #[test]
2234    fn test_value_comparison() {
2235        let v = Value::Bool(true);
2236        assert!(v == true);
2237
2238        let v = Value::Integer(42);
2239        assert!(v == 42);
2240
2241        let v = Value::String("hello".to_string());
2242        assert!(v == "hello");
2243    }
2244
2245    #[test]
2246    fn test_value_round_trip() {
2247        let config = r#"
2248                name = "Test";
2249                version = 1;
2250                enabled = true;
2251                ratio = 2.25;
2252                items = [1, 2, 3];
2253        "#;
2254
2255        let v: Value = from_str(config).unwrap();
2256        let serialized = crate::to_string(&v).unwrap();
2257        let v2: Value = from_str(&serialized).unwrap();
2258
2259        assert_eq!(v["name"], v2["name"]);
2260        assert_eq!(v["version"], v2["version"]);
2261        assert_eq!(v["enabled"], v2["enabled"]);
2262    }
2263
2264    // === Path parsing tests ===
2265
2266    #[test]
2267    fn test_parse_path_dotted_keys() {
2268        let segs = parse_path("a.b.c").unwrap();
2269        assert_eq!(
2270            segs,
2271            vec![
2272                PathSegment::Key("a"),
2273                PathSegment::Key("b"),
2274                PathSegment::Key("c"),
2275            ]
2276        );
2277    }
2278
2279    #[test]
2280    fn test_parse_path_bracket_index() {
2281        let segs = parse_path("items.[0].title").unwrap();
2282        assert_eq!(
2283            segs,
2284            vec![
2285                PathSegment::Key("items"),
2286                PathSegment::Index(0),
2287                PathSegment::Key("title"),
2288            ]
2289        );
2290    }
2291
2292    #[test]
2293    fn test_parse_path_bare_numeric() {
2294        let segs = parse_path("items.0").unwrap();
2295        assert_eq!(segs, vec![PathSegment::Key("items"), PathSegment::Index(0)]);
2296    }
2297
2298    #[test]
2299    fn test_parse_path_single_segment() {
2300        assert_eq!(
2301            parse_path("version").unwrap(),
2302            vec![PathSegment::Key("version")]
2303        );
2304    }
2305
2306    #[test]
2307    fn test_parse_path_invalid() {
2308        assert!(parse_path("").is_none());
2309        assert!(parse_path("a..b").is_none());
2310        assert!(parse_path(".a").is_none());
2311        assert!(parse_path("a.").is_none());
2312        assert!(parse_path("items.[abc]").is_none());
2313        assert!(parse_path("items.[-1]").is_none());
2314        assert!(parse_path("items.[]").is_none());
2315    }
2316
2317    // === Bracket notation lookup tests ===
2318
2319    #[test]
2320    fn test_lookup_bracket_notation() {
2321        let config = r#"
2322                books = (
2323                    { title = "Treasure Island"; price = 29; },
2324                    { title = "Snow Crash"; price = 9; }
2325                );
2326        "#;
2327        let v: Value = from_str(config).unwrap();
2328
2329        // Bracket notation
2330        assert_eq!(
2331            v.lookup("books.[0].title").and_then(|v| v.as_str()),
2332            Some("Treasure Island")
2333        );
2334        assert_eq!(
2335            v.lookup("books.[1].price").and_then(|v| v.as_i32()),
2336            Some(9)
2337        );
2338
2339        // Bare numeric still works
2340        assert_eq!(
2341            v.lookup("books.0.title").and_then(|v| v.as_str()),
2342            Some("Treasure Island")
2343        );
2344    }
2345
2346    #[test]
2347    fn test_lookup_mut_bracket_notation() {
2348        let config = r#"items = [10, 20, 30];"#;
2349        let mut v: Value = from_str(config).unwrap();
2350
2351        *v.lookup_mut("items.[1]").unwrap() = Value::Integer(99);
2352        assert_eq!(v.lookup("items.[1]").unwrap().as_i32(), Some(99));
2353        assert_eq!(v.lookup("items.1").unwrap().as_i32(), Some(99));
2354    }
2355
2356    // === Set tests ===
2357
2358    #[test]
2359    fn test_set_existing_key() {
2360        let mut v: Value = from_str(r#"name = "old";"#).unwrap();
2361        assert!(v.set("name", Value::String("new".to_string())).is_ok());
2362        assert_eq!(v.lookup("name").unwrap().as_str(), Some("new"));
2363    }
2364
2365    #[test]
2366    fn test_set_new_key() {
2367        let mut v: Value = from_str(r#"a = 1;"#).unwrap();
2368        assert!(v.set("b", Value::Integer(2)).is_ok());
2369        assert_eq!(v.lookup("b").unwrap().as_i32(), Some(2));
2370    }
2371
2372    #[test]
2373    fn test_set_creates_intermediate_groups() {
2374        let mut v = Value::Group(IndexMap::new());
2375        assert!(v.set("a.b.c", Value::Integer(42)).is_ok());
2376        assert_eq!(v.lookup("a.b.c").unwrap().as_i32(), Some(42));
2377        assert!(v.lookup("a").unwrap().is_group());
2378        assert!(v.lookup("a.b").unwrap().is_group());
2379    }
2380
2381    #[test]
2382    fn test_set_array_element() {
2383        let mut v: Value = from_str(r#"items = [1, 2, 3];"#).unwrap();
2384        assert!(v.set("items.[0]", Value::Integer(99)).is_ok());
2385        assert_eq!(v.lookup("items.[0]").unwrap().as_i32(), Some(99));
2386    }
2387
2388    #[test]
2389    fn test_set_array_out_of_bounds() {
2390        let mut v: Value = from_str(r#"items = [1, 2];"#).unwrap();
2391        assert!(v.set("items.[5]", Value::Integer(99)).is_err());
2392    }
2393
2394    #[test]
2395    fn test_set_on_scalar_fails() {
2396        let mut v: Value = from_str(r#"name = "hello";"#).unwrap();
2397        assert!(v.set("name.child", Value::Integer(1)).is_err());
2398    }
2399
2400    #[test]
2401    fn test_set_deeply_nested() {
2402        let mut v = Value::Group(IndexMap::new());
2403        assert!(v.set("level1.level2.level3.level4", Value::Bool(true)).is_ok());
2404        assert_eq!(
2405            v.lookup("level1.level2.level3.level4").unwrap().as_bool(),
2406            Some(true)
2407        );
2408    }
2409
2410    #[test]
2411    fn test_set_into_list_element_group() {
2412        let config = r#"
2413                books = (
2414                    { title = "Old Title"; }
2415                );
2416        "#;
2417        let mut v: Value = from_str(config).unwrap();
2418        assert!(v.set("books.[0].title", Value::String("New Title".to_string())).is_ok());
2419        assert_eq!(
2420            v.lookup("books.[0].title").unwrap().as_str(),
2421            Some("New Title")
2422        );
2423    }
2424
2425    // === Remove tests ===
2426
2427    #[test]
2428    fn test_remove_group_key() {
2429        let mut v: Value = from_str(r#"a = 1; b = 2; c = 3;"#).unwrap();
2430        let removed = v.remove("b");
2431        assert_eq!(removed, Some(Value::Integer(2)));
2432        assert!(v.lookup("b").is_none());
2433        assert_eq!(v.lookup("a").unwrap().as_i32(), Some(1));
2434        assert_eq!(v.lookup("c").unwrap().as_i32(), Some(3));
2435    }
2436
2437    #[test]
2438    fn test_remove_array_element() {
2439        let mut v: Value = from_str(r#"items = [10, 20, 30];"#).unwrap();
2440        let removed = v.remove("items.[1]");
2441        assert_eq!(removed, Some(Value::Integer(20)));
2442        // Elements shift: [10, 30]
2443        assert_eq!(v.lookup("items.[0]").unwrap().as_i32(), Some(10));
2444        assert_eq!(v.lookup("items.[1]").unwrap().as_i32(), Some(30));
2445        assert!(v.lookup("items.[2]").is_none());
2446    }
2447
2448    #[test]
2449    fn test_remove_nested() {
2450        let config = r#"
2451                database = {
2452                    host = "localhost";
2453                    port = 5432;
2454                };
2455        "#;
2456        let mut v: Value = from_str(config).unwrap();
2457        let removed = v.remove("database.host");
2458        assert_eq!(removed, Some(Value::String("localhost".to_string())));
2459        assert!(v.lookup("database.host").is_none());
2460        assert_eq!(v.lookup("database.port").unwrap().as_i32(), Some(5432));
2461    }
2462
2463    #[test]
2464    fn test_remove_nonexistent() {
2465        let mut v: Value = from_str(r#"a = 1;"#).unwrap();
2466        assert_eq!(v.remove("z"), None);
2467        assert_eq!(v.remove("a.b"), None);
2468    }
2469
2470    #[test]
2471    fn test_remove_index_out_of_bounds() {
2472        let mut v: Value = from_str(r#"items = [1, 2];"#).unwrap();
2473        assert_eq!(v.remove("items.[5]"), None);
2474    }
2475
2476    #[test]
2477    fn test_remove_entire_subtree() {
2478        let config = r#"
2479                app = {
2480                    window = { title = "hi"; };
2481                    debug = false;
2482                };
2483        "#;
2484        let mut v: Value = from_str(config).unwrap();
2485        let removed = v.remove("app.window");
2486        assert!(removed.unwrap().is_group());
2487        assert!(v.lookup("app.window").is_none());
2488        assert_eq!(v.lookup("app.debug").unwrap().as_bool(), Some(false));
2489    }
2490
2491    #[test]
2492    fn test_remove_bare_numeric_index() {
2493        let mut v: Value = from_str(r#"items = [10, 20, 30];"#).unwrap();
2494        let removed = v.remove("items.0");
2495        assert_eq!(removed, Some(Value::Integer(10)));
2496        assert_eq!(v.lookup("items.0").unwrap().as_i32(), Some(20));
2497    }
2498
2499    // === Ordering and format preservation tests ===
2500
2501    #[test]
2502    fn test_ordering_preserved_in_round_trip() {
2503        let input = r#"
2504  first = 1;
2505  second = 2;
2506  third = 3;
2507  fourth = 4;
2508"#;
2509        let v: Value = from_str(input).unwrap();
2510        let output = Config::try_from(v.clone()).unwrap().to_string().unwrap();
2511
2512        // Parse both and verify key order
2513        let v_output: Value = from_str(&output).unwrap();
2514        if let Value::Group(map) = v {
2515            if let Value::Group(map_output) = v_output {
2516                let keys1: Vec<&String> = map.keys().collect();
2517                let keys2: Vec<&String> = map_output.keys().collect();
2518                assert_eq!(keys1, keys2);
2519                assert_eq!(keys1, vec!["first", "second", "third", "fourth"]);
2520            }
2521        }
2522    }
2523
2524    #[test]
2525    fn test_config_implicit_no_outer_braces() {
2526        let input = "a = 1;\nb = 2;\nc = 3;";
2527        let v: Value = from_str(input).unwrap();
2528
2529        let output = Config::try_from(v).unwrap().to_string().unwrap();
2530
2531        // Implicit config omits outer braces
2532        assert!(!output.trim_start().starts_with('{'));
2533        assert!(!output.trim_end().ends_with('}'));
2534
2535        // Should contain the keys
2536        assert!(output.contains("a = 1"));
2537        assert!(output.contains("b = 2"));
2538        assert!(output.contains("c = 3"));
2539    }
2540
2541    #[test]
2542    fn test_nested_groups_have_braces() {
2543        let input = r#"
2544  outer = 1;
2545  nested = {
2546    inner = 2;
2547  };
2548"#;
2549        let v: Value = from_str(input).unwrap();
2550        let output = Config::try_from(v).unwrap().to_string().unwrap();
2551
2552        // Outer group should not have braces (implicit format)
2553        assert!(!output.trim_start().starts_with('{'));
2554
2555        // Nested groups should still have braces
2556        assert!(output.contains("nested = {"));
2557
2558        // Parse and verify structure
2559        let v2: Value = from_str(&output).unwrap();
2560        assert_eq!(v2["outer"], 1);
2561        assert_eq!(v2["nested.inner"], 2);
2562    }
2563
2564    #[test]
2565    fn test_ordering_with_mixed_types() {
2566        let input = r#"
2567  string_first = "hello";
2568  int_second = 42;
2569  bool_third = true;
2570  float_fourth = 2.25;
2571  array_fifth = [1, 2, 3];
2572"#;
2573        let v: Value = from_str(input).unwrap();
2574
2575        if let Value::Group(map) = &v {
2576            let keys: Vec<&String> = map.keys().collect();
2577            assert_eq!(
2578                keys,
2579                vec![
2580                    "string_first",
2581                    "int_second",
2582                    "bool_third",
2583                    "float_fourth",
2584                    "array_fifth"
2585                ]
2586            );
2587        }
2588
2589        let output = Config::try_from(v).unwrap().to_string().unwrap();
2590        let v2: Value = from_str(&output).unwrap();
2591
2592        // Verify order is preserved after round-trip
2593        if let Value::Group(map) = &v2 {
2594            let keys: Vec<&String> = map.keys().collect();
2595            assert_eq!(
2596                keys,
2597                vec![
2598                    "string_first",
2599                    "int_second",
2600                    "bool_third",
2601                    "float_fourth",
2602                    "array_fifth"
2603                ]
2604            );
2605        }
2606    }
2607
2608    #[test]
2609    fn test_adding_new_keys_appends_to_end() {
2610        let input = "a = 1;\nb = 2;";
2611        let mut v: Value = from_str(input).unwrap();
2612
2613        // Add a new key
2614        v.set("c", Value::Integer(3)).unwrap();
2615
2616        if let Value::Group(map) = &v {
2617            let keys: Vec<&String> = map.keys().collect();
2618            assert_eq!(keys, vec!["a", "b", "c"]);
2619        }
2620
2621        let output = Config::try_from(v).unwrap().to_string().unwrap();
2622
2623        // Verify new key appears at the end
2624        let a_pos = output.find("a = 1").unwrap();
2625        let b_pos = output.find("b = 2").unwrap();
2626        let c_pos = output.find("c = 3").unwrap();
2627
2628        assert!(a_pos < b_pos);
2629        assert!(b_pos < c_pos);
2630    }
2631
2632    #[test]
2633    fn test_implicit_group_round_trip_exact() {
2634        let input = "name = \"test\";\nversion = 42;";
2635        let v: Value = from_str(input).unwrap();
2636        let output = Config::try_from(v).unwrap().to_string().unwrap();
2637
2638        // Output should match input exactly
2639        assert_eq!(output, input);
2640
2641        // Round-trip should be stable
2642        let v2: Value = from_str(&output).unwrap();
2643        let output2 = Config::try_from(v2).unwrap().to_string().unwrap();
2644        assert_eq!(output, output2);
2645    }
2646
2647    #[test]
2648    fn test_nested_indentation() {
2649        let input = r#"
2650  outer = 1;
2651  nested = {
2652    inner = 2;
2653    deep = {
2654      value = 3;
2655    };
2656  };
2657  another = 4;
2658"#;
2659        let v: Value = from_str(input).unwrap();
2660        let output = Config::try_from(v).unwrap().to_string().unwrap();
2661
2662        // Top-level keys have no indentation (implicit outer group)
2663        assert!(output.contains("outer = 1"));
2664        assert!(output.contains("nested = {"));
2665        // Nested keys get 2-space indentation
2666        assert!(output.contains("  inner = 2"));
2667        assert!(output.contains("  deep = {"));
2668        // Deeply nested keys get 4-space indentation
2669        assert!(output.contains("    value = 3"));
2670
2671        // Verify it can be parsed back
2672        let v2: Value = from_str(&output).unwrap();
2673        assert_eq!(v2["outer"], 1);
2674        assert_eq!(v2["nested.inner"], 2);
2675        assert_eq!(v2["nested.deep.value"], 3);
2676        assert_eq!(v2["another"], 4);
2677    }
2678
2679    #[test]
2680    fn test_implicit_group_with_nested_explicit_groups() {
2681        let input = r#"top_level = 1;
2682nested = {
2683  inner = 2;
2684};
2685also_top = 3;"#;
2686        let v: Value = from_str(input).unwrap();
2687        let output = Config::try_from(v).unwrap().to_string().unwrap();
2688
2689        // Top level should not have braces (implicit format)
2690        assert!(!output.trim().starts_with('{'));
2691
2692        // Nested group should have braces
2693        assert!(output.contains("nested = {"));
2694        assert!(output.contains("  inner = 2"));
2695
2696        // Parse and verify
2697        let v2: Value = from_str(&output).unwrap();
2698        assert_eq!(v2["top_level"], 1);
2699        assert_eq!(v2["nested.inner"], 2);
2700        assert_eq!(v2["also_top"], 3);
2701    }
2702
2703    #[test]
2704    fn test_deeply_nested_groups() {
2705        let input = r#"
2706  level1 = {
2707    level2 = {
2708      level3 = {
2709        value = 42;
2710      };
2711    };
2712  };
2713"#;
2714        let v: Value = from_str(input).unwrap();
2715        let output = Config::try_from(v).unwrap().to_string().unwrap();
2716
2717        // Top-level is implicit (no braces, no indent)
2718        // Each nested level adds 2-space indentation
2719        assert!(output.contains("level1 = {"));
2720        assert!(output.contains("  level2 = {"));
2721        assert!(output.contains("    level3 = {"));
2722        assert!(output.contains("      value = 42"));
2723    }
2724
2725    // === Config wrapper tests ===
2726
2727    #[test]
2728    fn test_config_implicit_round_trip() {
2729        let input = "name = \"test\";\nversion = 42;";
2730        let config = Config::from_str(input).unwrap();
2731
2732        assert_eq!(config["name"], "test");
2733        assert_eq!(config["version"], 42);
2734
2735        let output = config.to_string().unwrap();
2736        assert_eq!(output, input);
2737    }
2738
2739    #[test]
2740    fn test_config_deref_lookup() {
2741        let config = Config::from_str("a = 1; b = { c = 2; };").unwrap();
2742
2743        // Deref allows Value methods directly
2744        assert_eq!(config.lookup("b.c").unwrap().as_i32(), Some(2));
2745        assert!(config.is_group());
2746    }
2747
2748    #[test]
2749    fn test_config_deref_mut() {
2750        let mut config = Config::from_str("a = 1; b = 2;").unwrap();
2751
2752        config.set("c", Value::Integer(3)).unwrap();
2753        assert_eq!(config["c"], 3);
2754
2755        let output = config.to_string().unwrap();
2756        assert!(output.contains("c = 3"));
2757    }
2758
2759    #[test]
2760    fn test_config_display() {
2761        let config = Config::from_str("x = 10; y = 20;").unwrap();
2762        let displayed = format!("{}", config);
2763        assert!(displayed.contains("x = 10"));
2764        assert!(displayed.contains("y = 20"));
2765        assert!(!displayed.starts_with('{'));
2766    }
2767
2768    #[test]
2769    fn test_config_new_empty() {
2770        let mut config = Config::new();
2771        assert!(config.is_group());
2772
2773        config.set("key", Value::String("val".to_string())).unwrap();
2774        let output = config.to_string().unwrap();
2775        assert_eq!(output, "key = \"val\";");
2776    }
2777
2778    #[test]
2779    fn test_config_from_value() {
2780        let mut map = Map::new();
2781        map.insert("key".to_string(), Value::String("val".to_string()));
2782        let config = Config::try_from(Value::Group(map)).unwrap();
2783
2784        let output = config.to_string().unwrap();
2785        assert_eq!(output, "key = \"val\";");
2786    }
2787
2788    #[test]
2789    fn test_config_default() {
2790        let config = Config::default();
2791        assert!(config.is_group());
2792        assert_eq!(config.as_group().unwrap().len(), 0);
2793    }
2794
2795    #[test]
2796    fn test_config_serialize() {
2797        let config = Config::from_str("a = 1; b = 2;").unwrap();
2798        // Serde serialization writes an implicit setting list at the root,
2799        // per the grammar (no braces around the top level)
2800        let serde_output = crate::to_string(&config).unwrap();
2801        assert_eq!(serde_output, "a = 1;\nb = 2;");
2802
2803        // Config::to_string produces the same implicit format by default
2804        let format_output = config.to_string().unwrap();
2805        assert!(!format_output.starts_with('{'));
2806    }
2807
2808    #[test]
2809    fn test_serde_root_is_implicit_nested_is_braced() {
2810        use serde::Serialize;
2811
2812        #[derive(Serialize)]
2813        struct Inner {
2814            x: i32,
2815        }
2816        #[derive(Serialize)]
2817        struct Outer {
2818            a: i32,
2819            inner: Inner,
2820        }
2821
2822        let out = crate::to_string(&Outer {
2823            a: 1,
2824            inner: Inner { x: 2 },
2825        })
2826        .unwrap();
2827        // Root: no braces. Nested group: braces.
2828        assert_eq!(out, "a = 1;\ninner = {\n  x = 2;\n};");
2829
2830        // Round-trips through our own parser
2831        let v: Value = from_str(&out).unwrap();
2832        assert_eq!(v["a"], 1);
2833        assert_eq!(v["inner.x"], 2);
2834
2835        // Empty struct serializes to an empty (valid) configuration
2836        #[derive(Serialize)]
2837        struct Empty {}
2838        assert_eq!(crate::to_string(&Empty {}).unwrap(), "");
2839    }
2840
2841    #[test]
2842    fn test_top_level_braces_rejected() {
2843        // Grammar: <configuration> ::= <setting-list> | <empty>; a braced
2844        // group exists only as a nested value, never at the document root
2845        assert!(matches!(
2846            from_str::<Value>("{ a = 1; }"),
2847            Err(e) if *e.kind() == ErrorKind::TopLevelBraces
2848        ));
2849        assert!(matches!(
2850            Config::from_str("{ a = 1; }"),
2851            Err(e) if *e.kind() == ErrorKind::TopLevelBraces
2852        ));
2853        // ... including behind leading comments
2854        assert!(Config::from_str("# header\n{ a = 1; }").is_err());
2855        assert!(Config::from_str("/* block */ { a = 1; }").is_err());
2856
2857        // Nested groups keep their braces, of course
2858        let v: Value = from_str("g = { a = 1; };").unwrap();
2859        assert_eq!(v["g.a"], 1);
2860    }
2861
2862    #[test]
2863    fn test_config_requires_group_root() {
2864        // Grammar: a configuration is a setting list, not a bare value.
2865        // (The serde-level from_str still accepts scalars for primitives.)
2866        assert!(matches!(Config::from_str("42"), Err(e) if *e.kind() == ErrorKind::ExpectedGroup));
2867        assert!(matches!(Config::from_str("true"), Err(e) if *e.kind() == ErrorKind::ExpectedGroup));
2868        assert_eq!(from_str::<i32>("42").unwrap(), 42);
2869    }
2870
2871    #[test]
2872    fn test_from_value_preserves_array_vs_list() {
2873        // Regression test: the Array/List distinction previously depended on
2874        // stale thread-local state left behind by an earlier parse
2875        let _warmup: Value = from_str("(1, 2)").unwrap();
2876
2877        let arr = Value::Array(vec![Value::Integer(1), Value::Integer(2)]);
2878        let round: Value = from_value(arr.clone()).unwrap();
2879        assert_eq!(round, arr);
2880        assert!(round.is_array());
2881
2882        let _warmup: Value = from_str("[1, 2]").unwrap();
2883        let list = Value::List(vec![Value::Integer(1), Value::String("a".into())]);
2884        let round: Value = from_value(list.clone()).unwrap();
2885        assert_eq!(round, list);
2886        assert!(round.is_list());
2887    }
2888
2889    #[test]
2890    fn test_from_value_enum() {
2891        use serde::Deserialize;
2892
2893        #[derive(Debug, Deserialize, PartialEq)]
2894        enum E {
2895            Unit,
2896            Newtype(i32),
2897            Struct { x: i32 },
2898        }
2899
2900        assert_eq!(
2901            from_value::<E>(Value::String("Unit".to_string())).unwrap(),
2902            E::Unit
2903        );
2904
2905        let v: Value = from_str("Newtype = 5;").unwrap();
2906        assert_eq!(from_value::<E>(v).unwrap(), E::Newtype(5));
2907
2908        let v: Value = from_str("Struct = { x = 9; };").unwrap();
2909        assert_eq!(from_value::<E>(v).unwrap(), E::Struct { x: 9 });
2910    }
2911
2912    #[test]
2913    fn test_display_is_valid_libconfig() {
2914        let v: Value = from_str(r#"s = "a\"b"; g = { n = 1; }; items = [1, 2];"#).unwrap();
2915        // Display renders the *value* form (a group displays braced), so it
2916        // must parse back when placed in a value position
2917        let displayed = format!("{}", v);
2918        let reparsed: Value = from_str(&format!("wrapped = {};", displayed)).unwrap();
2919        assert_eq!(reparsed["wrapped"], v);
2920    }
2921
2922    #[test]
2923    fn test_display_scalars() {
2924        assert_eq!(format!("{}", Value::Integer64(5)), "5L");
2925        assert_eq!(format!("{}", Value::Float(1.0)), "1.0");
2926        assert_eq!(
2927            format!("{}", Value::String("say \"hi\"".to_string())),
2928            r#""say \"hi\"""#
2929        );
2930    }
2931
2932    #[test]
2933    fn test_non_finite_float_errors_in_config_to_string() {
2934        let mut config = Config::new();
2935        config.set("bad", Value::Float(f64::NAN)).unwrap();
2936        assert!(matches!(config.to_string(), Err(e) if *e.kind() == ErrorKind::NonFiniteFloat));
2937    }
2938
2939    // === Include directive tests ===
2940
2941    fn temp_dir(tag: &str) -> std::path::PathBuf {
2942        let dir = std::env::temp_dir().join(format!(
2943            "libconfigasaurus-test-{}-{}",
2944            tag,
2945            std::process::id()
2946        ));
2947        std::fs::create_dir_all(&dir).unwrap();
2948        dir
2949    }
2950
2951    #[test]
2952    fn test_include_directive() {
2953        let dir = temp_dir("basic");
2954        std::fs::write(dir.join("inner.cfg"), "b = 2;\n").unwrap();
2955        std::fs::write(
2956            dir.join("outer.cfg"),
2957            "a = 1;\n@include \"inner.cfg\"\nc = 3;\n",
2958        )
2959        .unwrap();
2960
2961        let config = Config::from_file_with_include_dir(dir.join("outer.cfg"), &dir).unwrap();
2962        assert_eq!(config["a"], 1);
2963        assert_eq!(config["b"], 2);
2964        assert_eq!(config["c"], 3);
2965
2966        std::fs::remove_dir_all(&dir).ok();
2967    }
2968
2969    #[test]
2970    fn test_include_nested_inside_group() {
2971        // Mirrors the manual's quote.cfg / test.cfg example: an @include
2972        // inside a group inlines settings at the point of inclusion
2973        let dir = temp_dir("nested");
2974        std::fs::write(dir.join("quote.cfg"), "quote = \"Criticism\";\n").unwrap();
2975        std::fs::write(
2976            dir.join("test.cfg"),
2977            "info: {\n  name = \"WC\";\n  @include \"quote.cfg\"\n  country = \"UK\";\n};\n",
2978        )
2979        .unwrap();
2980
2981        let config = Config::from_file_with_include_dir(dir.join("test.cfg"), &dir).unwrap();
2982        assert_eq!(config["info.name"], "WC");
2983        assert_eq!(config["info.quote"], "Criticism");
2984        assert_eq!(config["info.country"], "UK");
2985
2986        std::fs::remove_dir_all(&dir).ok();
2987    }
2988
2989    #[test]
2990    fn test_include_depth_limit() {
2991        let dir = temp_dir("depth");
2992        std::fs::write(dir.join("loop.cfg"), "@include \"loop.cfg\"\n").unwrap();
2993
2994        let result = Config::from_file_with_include_dir(dir.join("loop.cfg"), &dir);
2995        assert!(matches!(result, Err(e) if matches!(e.kind(), ErrorKind::Include(_))));
2996
2997        std::fs::remove_dir_all(&dir).ok();
2998    }
2999
3000    #[test]
3001    fn test_include_missing_file_errors() {
3002        let result = Config::from_str("@include \"does-not-exist-anywhere.cfg\"\n");
3003        assert!(matches!(result, Err(e) if matches!(e.kind(), ErrorKind::Include(_))));
3004    }
3005
3006    // === API audit additions ===
3007
3008    #[test]
3009    fn test_set_validates_names_and_paths() {
3010        let mut config = Config::new();
3011
3012        // Invalid setting names are rejected up front, not at write time
3013        assert!(matches!(
3014            config.set("my key", 1).unwrap_err().kind(),
3015            ErrorKind::InvalidSettingName(_)
3016        ));
3017        assert!(matches!(
3018            config.set("2bad", 1).unwrap_err().kind(),
3019            ErrorKind::InvalidPath(_) | ErrorKind::InvalidSettingName(_)
3020        ));
3021        assert!(matches!(
3022            config.set("", 1).unwrap_err().kind(),
3023            ErrorKind::InvalidPath(_)
3024        ));
3025
3026        // Traversal failures are distinguishable
3027        config.set("scalar", 5).unwrap();
3028        assert!(matches!(
3029            config.set("scalar.child", 1).unwrap_err().kind(),
3030            ErrorKind::PathTraversal(_)
3031        ));
3032        config.set("items", Value::Array(vec![Value::Integer(1)])).unwrap();
3033        assert!(matches!(
3034            config.set("items.[5]", 9).unwrap_err().kind(),
3035            ErrorKind::PathTraversal(_)
3036        ));
3037    }
3038
3039    #[test]
3040    fn test_set_accepts_into_value() {
3041        let mut config = Config::new();
3042        config.set("count", 42).unwrap();
3043        config.set("big", 5_000_000_000i64).unwrap();
3044        config.set("ratio", 0.5).unwrap();
3045        config.set("on", true).unwrap();
3046        config.set("name", "svc").unwrap();
3047        config.set("label", String::from("owned")).unwrap();
3048
3049        assert_eq!(config["count"], 42);
3050        assert_eq!(config["big"], 5_000_000_000i64);
3051        assert_eq!(config["ratio"], 0.5);
3052        assert_eq!(config["on"], true);
3053        assert_eq!(config["name"], "svc");
3054        assert_eq!(config["label"], "owned");
3055    }
3056
3057    #[test]
3058    fn test_value_from_conversions() {
3059        assert_eq!(Value::from(true), Value::Bool(true));
3060        assert_eq!(Value::from(7i32), Value::Integer(7));
3061        assert_eq!(Value::from(7u16), Value::Integer(7));
3062        assert_eq!(Value::from(7i64), Value::Integer64(7));
3063        // u32 beyond i32 range is a 64-bit integer per the manual
3064        assert_eq!(Value::from(u32::MAX), Value::Integer64(4294967295));
3065        assert_eq!(Value::from(2.5f64), Value::Float(2.5));
3066        assert_eq!(Value::from("x"), Value::String("x".to_string()));
3067        assert_eq!(Value::from(Map::new()), Value::Group(Map::new()));
3068    }
3069
3070    #[test]
3071    fn test_reverse_comparisons() {
3072        let v: Value = from_str("a = 42; s = \"hi\"; b = true; f = 2.5;").unwrap();
3073        assert!(42 == v["a"]);
3074        assert!("hi" == v["s"]);
3075        assert!(true == v["b"]);
3076        assert!(2.5 == v["f"]);
3077        let owned = String::from("hi");
3078        assert!(owned == v["s"]);
3079    }
3080
3081    #[test]
3082    fn test_mutable_accessors() {
3083        let mut v: Value = from_str("g = { a = 1; }; items = [1, 2];").unwrap();
3084
3085        v.get_mut("g")
3086            .unwrap()
3087            .as_group_mut()
3088            .unwrap()
3089            .insert("b".to_string(), Value::Integer(2));
3090        assert_eq!(v["g.b"], 2);
3091
3092        v.get_mut("items")
3093            .unwrap()
3094            .as_array_mut()
3095            .unwrap()
3096            .push(Value::Integer(3));
3097        assert_eq!(v["items.[2]"], 3);
3098
3099        *v.get_mut("items").unwrap().get_index_mut(0).unwrap() = Value::Integer(9);
3100        assert_eq!(v["items.[0]"], 9);
3101    }
3102
3103    #[test]
3104    fn test_to_value() {
3105        use serde::Serialize;
3106
3107        #[derive(Serialize)]
3108        struct Inner {
3109            x: i32,
3110        }
3111        #[derive(Serialize)]
3112        struct Outer {
3113            a: i32,
3114            names: Vec<String>,
3115            inner: Inner,
3116            things: Vec<Inner>,
3117        }
3118
3119        let v = to_value(&Outer {
3120            a: 1,
3121            names: vec!["p".into(), "q".into()],
3122            inner: Inner { x: 2 },
3123            things: vec![Inner { x: 3 }],
3124        })
3125        .unwrap();
3126
3127        assert_eq!(v["a"], 1);
3128        assert!(v["names"].is_array());
3129        assert_eq!(v["inner.x"], 2);
3130        assert!(v["things"].is_list()); // groups can't live in arrays
3131        assert_eq!(v["things.[0].x"], 3);
3132
3133        // to_value output serializes identically to serializing T directly
3134        assert_eq!(
3135            crate::to_string(&v).unwrap(),
3136            crate::to_string(&Outer {
3137                a: 1,
3138                names: vec!["p".into(), "q".into()],
3139                inner: Inner { x: 2 },
3140                things: vec![Inner { x: 3 }],
3141            })
3142            .unwrap()
3143        );
3144
3145        // Out-of-range and unrepresentable values error
3146        assert!(matches!(
3147            to_value(&u64::MAX).unwrap_err().kind(),
3148            ErrorKind::IntegerOutOfRange
3149        ));
3150        assert!(to_value(&()).is_err());
3151    }
3152
3153    #[test]
3154    fn test_to_value_enums() {
3155        use serde::Serialize;
3156
3157        #[derive(Serialize)]
3158        enum E {
3159            Unit,
3160            Newtype(i32),
3161            Tuple(i32, bool),
3162            Struct { x: i32 },
3163        }
3164
3165        assert_eq!(to_value(&E::Unit).unwrap(), Value::String("Unit".into()));
3166        let v = to_value(&E::Newtype(5)).unwrap();
3167        assert_eq!(v["Newtype"], 5);
3168        let v = to_value(&E::Tuple(7, true)).unwrap();
3169        assert!(v["Tuple"].is_list());
3170        let v = to_value(&E::Struct { x: 9 }).unwrap();
3171        assert_eq!(v["Struct.x"], 9);
3172    }
3173
3174    #[test]
3175    fn test_option_none_fields_are_omitted() {
3176        use serde::{Deserialize, Serialize};
3177
3178        #[derive(Serialize, Deserialize, PartialEq, Debug)]
3179        struct S {
3180            a: i32,
3181            b: Option<i32>,
3182            c: Option<String>,
3183        }
3184
3185        let none = S {
3186            a: 1,
3187            b: None,
3188            c: Some("x".into()),
3189        };
3190        let text = crate::to_string(&none).unwrap();
3191        // The None field is simply absent
3192        assert_eq!(text, "a = 1;\nc = \"x\";");
3193
3194        // ... and absent settings deserialize back to None: symmetric
3195        let back: S = from_str(&text).unwrap();
3196        assert_eq!(back, none);
3197
3198        // Maps with Option values behave the same way
3199        let mut map: IndexMap<String, Option<i32>> = IndexMap::new();
3200        map.insert("kept".into(), Some(1));
3201        map.insert("dropped".into(), None);
3202        assert_eq!(crate::to_string(&map).unwrap(), "kept = 1;");
3203
3204        // A bare None (no setting to omit) is still unrepresentable
3205        assert!(crate::to_string(&None::<i32>).is_err());
3206        // As is a None inside a list, where position matters
3207        assert!(crate::to_string(&(1, None::<i32>)).is_err());
3208    }
3209
3210    #[test]
3211    fn test_config_try_from_and_into_value() {
3212        let group: Value = from_str("a = 1;").unwrap();
3213        let config = Config::try_from(group.clone()).unwrap();
3214        assert_eq!(config.into_value(), group);
3215
3216        assert!(matches!(
3217            Config::try_from(Value::Integer(42)).unwrap_err().kind(),
3218            ErrorKind::ExpectedGroup
3219        ));
3220    }
3221
3222    #[test]
3223    fn test_config_root_invariant_checked_at_write() {
3224        // DerefMut still allows replacing the root; to_string catches it
3225        let mut config = Config::new();
3226        *config = Value::Integer(42);
3227        assert!(matches!(
3228            config.to_string().unwrap_err().kind(),
3229            ErrorKind::ExpectedGroup
3230        ));
3231    }
3232
3233    #[test]
3234    fn test_config_deserialize() {
3235        let config: Config = from_str("a = 1; g = { b = 2; };").unwrap();
3236        assert_eq!(config["a"], 1);
3237        assert_eq!(config["g.b"], 2);
3238
3239        assert!(from_str::<Config>("42").is_err());
3240    }
3241
3242    #[test]
3243    fn test_config_to_file_round_trip() {
3244        let dir = temp_dir("tofile");
3245        let path = dir.join("out.cfg");
3246
3247        let mut config = Config::new();
3248        config.set("name", "svc").unwrap();
3249        config.set("db.port", 5432).unwrap();
3250        config.to_file(&path).unwrap();
3251
3252        let raw = std::fs::read_to_string(&path).unwrap();
3253        assert!(raw.ends_with('\n'));
3254
3255        let back = Config::from_file(&path).unwrap();
3256        assert_eq!(back, config);
3257
3258        std::fs::remove_dir_all(&dir).ok();
3259    }
3260
3261    #[test]
3262    fn test_from_reader_to_writer() {
3263        let mut buf = Vec::new();
3264        let v: Value = from_str("a = 1; b = \"x\";").unwrap();
3265        crate::to_writer(&mut buf, &v).unwrap();
3266
3267        let back: Value = crate::from_reader(buf.as_slice()).unwrap();
3268        assert_eq!(back, v);
3269    }
3270
3271    #[test]
3272    fn test_value_via_foreign_format_is_deterministic() {
3273        // Leave a "list" marker behind from a libconfig parse...
3274        let _warm: Value = from_str("(1, 2)").unwrap();
3275        // ...then drive Value's Deserialize impl from serde_json: the result
3276        // must not depend on that history
3277        let v: Value = serde_json::from_str("[1, 2]").unwrap();
3278        assert!(v.is_array(), "homogeneous scalars from JSON become an array");
3279
3280        let v: Value = serde_json::from_str("[1, \"mixed\"]").unwrap();
3281        assert!(v.is_list(), "heterogeneous JSON sequences become a list");
3282
3283        let v: Value = serde_json::from_str(r#"{"a": 1}"#).unwrap();
3284        assert!(v.is_group());
3285    }
3286
3287    #[test]
3288    fn test_include_custom_resolver() {
3289        let dir = temp_dir("resolver");
3290        std::fs::write(dir.join("one.cfg"), "a = 1;\n").unwrap();
3291        std::fs::write(dir.join("two.cfg"), "b = 2;\n").unwrap();
3292
3293        // A resolver can expand one directive into several files (the
3294        // manual's wildcard-expansion use case), in order
3295        let d = dir.clone();
3296        let config = Config::from_str_with_include_fn("@include \"*.cfg\"\nc = 3;\n", |path| {
3297            assert_eq!(path, "*.cfg");
3298            Ok(vec![d.join("one.cfg"), d.join("two.cfg")])
3299        })
3300        .unwrap();
3301        assert_eq!(config["a"], 1);
3302        assert_eq!(config["b"], 2);
3303        assert_eq!(config["c"], 3);
3304        // Inlined in resolver order, before the following settings
3305        let keys: Vec<&String> = config.as_group().unwrap().keys().collect();
3306        assert_eq!(keys, ["a", "b", "c"]);
3307
3308        // Resolver errors surface as parse failures
3309        let result = Config::from_str_with_include_fn("@include \"nope\"\n", |path| {
3310            Err(ErrorKind::Include(format!("cannot resolve '{path}'")).into())
3311        });
3312        assert!(matches!(
3313            result.unwrap_err().kind(),
3314            ErrorKind::Include(msg) if msg.contains("cannot resolve")
3315        ));
3316
3317        // Nested includes go through the same resolver
3318        std::fs::write(dir.join("outer.cfg"), "@include \"inner\"\nz = 9;\n").unwrap();
3319        std::fs::write(dir.join("inner.cfg"), "y = 8;\n").unwrap();
3320        let d = dir.clone();
3321        let config =
3322            Config::from_file_with_include_fn(dir.join("outer.cfg"), move |path| {
3323                Ok(vec![d.join(format!("{path}.cfg"))])
3324            })
3325            .unwrap();
3326        assert_eq!(config["y"], 8);
3327        assert_eq!(config["z"], 9);
3328
3329        std::fs::remove_dir_all(&dir).ok();
3330    }
3331
3332    #[test]
3333    fn test_include_ignored_in_comments_and_strings() {
3334        // Inside a block comment: not processed
3335        let config = Config::from_str("/*\n@include \"nope.cfg\"\n*/\na = 1;").unwrap();
3336        assert_eq!(config["a"], 1);
3337
3338        // Inside a string: not processed (not at start of line)
3339        let config = Config::from_str("s = \"see @include docs\";").unwrap();
3340        assert_eq!(config["s"], "see @include docs");
3341    }
3342}