Skip to main content

qubit_config/
config.rs

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