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