Skip to main content

qubit_config/
config.rs

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