Skip to main content

qubit_config/
config.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! # Configuration Manager
10//!
11//! Provides storage, retrieval, and management of configurations.
12//!
13//! # Author
14//!
15//! Haixing Hu
16
17#![allow(private_bounds)]
18
19use std::collections::HashMap;
20use std::ops::Deref;
21
22use serde::de::DeserializeOwned;
23use serde::{Deserialize, Serialize};
24use serde_json::{Map, Value, from_value};
25
26use crate::config_prefix_view::ConfigPrefixView;
27use crate::config_reader::ConfigReader;
28use crate::constants::DEFAULT_MAX_SUBSTITUTION_DEPTH;
29use crate::source::ConfigSource;
30use crate::utils;
31use crate::{ConfigError, ConfigResult, Property};
32use qubit_common::DataType;
33use qubit_value::MultiValues;
34use qubit_value::multi_values::{
35    MultiValuesAddArg, MultiValuesAdder, MultiValuesFirstGetter, MultiValuesGetter,
36    MultiValuesMultiAdder, MultiValuesSetArg, MultiValuesSetter, MultiValuesSetterSlice,
37    MultiValuesSingleSetter,
38};
39
40/// Configuration Manager
41///
42/// Manages a set of configuration properties with type-safe read/write
43/// interfaces.
44///
45/// # Features
46///
47/// - Supports multiple data types
48/// - Supports variable substitution (`${var_name}` format)
49/// - Supports configuration merging
50/// - Supports final value protection
51/// - Thread-safe (when wrapped in `Arc<RwLock<Config>>`)
52///
53/// # Examples
54///
55/// ```rust
56/// use qubit_config::Config;
57///
58/// let mut config = Config::new();
59///
60/// // Set configuration values (type inference)
61/// config.set("port", 8080).unwrap();                    // inferred as i32
62/// config.set("host", "localhost").unwrap();
63/// // &str is converted to String
64/// config.set("debug", true).unwrap();                   // inferred as bool
65/// config.set("timeout", 30.5).unwrap();                 // inferred as f64
66/// config.set("code", 42u8).unwrap();                    // inferred as u8
67///
68/// // Set multiple values (type inference)
69/// config.set("ports", vec![8080, 8081, 8082]).unwrap(); // inferred as i32
70/// config.set("hosts", vec!["host1", "host2"]).unwrap();
71/// // &str elements are converted
72///
73/// // Read configuration values (type inference)
74/// let port: i32 = config.get("port").unwrap();
75/// let host: String = config.get("host").unwrap();
76/// let debug: bool = config.get("debug").unwrap();
77/// let code: u8 = config.get("code").unwrap();
78///
79/// // Read configuration values (turbofish)
80/// let port = config.get::<i32>("port").unwrap();
81///
82/// // Read configuration value or use default
83/// let timeout: u64 = config.get_or("timeout", 30);
84/// ```
85///
86/// # Author
87///
88/// Haixing Hu
89///
90#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
91pub struct Config {
92    /// Configuration description
93    description: Option<String>,
94    /// Configuration property mapping
95    pub(crate) properties: HashMap<String, Property>,
96    /// Whether variable substitution is enabled
97    enable_variable_substitution: bool,
98    /// Maximum depth for variable substitution
99    max_substitution_depth: usize,
100}
101
102/// Guarded mutable access to a non-final [`Property`] stored in a [`Config`].
103///
104/// This wrapper deliberately exposes read-only deref to [`Property`], but not
105/// `DerefMut`. Value-changing operations re-check the property's final flag on
106/// every call, so setting a property final through the guard immediately blocks
107/// subsequent mutation through the same guard.
108pub struct ConfigPropertyMut<'a> {
109    property: &'a mut Property,
110}
111
112impl<'a> ConfigPropertyMut<'a> {
113    /// Creates a guarded mutable property reference.
114    ///
115    /// # Parameters
116    ///
117    /// * `property` - The property to guard.
118    ///
119    /// # Returns
120    ///
121    /// A mutable guard for `property`.
122    #[inline]
123    fn new(property: &'a mut Property) -> Self {
124        Self { property }
125    }
126
127    /// Returns the underlying property as a read-only reference.
128    ///
129    /// # Returns
130    ///
131    /// The guarded property.
132    #[inline]
133    pub fn as_property(&self) -> &Property {
134        self.property
135    }
136
137    /// Sets the property description when the property is not final.
138    ///
139    /// # Parameters
140    ///
141    /// * `description` - New property description.
142    ///
143    /// # Returns
144    ///
145    /// `Ok(())` on success.
146    ///
147    /// # Errors
148    ///
149    /// Returns [`ConfigError::PropertyIsFinal`] if the property has already
150    /// been marked final.
151    #[inline]
152    pub fn set_description(&mut self, description: Option<String>) -> ConfigResult<()> {
153        self.ensure_not_final()?;
154        self.property.set_description(description);
155        Ok(())
156    }
157
158    /// Sets whether the property is final.
159    ///
160    /// Marking a non-final property as final succeeds. A property that is
161    /// already final may be marked final again, but cannot be unset through
162    /// this guard.
163    ///
164    /// # Parameters
165    ///
166    /// * `is_final` - Whether the property should be final.
167    ///
168    /// # Returns
169    ///
170    /// `Ok(())` on success.
171    ///
172    /// # Errors
173    ///
174    /// Returns [`ConfigError::PropertyIsFinal`] when trying to unset an
175    /// already-final property.
176    #[inline]
177    pub fn set_final(&mut self, is_final: bool) -> ConfigResult<()> {
178        if self.property.is_final() && !is_final {
179            return Err(ConfigError::PropertyIsFinal(
180                self.property.name().to_string(),
181            ));
182        }
183        self.property.set_final(is_final);
184        Ok(())
185    }
186
187    /// Replaces the property value when the property is not final.
188    ///
189    /// # Parameters
190    ///
191    /// * `value` - New property value.
192    ///
193    /// # Returns
194    ///
195    /// `Ok(())` on success.
196    ///
197    /// # Errors
198    ///
199    /// Returns [`ConfigError::PropertyIsFinal`] if the property has already
200    /// been marked final.
201    #[inline]
202    pub fn set_value(&mut self, value: MultiValues) -> ConfigResult<()> {
203        self.ensure_not_final()?;
204        self.property.set_value(value);
205        Ok(())
206    }
207
208    /// Replaces the property value using the generic [`MultiValues`] setter.
209    ///
210    /// # Type Parameters
211    ///
212    /// * `S` - Input accepted by [`MultiValues`] setter traits.
213    ///
214    /// # Parameters
215    ///
216    /// * `values` - New value or values.
217    ///
218    /// # Returns
219    ///
220    /// `Ok(())` on success.
221    ///
222    /// # Errors
223    ///
224    /// Returns [`ConfigError::PropertyIsFinal`] if the property has already
225    /// been marked final, or a converted value error if setting fails.
226    pub fn set<S>(&mut self, values: S) -> ConfigResult<()>
227    where
228        S: for<'b> MultiValuesSetArg<'b>,
229        <S as MultiValuesSetArg<'static>>::Item: Clone,
230        MultiValues: MultiValuesSetter<<S as MultiValuesSetArg<'static>>::Item>
231            + MultiValuesSetterSlice<<S as MultiValuesSetArg<'static>>::Item>
232            + MultiValuesSingleSetter<<S as MultiValuesSetArg<'static>>::Item>,
233    {
234        self.ensure_not_final()?;
235        self.property.set(values).map_err(ConfigError::from)
236    }
237
238    /// Appends values using the generic [`MultiValues`] adder.
239    ///
240    /// # Type Parameters
241    ///
242    /// * `S` - Input accepted by [`MultiValues`] adder traits.
243    ///
244    /// # Parameters
245    ///
246    /// * `values` - Value or values to append.
247    ///
248    /// # Returns
249    ///
250    /// `Ok(())` on success.
251    ///
252    /// # Errors
253    ///
254    /// Returns [`ConfigError::PropertyIsFinal`] if the property has already
255    /// been marked final, or a converted value error if appending fails.
256    pub fn add<S>(&mut self, values: S) -> ConfigResult<()>
257    where
258        S: for<'b> MultiValuesAddArg<'b, Item = <S as MultiValuesSetArg<'static>>::Item>
259            + for<'b> MultiValuesSetArg<'b>,
260        <S as MultiValuesSetArg<'static>>::Item: Clone,
261        MultiValues: MultiValuesAdder<<S as MultiValuesSetArg<'static>>::Item>
262            + MultiValuesMultiAdder<<S as MultiValuesSetArg<'static>>::Item>
263            + MultiValuesSetter<<S as MultiValuesSetArg<'static>>::Item>
264            + MultiValuesSetterSlice<<S as MultiValuesSetArg<'static>>::Item>
265            + MultiValuesSingleSetter<<S as MultiValuesSetArg<'static>>::Item>,
266    {
267        self.ensure_not_final()?;
268        self.property.add(values).map_err(ConfigError::from)
269    }
270
271    /// Clears the property value when the property is not final.
272    ///
273    /// # Returns
274    ///
275    /// `Ok(())` on success.
276    ///
277    /// # Errors
278    ///
279    /// Returns [`ConfigError::PropertyIsFinal`] if the property has already
280    /// been marked final.
281    #[inline]
282    pub fn clear(&mut self) -> ConfigResult<()> {
283        self.ensure_not_final()?;
284        self.property.clear();
285        Ok(())
286    }
287
288    /// Ensures the guarded property has not been marked final.
289    ///
290    /// # Returns
291    ///
292    /// `Ok(())` if the property is mutable.
293    ///
294    /// # Errors
295    ///
296    /// Returns [`ConfigError::PropertyIsFinal`] if the property is final.
297    #[inline]
298    fn ensure_not_final(&self) -> ConfigResult<()> {
299        if self.property.is_final() {
300            return Err(ConfigError::PropertyIsFinal(
301                self.property.name().to_string(),
302            ));
303        }
304        Ok(())
305    }
306}
307
308impl Deref for ConfigPropertyMut<'_> {
309    type Target = Property;
310
311    /// Dereferences to the guarded property for read-only access.
312    #[inline]
313    fn deref(&self) -> &Self::Target {
314        self.property
315    }
316}
317
318impl Config {
319    /// Creates a new empty configuration
320    ///
321    /// # Returns
322    ///
323    /// Returns a new configuration instance
324    ///
325    /// # Examples
326    ///
327    /// ```rust
328    /// use qubit_config::Config;
329    ///
330    /// let mut config = Config::new();
331    /// assert!(config.is_empty());
332    /// ```
333    #[inline]
334    pub fn new() -> Self {
335        Self {
336            description: None,
337            properties: HashMap::new(),
338            enable_variable_substitution: true,
339            max_substitution_depth: DEFAULT_MAX_SUBSTITUTION_DEPTH,
340        }
341    }
342
343    /// Creates a configuration with description
344    ///
345    /// # Parameters
346    ///
347    /// * `description` - Configuration description
348    ///
349    /// # Returns
350    ///
351    /// Returns a new configuration instance
352    ///
353    /// # Examples
354    ///
355    /// ```rust
356    /// use qubit_config::Config;
357    ///
358    /// let config = Config::with_description("Server Configuration");
359    /// assert_eq!(config.description(), Some("Server Configuration"));
360    /// ```
361    #[inline]
362    pub fn with_description(description: &str) -> Self {
363        Self {
364            description: Some(description.to_string()),
365            properties: HashMap::new(),
366            enable_variable_substitution: true,
367            max_substitution_depth: DEFAULT_MAX_SUBSTITUTION_DEPTH,
368        }
369    }
370
371    // ========================================================================
372    // Basic Property Access
373    // ========================================================================
374
375    /// Gets the configuration description
376    ///
377    /// # Returns
378    ///
379    /// Returns the configuration description as Option
380    #[inline]
381    pub fn description(&self) -> Option<&str> {
382        self.description.as_deref()
383    }
384
385    /// Sets the configuration description
386    ///
387    /// # Parameters
388    ///
389    /// * `description` - Configuration description
390    ///
391    /// # Returns
392    ///
393    /// Nothing.
394    #[inline]
395    pub fn set_description(&mut self, description: Option<String>) {
396        self.description = description;
397    }
398
399    /// Checks if variable substitution is enabled
400    ///
401    /// # Returns
402    ///
403    /// Returns `true` if variable substitution is enabled
404    #[inline]
405    pub fn is_enable_variable_substitution(&self) -> bool {
406        self.enable_variable_substitution
407    }
408
409    /// Sets whether to enable variable substitution
410    ///
411    /// # Parameters
412    ///
413    /// * `enable` - Whether to enable
414    ///
415    /// # Returns
416    ///
417    /// Nothing.
418    #[inline]
419    pub fn set_enable_variable_substitution(&mut self, enable: bool) {
420        self.enable_variable_substitution = enable;
421    }
422
423    /// Gets the maximum depth for variable substitution
424    ///
425    /// # Returns
426    ///
427    /// Returns the maximum depth value
428    #[inline]
429    pub fn max_substitution_depth(&self) -> usize {
430        self.max_substitution_depth
431    }
432
433    /// Creates a read-only prefix view using [`ConfigPrefixView`].
434    ///
435    /// # Parameters
436    ///
437    /// * `prefix` - Prefix
438    ///
439    /// # Returns
440    ///
441    /// Returns a read-only prefix view
442    ///
443    /// # Examples
444    ///
445    /// ```rust
446    /// use qubit_config::{Config, ConfigReader};
447    ///
448    /// let mut config = Config::new();
449    /// config.set("server.port", 8080).unwrap();
450    /// config.set("server.host", "localhost").unwrap();
451    ///
452    /// let server = config.prefix_view("server");
453    /// assert_eq!(server.get::<i32>("port").unwrap(), 8080);
454    /// assert_eq!(server.get::<String>("host").unwrap(), "localhost");
455    /// ```
456    #[inline]
457    pub fn prefix_view(&self, prefix: &str) -> ConfigPrefixView<'_> {
458        ConfigPrefixView::new(self, prefix)
459    }
460
461    /// Sets the maximum depth for variable substitution
462    ///
463    /// # Parameters
464    ///
465    /// * `depth` - Maximum depth
466    ///
467    /// # Returns
468    ///
469    /// Nothing.
470    #[inline]
471    pub fn set_max_substitution_depth(&mut self, depth: usize) {
472        self.max_substitution_depth = depth;
473    }
474
475    // ========================================================================
476    // Configuration Item Management
477    // ========================================================================
478
479    /// Checks if the configuration contains an item with the specified name
480    ///
481    /// # Parameters
482    ///
483    /// * `name` - Configuration item name
484    ///
485    /// # Returns
486    ///
487    /// Returns `true` if the configuration item exists
488    ///
489    /// # Examples
490    ///
491    /// ```rust
492    /// use qubit_config::Config;
493    ///
494    /// let mut config = Config::new();
495    /// config.set("port", 8080).unwrap();
496    ///
497    /// assert!(config.contains("port"));
498    /// assert!(!config.contains("host"));
499    /// ```
500    #[inline]
501    pub fn contains(&self, name: &str) -> bool {
502        self.properties.contains_key(name)
503    }
504
505    /// Gets a reference to a configuration item
506    ///
507    /// # Parameters
508    ///
509    /// * `name` - Configuration item name
510    ///
511    /// # Returns
512    ///
513    /// Returns Option containing the configuration item
514    #[inline]
515    pub fn get_property(&self, name: &str) -> Option<&Property> {
516        self.properties.get(name)
517    }
518
519    /// Gets guarded mutable access to a non-final configuration item.
520    ///
521    /// # Parameters
522    ///
523    /// * `name` - Configuration item name
524    ///
525    /// # Returns
526    ///
527    /// Returns `Ok(Some(_))` for an existing non-final property, `Ok(None)`
528    /// for a missing property, or [`ConfigError::PropertyIsFinal`] for an
529    /// existing final property. The returned guard re-checks final state before
530    /// each value-changing operation.
531    #[inline]
532    pub fn get_property_mut(&mut self, name: &str) -> ConfigResult<Option<ConfigPropertyMut<'_>>> {
533        self.ensure_property_not_final(name)?;
534        Ok(self.properties.get_mut(name).map(ConfigPropertyMut::new))
535    }
536
537    /// Sets the final flag of an existing configuration item.
538    ///
539    /// A non-final property can be marked final. A property that is already
540    /// final may be marked final again, but cannot be unset through this API.
541    ///
542    /// # Parameters
543    ///
544    /// * `name` - Configuration item name.
545    /// * `is_final` - Whether the property should be final.
546    ///
547    /// # Returns
548    ///
549    /// `Ok(())` on success.
550    ///
551    /// # Errors
552    ///
553    /// - [`ConfigError::PropertyNotFound`] if the key does not exist.
554    /// - [`ConfigError::PropertyIsFinal`] when trying to unset a final
555    ///   property.
556    pub fn set_final(&mut self, name: &str, is_final: bool) -> ConfigResult<()> {
557        let property = self
558            .properties
559            .get_mut(name)
560            .ok_or_else(|| ConfigError::PropertyNotFound(name.to_string()))?;
561        if property.is_final() && !is_final {
562            return Err(ConfigError::PropertyIsFinal(name.to_string()));
563        }
564        property.set_final(is_final);
565        Ok(())
566    }
567
568    /// Removes a non-final configuration item.
569    ///
570    /// # Parameters
571    ///
572    /// * `name` - Configuration item name
573    ///
574    /// # Returns
575    ///
576    /// Returns the removed configuration item, or None if it doesn't exist
577    ///
578    /// # Examples
579    ///
580    /// ```rust
581    /// use qubit_config::Config;
582    ///
583    /// let mut config = Config::new();
584    /// config.set("port", 8080).unwrap();
585    ///
586    /// let removed = config.remove("port").unwrap();
587    /// assert!(removed.is_some());
588    /// assert!(!config.contains("port"));
589    /// ```
590    #[inline]
591    pub fn remove(&mut self, name: &str) -> ConfigResult<Option<Property>> {
592        self.ensure_property_not_final(name)?;
593        Ok(self.properties.remove(name))
594    }
595
596    /// Clears all configuration items if none of them are final.
597    ///
598    /// # Examples
599    ///
600    /// ```rust
601    /// use qubit_config::Config;
602    ///
603    /// let mut config = Config::new();
604    /// config.set("port", 8080).unwrap();
605    /// config.set("host", "localhost").unwrap();
606    ///
607    /// config.clear().unwrap();
608    /// assert!(config.is_empty());
609    /// ```
610    ///
611    /// # Returns
612    ///
613    /// `Ok(())` when all properties were removed.
614    #[inline]
615    pub fn clear(&mut self) -> ConfigResult<()> {
616        self.ensure_no_final_properties()?;
617        self.properties.clear();
618        Ok(())
619    }
620
621    /// Gets the number of configuration items
622    ///
623    /// # Returns
624    ///
625    /// Returns the number of configuration items
626    #[inline]
627    pub fn len(&self) -> usize {
628        self.properties.len()
629    }
630
631    /// Checks if the configuration is empty
632    ///
633    /// # Returns
634    ///
635    /// Returns `true` if the configuration contains no items
636    #[inline]
637    pub fn is_empty(&self) -> bool {
638        self.properties.is_empty()
639    }
640
641    /// Gets all configuration item names
642    ///
643    /// # Returns
644    ///
645    /// Returns a Vec of configuration item names
646    ///
647    /// # Examples
648    ///
649    /// ```rust
650    /// use qubit_config::Config;
651    ///
652    /// let mut config = Config::new();
653    /// config.set("port", 8080).unwrap();
654    /// config.set("host", "localhost").unwrap();
655    ///
656    /// let keys = config.keys();
657    /// assert_eq!(keys.len(), 2);
658    /// assert!(keys.contains(&"port".to_string()));
659    /// assert!(keys.contains(&"host".to_string()));
660    /// ```
661    pub fn keys(&self) -> Vec<String> {
662        self.properties.keys().cloned().collect()
663    }
664
665    /// Looks up a property by key for internal read paths.
666    ///
667    /// # Parameters
668    ///
669    /// * `name` - Configuration key
670    ///
671    /// # Returns
672    ///
673    /// `Ok(&Property)` if the key exists, or [`ConfigError::PropertyNotFound`]
674    /// otherwise.
675    #[inline]
676    fn get_property_by_name(&self, name: &str) -> ConfigResult<&Property> {
677        self.properties
678            .get(name)
679            .ok_or_else(|| ConfigError::PropertyNotFound(name.to_string()))
680    }
681
682    /// Ensures the entry for `name` is not marked final before a write.
683    ///
684    /// Missing keys are allowed (writes may create them).
685    ///
686    /// # Parameters
687    ///
688    /// * `name` - Configuration key
689    ///
690    /// # Returns
691    ///
692    /// `Ok(())` if the key is absent or not final, or
693    /// [`ConfigError::PropertyIsFinal`] if an existing property is final.
694    #[inline]
695    fn ensure_property_not_final(&self, name: &str) -> ConfigResult<()> {
696        if let Some(prop) = self.properties.get(name)
697            && prop.is_final()
698        {
699            return Err(ConfigError::PropertyIsFinal(name.to_string()));
700        }
701        Ok(())
702    }
703
704    /// Ensures no property is final before a bulk destructive operation.
705    #[inline]
706    fn ensure_no_final_properties(&self) -> ConfigResult<()> {
707        if let Some((name, _)) = self.properties.iter().find(|(_, prop)| prop.is_final()) {
708            return Err(ConfigError::PropertyIsFinal(name.clone()));
709        }
710        Ok(())
711    }
712
713    /// Shared "optional get" semantics: treat missing or empty properties as
714    /// `None`, otherwise run `read` and wrap the result in `Some`.
715    ///
716    /// Used by [`Self::get_optional`], [`Self::get_optional_list`],
717    /// [`Self::get_optional_string`], and [`Self::get_optional_string_list`].
718    ///
719    /// # Type Parameters
720    ///
721    /// * `T` - Value type produced by `read`
722    ///
723    /// # Parameters
724    ///
725    /// * `name` - Configuration key
726    /// * `read` - Loads `T` when the key exists and is non-empty (typically
727    ///   delegates to [`Self::get`], [`Self::get_list`], etc.)
728    ///
729    /// # Returns
730    ///
731    /// `Ok(None)` if the key is missing or the property has no values;
732    /// `Ok(Some(value))` on success; or the error from `read` on failure.
733    fn get_optional_when_present<T>(
734        &self,
735        name: &str,
736        read: impl FnOnce(&Self) -> ConfigResult<T>,
737    ) -> ConfigResult<Option<T>> {
738        match self.properties.get(name) {
739            None => Ok(None),
740            Some(prop) if prop.is_empty() => Ok(None),
741            Some(_) => read(self).map(Some),
742        }
743    }
744
745    // ========================================================================
746    // Core Generic Methods
747    // ========================================================================
748
749    /// Gets a configuration value.
750    ///
751    /// Core read API with type inference.
752    ///
753    /// # Note
754    ///
755    /// This method does not perform variable substitution for string types. If
756    /// you need variable substitution, use [`Self::get_string`].
757    ///
758    /// # Type Parameters
759    ///
760    /// * `T` - Target type, must implement `FromPropertyValue` trait
761    ///
762    /// # Parameters
763    ///
764    /// * `name` - Configuration item name
765    ///
766    /// # Returns
767    ///
768    /// The value of the specified type on success, or a [`ConfigError`] on
769    /// failure.
770    ///
771    /// # Errors
772    ///
773    /// - [`ConfigError::PropertyNotFound`] if the key does not exist
774    /// - [`ConfigError::PropertyHasNoValue`] if the property has no value
775    /// - [`ConfigError::TypeMismatch`] if the type does not match
776    ///
777    /// # Examples
778    ///
779    /// ```rust
780    /// use qubit_config::Config;
781    ///
782    /// let mut config = Config::new();
783    /// config.set("port", 8080).unwrap();
784    /// config.set("host", "localhost").unwrap();
785    ///
786    /// // Method 1: Type inference
787    /// let port: i32 = config.get("port").unwrap();
788    /// let host: String = config.get("host").unwrap();
789    ///
790    /// // Method 2: Turbofish
791    /// let port = config.get::<i32>("port").unwrap();
792    /// let host = config.get::<String>("host").unwrap();
793    ///
794    /// // Method 3: Inference from usage
795    /// fn start_server(port: i32, host: String) { }
796    /// start_server(config.get("port").unwrap(), config.get("host").unwrap());
797    /// ```
798    pub fn get<T>(&self, name: &str) -> ConfigResult<T>
799    where
800        MultiValues: MultiValuesFirstGetter<T>,
801    {
802        let property = self.get_property_by_name(name)?;
803
804        property
805            .get_first::<T>()
806            .map_err(|e| utils::map_value_error(name, e))
807    }
808
809    /// Gets a configuration value or returns a default value
810    ///
811    /// Returns `default` if the key is missing or if reading the value fails.
812    ///
813    /// # Type Parameters
814    ///
815    /// * `T` - Target type, must implement `FromPropertyValue` trait
816    ///
817    /// # Parameters
818    ///
819    /// * `name` - Configuration item name
820    /// * `default` - Default value
821    ///
822    /// # Returns
823    ///
824    /// Returns the configuration value or default value
825    ///
826    /// # Examples
827    ///
828    /// ```rust
829    /// use qubit_config::Config;
830    ///
831    /// let config = Config::new();
832    ///
833    /// let port: i32 = config.get_or("port", 8080);
834    /// let host: String = config.get_or("host", "localhost".to_string());
835    ///
836    /// assert_eq!(port, 8080);
837    /// assert_eq!(host, "localhost");
838    /// ```
839    pub fn get_or<T>(&self, name: &str, default: T) -> T
840    where
841        MultiValues: MultiValuesFirstGetter<T>,
842    {
843        self.get(name).unwrap_or(default)
844    }
845
846    /// Gets a list of configuration values
847    ///
848    /// Gets all values of a configuration item (multi-value configuration).
849    ///
850    /// # Type Parameters
851    ///
852    /// * `T` - Target type, must implement `FromPropertyValue` trait
853    ///
854    /// # Parameters
855    ///
856    /// * `name` - Configuration item name
857    ///
858    /// # Returns
859    ///
860    /// Returns a list of values on success, or an error on failure
861    ///
862    /// # Examples
863    ///
864    /// ```rust
865    /// use qubit_config::Config;
866    ///
867    /// let mut config = Config::new();
868    /// config.set("ports", vec![8080, 8081, 8082]).unwrap();
869    ///
870    /// let ports: Vec<i32> = config.get_list("ports").unwrap();
871    /// assert_eq!(ports, vec![8080, 8081, 8082]);
872    /// ```
873    pub fn get_list<T>(&self, name: &str) -> ConfigResult<Vec<T>>
874    where
875        MultiValues: MultiValuesGetter<T>,
876    {
877        let property = self.get_property_by_name(name)?;
878        if property.is_empty() {
879            return Ok(Vec::new());
880        }
881
882        property
883            .get::<T>()
884            .map_err(|e| utils::map_value_error(name, e))
885    }
886
887    /// Sets a configuration value
888    ///
889    /// This is the core method for setting configuration values, supporting
890    /// type inference.
891    ///
892    /// # Type Parameters
893    ///
894    /// * `T` - Element type, automatically inferred from the `values` parameter
895    ///
896    /// # Parameters
897    ///
898    /// * `name` - Configuration item name
899    /// * `values` - Value to store; supports `T`, `Vec<T>`, `&[T]`, and related
900    ///   forms accepted by [`MultiValues`] setters
901    ///
902    /// # Returns
903    ///
904    /// Returns Ok(()) on success, or an error on failure
905    ///
906    /// # Errors
907    ///
908    /// - [`ConfigError::PropertyIsFinal`] if the property is marked final
909    ///
910    /// # Examples
911    ///
912    /// ```rust
913    /// use qubit_config::Config;
914    ///
915    /// let mut config = Config::new();
916    ///
917    /// // Set single values (type auto-inference)
918    /// config.set("port", 8080).unwrap();                    // T inferred as i32
919    /// config.set("host", "localhost").unwrap();
920    /// // T inferred as String; &str is converted
921    /// config.set("debug", true).unwrap();                   // T inferred as bool
922    /// config.set("timeout", 30.5).unwrap();                 // T inferred as f64
923    ///
924    /// // Set multiple values (type auto-inference)
925    /// config.set("ports", vec![8080, 8081, 8082]).unwrap(); // T inferred as i32
926    /// config.set("hosts", vec!["host1", "host2"]).unwrap();
927    /// // T inferred as &str (then converted)
928    /// ```
929    pub fn set<S>(&mut self, name: &str, values: S) -> ConfigResult<()>
930    where
931        S: for<'a> MultiValuesSetArg<'a>,
932        <S as MultiValuesSetArg<'static>>::Item: Clone,
933        MultiValues: MultiValuesSetter<<S as MultiValuesSetArg<'static>>::Item>
934            + MultiValuesSetterSlice<<S as MultiValuesSetArg<'static>>::Item>
935            + MultiValuesSingleSetter<<S as MultiValuesSetArg<'static>>::Item>,
936    {
937        self.ensure_property_not_final(name)?;
938        let property = self
939            .properties
940            .entry(name.to_string())
941            .or_insert_with(|| Property::new(name));
942
943        property.set(values).map_err(ConfigError::from)
944    }
945
946    /// Adds configuration values
947    ///
948    /// Adds values to an existing configuration item (multi-value properties).
949    ///
950    /// # Type Parameters
951    ///
952    /// * `T` - Element type, automatically inferred from the `values` parameter
953    ///
954    /// # Parameters
955    ///
956    /// * `name` - Configuration item name
957    /// * `values` - Values to append; supports the same forms as [`Self::set`]
958    ///
959    /// # Returns
960    ///
961    /// Returns Ok(()) on success, or an error on failure
962    ///
963    /// # Examples
964    ///
965    /// ```rust
966    /// use qubit_config::Config;
967    ///
968    /// let mut config = Config::new();
969    /// config.set("port", 8080).unwrap();                    // Set initial value
970    /// config.add("port", 8081).unwrap();                    // Add single value
971    /// config.add("port", vec![8082, 8083]).unwrap();        // Add multiple values
972    /// config.add("port", vec![8084, 8085]).unwrap();       // Add slice
973    ///
974    /// let ports: Vec<i32> = config.get_list("port").unwrap();
975    /// assert_eq!(ports, vec![8080, 8081, 8082, 8083, 8084, 8085]);
976    /// ```
977    pub fn add<S>(&mut self, name: &str, values: S) -> ConfigResult<()>
978    where
979        S: for<'a> MultiValuesAddArg<'a, Item = <S as MultiValuesSetArg<'static>>::Item>
980            + for<'a> MultiValuesSetArg<'a>,
981        <S as MultiValuesSetArg<'static>>::Item: Clone,
982        MultiValues: MultiValuesAdder<<S as MultiValuesSetArg<'static>>::Item>
983            + MultiValuesMultiAdder<<S as MultiValuesSetArg<'static>>::Item>
984            + MultiValuesSetter<<S as MultiValuesSetArg<'static>>::Item>
985            + MultiValuesSetterSlice<<S as MultiValuesSetArg<'static>>::Item>
986            + MultiValuesSingleSetter<<S as MultiValuesSetArg<'static>>::Item>,
987    {
988        self.ensure_property_not_final(name)?;
989
990        if let Some(property) = self.properties.get_mut(name) {
991            property.add(values).map_err(ConfigError::from)
992        } else {
993            let mut property = Property::new(name);
994            // Note: property.set() always returns Ok(()) in current MultiValues implementation,
995            // as it unconditionally replaces the entire value without any validation.
996            // We explicitly ignore the result to improve code coverage and avoid unreachable error paths.
997            let _ = property.set(values);
998            self.properties.insert(name.to_string(), property);
999            Ok(())
1000        }
1001    }
1002
1003    // ========================================================================
1004    // String Special Handling (Variable Substitution)
1005    // ========================================================================
1006
1007    /// Gets a string configuration value (with variable substitution)
1008    ///
1009    /// If variable substitution is enabled, replaces `${var_name}` placeholders
1010    /// in the stored string.
1011    ///
1012    /// # Parameters
1013    ///
1014    /// * `name` - Configuration item name
1015    ///
1016    /// # Returns
1017    ///
1018    /// Returns the string value on success, or an error on failure
1019    ///
1020    /// # Examples
1021    ///
1022    /// ```rust
1023    /// use qubit_config::Config;
1024    ///
1025    /// let mut config = Config::new();
1026    /// config.set("base_url", "http://localhost").unwrap();
1027    /// config.set("api_url", "${base_url}/api").unwrap();
1028    ///
1029    /// let api_url = config.get_string("api_url").unwrap();
1030    /// assert_eq!(api_url, "http://localhost/api");
1031    /// ```
1032    pub fn get_string(&self, name: &str) -> ConfigResult<String> {
1033        let value: String = self.get(name)?;
1034        if self.enable_variable_substitution {
1035            utils::substitute_variables(&value, self, self.max_substitution_depth)
1036        } else {
1037            Ok(value)
1038        }
1039    }
1040
1041    /// Gets a string with substitution, or `default` if reading fails.
1042    ///
1043    /// # Parameters
1044    ///
1045    /// * `name` - Configuration item name
1046    /// * `default` - Default value
1047    ///
1048    /// # Returns
1049    ///
1050    /// Returns the string value or default value
1051    ///
1052    pub fn get_string_or(&self, name: &str, default: &str) -> String {
1053        self.get_string(name)
1054            .unwrap_or_else(|_| default.to_string())
1055    }
1056
1057    /// Gets a list of string configuration values (with variable substitution)
1058    ///
1059    /// If variable substitution is enabled, runs it on each list element
1060    /// (same `${var_name}` rules as [`Self::get_string`]).
1061    ///
1062    /// # Parameters
1063    ///
1064    /// * `name` - Configuration item name
1065    ///
1066    /// # Returns
1067    ///
1068    /// Returns a list of strings on success, or an error on failure
1069    ///
1070    /// # Examples
1071    ///
1072    /// ```rust
1073    /// use qubit_config::Config;
1074    ///
1075    /// let mut config = Config::new();
1076    /// config.set("base_path", "/opt/app").unwrap();
1077    /// config.set("paths", vec!["${base_path}/bin", "${base_path}/lib"]).unwrap();
1078    ///
1079    /// let paths = config.get_string_list("paths").unwrap();
1080    /// assert_eq!(paths, vec!["/opt/app/bin", "/opt/app/lib"]);
1081    /// ```
1082    pub fn get_string_list(&self, name: &str) -> ConfigResult<Vec<String>> {
1083        let values: Vec<String> = self.get_list(name)?;
1084        if self.enable_variable_substitution {
1085            values
1086                .into_iter()
1087                .map(|v| utils::substitute_variables(&v, self, self.max_substitution_depth))
1088                .collect()
1089        } else {
1090            Ok(values)
1091        }
1092    }
1093
1094    /// Gets a list of string configuration values or returns a default value
1095    /// (with variable substitution)
1096    ///
1097    /// # Parameters
1098    ///
1099    /// * `name` - Configuration item name
1100    /// * `default` - Default value (can be array slice or vec)
1101    ///
1102    /// # Returns
1103    ///
1104    /// Returns the list of strings or default value
1105    ///
1106    /// # Examples
1107    ///
1108    /// ```rust
1109    /// use qubit_config::Config;
1110    ///
1111    /// let config = Config::new();
1112    ///
1113    /// // Using array slice
1114    /// let paths = config.get_string_list_or("paths", &["/default/path"]);
1115    /// assert_eq!(paths, vec!["/default/path"]);
1116    ///
1117    /// // Using vec
1118    /// let paths = config.get_string_list_or("paths", &vec!["path1", "path2"]);
1119    /// assert_eq!(paths, vec!["path1", "path2"]);
1120    /// ```
1121    pub fn get_string_list_or(&self, name: &str, default: &[&str]) -> Vec<String> {
1122        self.get_string_list(name)
1123            .unwrap_or_else(|_| default.iter().map(|s| s.to_string()).collect())
1124    }
1125
1126    // ========================================================================
1127    // Configuration Source Integration
1128    // ========================================================================
1129
1130    /// Merges configuration from a `ConfigSource`
1131    ///
1132    /// Loads all key-value pairs from the given source and merges them into
1133    /// this configuration. Existing non-final properties are overwritten;
1134    /// final properties are preserved and cause an error if the source tries
1135    /// to overwrite them.
1136    ///
1137    /// # Parameters
1138    ///
1139    /// * `source` - The configuration source to load from
1140    ///
1141    /// # Returns
1142    ///
1143    /// Returns `Ok(())` on success, or a `ConfigError` on failure
1144    ///
1145    /// # Examples
1146    ///
1147    /// ```rust
1148    /// use qubit_config::Config;
1149    /// use qubit_config::source::{
1150    ///     CompositeConfigSource, ConfigSource,
1151    ///     EnvConfigSource, TomlConfigSource,
1152    /// };
1153    ///
1154    /// let mut composite = CompositeConfigSource::new();
1155    /// let path = std::env::temp_dir().join(format!(
1156    ///     "qubit-config-doc-{}.toml",
1157    ///     std::process::id()
1158    /// ));
1159    /// std::fs::write(&path, "app.name = \"demo\"").unwrap();
1160    /// composite.add(TomlConfigSource::from_file(&path));
1161    /// composite.add(EnvConfigSource::with_prefix("APP_"));
1162    ///
1163    /// let mut config = Config::new();
1164    /// config.merge_from_source(&composite).unwrap();
1165    /// std::fs::remove_file(&path).unwrap();
1166    /// ```
1167    #[inline]
1168    pub fn merge_from_source(&mut self, source: &dyn ConfigSource) -> ConfigResult<()> {
1169        source.load(self)
1170    }
1171
1172    // ========================================================================
1173    // Prefix Traversal and Sub-tree Extraction (v0.4.0)
1174    // ========================================================================
1175
1176    /// Iterates over all configuration entries as `(key, &Property)` pairs.
1177    ///
1178    /// # Returns
1179    ///
1180    /// An iterator yielding `(&str, &Property)` tuples.
1181    ///
1182    /// # Examples
1183    ///
1184    /// ```rust
1185    /// use qubit_config::Config;
1186    ///
1187    /// let mut config = Config::new();
1188    /// config.set("host", "localhost").unwrap();
1189    /// config.set("port", 8080).unwrap();
1190    ///
1191    /// for (key, prop) in config.iter() {
1192    ///     println!("{} = {:?}", key, prop);
1193    /// }
1194    /// ```
1195    #[inline]
1196    pub fn iter(&self) -> impl Iterator<Item = (&str, &Property)> {
1197        self.properties.iter().map(|(k, v)| (k.as_str(), v))
1198    }
1199
1200    /// Iterates over all configuration entries whose key starts with `prefix`.
1201    ///
1202    /// # Parameters
1203    ///
1204    /// * `prefix` - The key prefix to filter by (e.g., `"http."`)
1205    ///
1206    /// # Returns
1207    ///
1208    /// An iterator of `(&str, &Property)` whose keys start with `prefix`.
1209    ///
1210    /// # Examples
1211    ///
1212    /// ```rust
1213    /// use qubit_config::Config;
1214    ///
1215    /// let mut config = Config::new();
1216    /// config.set("http.host", "localhost").unwrap();
1217    /// config.set("http.port", 8080).unwrap();
1218    /// config.set("db.host", "dbhost").unwrap();
1219    ///
1220    /// let http_entries: Vec<_> = config.iter_prefix("http.").collect();
1221    /// assert_eq!(http_entries.len(), 2);
1222    /// ```
1223    #[inline]
1224    pub fn iter_prefix<'a>(
1225        &'a self,
1226        prefix: &'a str,
1227    ) -> impl Iterator<Item = (&'a str, &'a Property)> {
1228        self.properties
1229            .iter()
1230            .filter(move |(k, _)| k.starts_with(prefix))
1231            .map(|(k, v)| (k.as_str(), v))
1232    }
1233
1234    /// Returns `true` if any configuration key starts with `prefix`.
1235    ///
1236    /// # Parameters
1237    ///
1238    /// * `prefix` - The key prefix to check
1239    ///
1240    /// # Returns
1241    ///
1242    /// `true` if at least one key starts with `prefix`, `false` otherwise.
1243    ///
1244    /// # Examples
1245    ///
1246    /// ```rust
1247    /// use qubit_config::Config;
1248    ///
1249    /// let mut config = Config::new();
1250    /// config.set("http.host", "localhost").unwrap();
1251    ///
1252    /// assert!(config.contains_prefix("http."));
1253    /// assert!(!config.contains_prefix("db."));
1254    /// ```
1255    #[inline]
1256    pub fn contains_prefix(&self, prefix: &str) -> bool {
1257        self.properties.keys().any(|k| k.starts_with(prefix))
1258    }
1259
1260    /// Extracts a sub-configuration for keys matching `prefix`.
1261    ///
1262    /// # Parameters
1263    ///
1264    /// * `prefix` - The key prefix to extract (e.g., `"http"`)
1265    /// * `strip_prefix` - When `true`, removes `prefix` and the following dot
1266    ///   from keys in the result; when `false`, keys are copied unchanged.
1267    ///
1268    /// # Returns
1269    ///
1270    /// A new `Config` containing only the matching entries.
1271    ///
1272    /// # Examples
1273    ///
1274    /// ```rust
1275    /// use qubit_config::Config;
1276    ///
1277    /// let mut config = Config::new();
1278    /// config.set("http.host", "localhost").unwrap();
1279    /// config.set("http.port", 8080).unwrap();
1280    /// config.set("db.host", "dbhost").unwrap();
1281    ///
1282    /// let http_config = config.subconfig("http", true).unwrap();
1283    /// assert!(http_config.contains("host"));
1284    /// assert!(http_config.contains("port"));
1285    /// assert!(!http_config.contains("db.host"));
1286    /// ```
1287    pub fn subconfig(&self, prefix: &str, strip_prefix: bool) -> ConfigResult<Config> {
1288        let mut sub = Config::new();
1289        sub.enable_variable_substitution = self.enable_variable_substitution;
1290        sub.max_substitution_depth = self.max_substitution_depth;
1291
1292        // Empty prefix means "all keys"
1293        if prefix.is_empty() {
1294            for (k, v) in &self.properties {
1295                sub.properties.insert(k.clone(), v.clone());
1296            }
1297            return Ok(sub);
1298        }
1299
1300        let full_prefix = format!("{prefix}.");
1301
1302        for (k, v) in &self.properties {
1303            if k == prefix || k.starts_with(&full_prefix) {
1304                let new_key = if strip_prefix {
1305                    if k == prefix {
1306                        prefix.to_string()
1307                    } else {
1308                        k[full_prefix.len()..].to_string()
1309                    }
1310                } else {
1311                    k.clone()
1312                };
1313                sub.properties.insert(new_key, v.clone());
1314            }
1315        }
1316
1317        Ok(sub)
1318    }
1319
1320    // ========================================================================
1321    // Optional and Null Semantics (v0.4.0)
1322    // ========================================================================
1323
1324    /// Returns `true` if the property exists but has no value (empty / null).
1325    ///
1326    /// This distinguishes between:
1327    /// - Key does not exist → `contains()` returns `false`
1328    /// - Key exists but is empty/null → `is_null()` returns `true`
1329    ///
1330    /// # Parameters
1331    ///
1332    /// * `name` - Configuration item name
1333    ///
1334    /// # Returns
1335    ///
1336    /// `true` if the property exists and has no values (is empty).
1337    ///
1338    /// # Examples
1339    ///
1340    /// ```rust
1341    /// use qubit_config::Config;
1342    /// use qubit_common::DataType;
1343    ///
1344    /// let mut config = Config::new();
1345    /// config.set_null("nullable", DataType::String).unwrap();
1346    ///
1347    /// assert!(config.is_null("nullable"));
1348    /// assert!(!config.is_null("missing"));
1349    /// ```
1350    pub fn is_null(&self, name: &str) -> bool {
1351        self.properties
1352            .get(name)
1353            .map(|p| p.is_empty())
1354            .unwrap_or(false)
1355    }
1356
1357    /// Gets an optional configuration value.
1358    ///
1359    /// Distinguishes between three states:
1360    /// - `Ok(Some(value))` – key exists and has a value
1361    /// - `Ok(None)` – key does not exist, **or** exists but is null/empty
1362    /// - `Err(e)` – key exists and has a value, but conversion failed
1363    ///
1364    /// # Type Parameters
1365    ///
1366    /// * `T` - Target type
1367    ///
1368    /// # Parameters
1369    ///
1370    /// * `name` - Configuration item name
1371    ///
1372    /// # Returns
1373    ///
1374    /// `Ok(Some(value))`, `Ok(None)`, or `Err` as described above.
1375    ///
1376    /// # Examples
1377    ///
1378    /// ```rust
1379    /// use qubit_config::Config;
1380    ///
1381    /// let mut config = Config::new();
1382    /// config.set("port", 8080).unwrap();
1383    ///
1384    /// let port: Option<i32> = config.get_optional("port").unwrap();
1385    /// assert_eq!(port, Some(8080));
1386    ///
1387    /// let missing: Option<i32> = config.get_optional("missing").unwrap();
1388    /// assert_eq!(missing, None);
1389    /// ```
1390    pub fn get_optional<T>(&self, name: &str) -> ConfigResult<Option<T>>
1391    where
1392        MultiValues: MultiValuesFirstGetter<T>,
1393    {
1394        self.get_optional_when_present(name, |c| c.get(name))
1395    }
1396
1397    /// Gets an optional list of configuration values.
1398    ///
1399    /// See also [`Self::get_optional_string_list`] for optional string lists
1400    /// with variable substitution.
1401    ///
1402    /// Distinguishes between three states:
1403    /// - `Ok(Some(vec))` – key exists and has values
1404    /// - `Ok(None)` – key does not exist, **or** exists but is null/empty
1405    /// - `Err(e)` – key exists and has values, but conversion failed
1406    ///
1407    /// # Type Parameters
1408    ///
1409    /// * `T` - Target element type
1410    ///
1411    /// # Parameters
1412    ///
1413    /// * `name` - Configuration item name
1414    ///
1415    /// # Returns
1416    ///
1417    /// `Ok(Some(vec))`, `Ok(None)`, or `Err` as described above.
1418    ///
1419    /// # Examples
1420    ///
1421    /// ```rust
1422    /// use qubit_config::Config;
1423    ///
1424    /// let mut config = Config::new();
1425    /// config.set("ports", vec![8080, 8081]).unwrap();
1426    ///
1427    /// let ports: Option<Vec<i32>> = config.get_optional_list("ports").unwrap();
1428    /// assert_eq!(ports, Some(vec![8080, 8081]));
1429    ///
1430    /// let missing: Option<Vec<i32>> = config.get_optional_list("missing").unwrap();
1431    /// assert_eq!(missing, None);
1432    /// ```
1433    pub fn get_optional_list<T>(&self, name: &str) -> ConfigResult<Option<Vec<T>>>
1434    where
1435        MultiValues: MultiValuesGetter<T>,
1436    {
1437        self.get_optional_when_present(name, |c| c.get_list(name))
1438    }
1439
1440    /// Gets an optional string (with variable substitution when enabled).
1441    ///
1442    /// Same semantics as [`Self::get_optional`], but values are read via
1443    /// [`Self::get_string`], so `${...}` substitution applies when enabled.
1444    ///
1445    /// # Parameters
1446    ///
1447    /// * `name` - Configuration item name
1448    ///
1449    /// # Returns
1450    ///
1451    /// `Ok(Some(s))`, `Ok(None)`, or `Err` as for [`Self::get_optional`].
1452    ///
1453    /// # Examples
1454    ///
1455    /// ```rust
1456    /// use qubit_config::Config;
1457    ///
1458    /// let mut config = Config::new();
1459    /// config.set("base", "http://localhost").unwrap();
1460    /// config.set("api", "${base}/api").unwrap();
1461    ///
1462    /// let api = config.get_optional_string("api").unwrap();
1463    /// assert_eq!(api.as_deref(), Some("http://localhost/api"));
1464    ///
1465    /// let missing = config.get_optional_string("missing").unwrap();
1466    /// assert_eq!(missing, None);
1467    /// ```
1468    pub fn get_optional_string(&self, name: &str) -> ConfigResult<Option<String>> {
1469        self.get_optional_when_present(name, |c| c.get_string(name))
1470    }
1471
1472    /// Gets an optional string list (substitution per element when enabled).
1473    ///
1474    /// Same semantics as [`Self::get_optional_list`], but elements use
1475    /// [`Self::get_string_list`] (same `${...}` rules as [`Self::get_string`]).
1476    ///
1477    /// # Parameters
1478    ///
1479    /// * `name` - Configuration item name
1480    ///
1481    /// # Returns
1482    ///
1483    /// `Ok(Some(vec))`, `Ok(None)`, or `Err` like [`Self::get_optional_list`].
1484    ///
1485    /// # Examples
1486    ///
1487    /// ```rust
1488    /// use qubit_config::Config;
1489    ///
1490    /// let mut config = Config::new();
1491    /// config.set("root", "/opt/app").unwrap();
1492    /// config.set("paths", vec!["${root}/bin", "${root}/lib"]).unwrap();
1493    ///
1494    /// let paths = config.get_optional_string_list("paths").unwrap();
1495    /// assert_eq!(
1496    ///     paths,
1497    ///     Some(vec![
1498    ///         "/opt/app/bin".to_string(),
1499    ///         "/opt/app/lib".to_string(),
1500    ///     ]),
1501    /// );
1502    /// ```
1503    pub fn get_optional_string_list(&self, name: &str) -> ConfigResult<Option<Vec<String>>> {
1504        self.get_optional_when_present(name, |c| c.get_string_list(name))
1505    }
1506
1507    // ========================================================================
1508    // Structured Config Deserialization (v0.4.0)
1509    // ========================================================================
1510
1511    /// Deserializes the subtree at `prefix` into `T` using `serde`.
1512    /// String values inside the generated serde value apply the same
1513    /// `${...}` substitution rules as [`Self::get_string`] and
1514    /// [`Self::get_string_list`] when substitution is enabled.
1515    ///
1516    /// Keys under `prefix` (prefix and trailing dot removed) form a flat map
1517    /// for `serde`, for example:
1518    ///
1519    /// ```rust
1520    /// #[derive(serde::Deserialize)]
1521    /// struct HttpOptions {
1522    ///     host: String,
1523    ///     port: u16,
1524    /// }
1525    /// ```
1526    ///
1527    /// can be populated from config keys `http.host` and `http.port` by calling
1528    /// `config.deserialize::<HttpOptions>("http")`.
1529    ///
1530    /// # Type Parameters
1531    ///
1532    /// * `T` - Target type, must implement `serde::de::DeserializeOwned`
1533    ///
1534    /// # Parameters
1535    ///
1536    /// * `prefix` - Key prefix for the struct fields (`""` means the root map)
1537    ///
1538    /// # Returns
1539    ///
1540    /// The deserialized `T`, or a [`ConfigError::DeserializeError`] on failure.
1541    ///
1542    /// # Examples
1543    ///
1544    /// ```rust
1545    /// use qubit_config::Config;
1546    /// use serde::Deserialize;
1547    ///
1548    /// #[derive(Deserialize, Debug, PartialEq)]
1549    /// struct Server {
1550    ///     host: String,
1551    ///     port: i32,
1552    /// }
1553    ///
1554    /// let mut config = Config::new();
1555    /// config.set("server.host", "localhost").unwrap();
1556    /// config.set("server.port", 8080).unwrap();
1557    ///
1558    /// let server: Server = config.deserialize("server").unwrap();
1559    /// assert_eq!(server.host, "localhost");
1560    /// assert_eq!(server.port, 8080);
1561    /// ```
1562    pub fn deserialize<T>(&self, prefix: &str) -> ConfigResult<T>
1563    where
1564        T: DeserializeOwned,
1565    {
1566        let sub = self.subconfig(prefix, true)?;
1567
1568        let mut properties = sub.properties.iter().collect::<Vec<_>>();
1569        properties.sort_by_key(|(left_key, _)| *left_key);
1570
1571        let mut map = Map::new();
1572        for (key, prop) in properties {
1573            let mut json_val = utils::property_to_json_value(prop);
1574            utils::substitute_json_strings_with_fallback(&mut json_val, &sub, self)?;
1575            utils::insert_deserialize_value(&mut map, key, json_val);
1576        }
1577
1578        let json_obj = Value::Object(map);
1579
1580        from_value(json_obj).map_err(|e| ConfigError::DeserializeError {
1581            path: prefix.to_string(),
1582            message: e.to_string(),
1583        })
1584    }
1585
1586    /// Inserts or replaces a property using an explicit [`Property`] object.
1587    ///
1588    /// This method enforces two invariants:
1589    ///
1590    /// - `name` must exactly match `property.name()`
1591    /// - existing final properties cannot be overridden
1592    ///
1593    /// # Parameters
1594    ///
1595    /// * `name` - Target key in this config.
1596    /// * `property` - Property to store under `name`.
1597    ///
1598    /// # Returns
1599    ///
1600    /// `Ok(())` on success.
1601    ///
1602    /// # Errors
1603    ///
1604    /// - [`ConfigError::MergeError`] when `name` and `property.name()` differ.
1605    /// - [`ConfigError::PropertyIsFinal`] when trying to override a final
1606    ///   property.
1607    pub fn insert_property(&mut self, name: &str, property: Property) -> ConfigResult<()> {
1608        if property.name() != name {
1609            return Err(ConfigError::MergeError(format!(
1610                "Property name mismatch: key '{name}' != property '{}'",
1611                property.name()
1612            )));
1613        }
1614        self.ensure_property_not_final(name)?;
1615        self.properties.insert(name.to_string(), property);
1616        Ok(())
1617    }
1618
1619    /// Sets a key to a typed null/empty value.
1620    ///
1621    /// This is the preferred public API for representing null/empty values
1622    /// without exposing raw mutable access to the internal map.
1623    ///
1624    /// # Parameters
1625    ///
1626    /// * `name` - Configuration item name.
1627    /// * `data_type` - Data type metadata for the empty value.
1628    ///
1629    /// # Returns
1630    ///
1631    /// `Ok(())` on success.
1632    ///
1633    /// # Errors
1634    ///
1635    /// - [`ConfigError::PropertyIsFinal`] when trying to override a final
1636    ///   property.
1637    #[inline]
1638    pub fn set_null(&mut self, name: &str, data_type: DataType) -> ConfigResult<()> {
1639        self.insert_property(
1640            name,
1641            Property::with_value(name, MultiValues::Empty(data_type)),
1642        )
1643    }
1644}
1645
1646impl ConfigReader for Config {
1647    #[inline]
1648    fn is_enable_variable_substitution(&self) -> bool {
1649        Config::is_enable_variable_substitution(self)
1650    }
1651
1652    #[inline]
1653    fn max_substitution_depth(&self) -> usize {
1654        Config::max_substitution_depth(self)
1655    }
1656
1657    #[inline]
1658    fn description(&self) -> Option<&str> {
1659        Config::description(self)
1660    }
1661
1662    #[inline]
1663    fn get_property(&self, name: &str) -> Option<&Property> {
1664        Config::get_property(self, name)
1665    }
1666
1667    #[inline]
1668    fn len(&self) -> usize {
1669        Config::len(self)
1670    }
1671
1672    #[inline]
1673    fn is_empty(&self) -> bool {
1674        Config::is_empty(self)
1675    }
1676
1677    #[inline]
1678    fn keys(&self) -> Vec<String> {
1679        Config::keys(self)
1680    }
1681
1682    #[inline]
1683    fn contains(&self, name: &str) -> bool {
1684        Config::contains(self, name)
1685    }
1686
1687    #[inline]
1688    fn get<T>(&self, name: &str) -> ConfigResult<T>
1689    where
1690        MultiValues: MultiValuesFirstGetter<T>,
1691    {
1692        Config::get(self, name)
1693    }
1694
1695    #[inline]
1696    fn get_list<T>(&self, name: &str) -> ConfigResult<Vec<T>>
1697    where
1698        MultiValues: MultiValuesGetter<T>,
1699    {
1700        Config::get_list(self, name)
1701    }
1702
1703    #[inline]
1704    fn get_optional<T>(&self, name: &str) -> ConfigResult<Option<T>>
1705    where
1706        MultiValues: MultiValuesFirstGetter<T>,
1707    {
1708        Config::get_optional(self, name)
1709    }
1710
1711    #[inline]
1712    fn get_optional_list<T>(&self, name: &str) -> ConfigResult<Option<Vec<T>>>
1713    where
1714        MultiValues: MultiValuesGetter<T>,
1715    {
1716        Config::get_optional_list(self, name)
1717    }
1718
1719    #[inline]
1720    fn contains_prefix(&self, prefix: &str) -> bool {
1721        Config::contains_prefix(self, prefix)
1722    }
1723
1724    #[inline]
1725    fn iter_prefix<'a>(
1726        &'a self,
1727        prefix: &'a str,
1728    ) -> Box<dyn Iterator<Item = (&'a str, &'a Property)> + 'a> {
1729        Box::new(Config::iter_prefix(self, prefix))
1730    }
1731
1732    #[inline]
1733    fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (&'a str, &'a Property)> + 'a> {
1734        Box::new(Config::iter(self))
1735    }
1736
1737    #[inline]
1738    fn is_null(&self, name: &str) -> bool {
1739        Config::is_null(self, name)
1740    }
1741
1742    #[inline]
1743    fn subconfig(&self, prefix: &str, strip_prefix: bool) -> ConfigResult<Config> {
1744        Config::subconfig(self, prefix, strip_prefix)
1745    }
1746
1747    #[inline]
1748    fn deserialize<T>(&self, prefix: &str) -> ConfigResult<T>
1749    where
1750        T: DeserializeOwned,
1751    {
1752        Config::deserialize(self, prefix)
1753    }
1754
1755    #[inline]
1756    fn prefix_view(&self, prefix: &str) -> ConfigPrefixView<'_> {
1757        Config::prefix_view(self, prefix)
1758    }
1759}
1760
1761impl Default for Config {
1762    /// Creates a new default configuration
1763    ///
1764    /// # Returns
1765    ///
1766    /// Returns a new configuration instance
1767    ///
1768    /// # Examples
1769    ///
1770    /// ```rust
1771    /// use qubit_config::Config;
1772    ///
1773    /// let config = Config::default();
1774    /// assert!(config.is_empty());
1775    /// ```
1776    #[inline]
1777    fn default() -> Self {
1778        Self::new()
1779    }
1780}