Skip to main content

qubit_config/source/
composite_config_source.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! # Composite Configuration Source
10//!
11//! Merges configuration from multiple sources in order.
12//!
13//! Sources are applied in the order they are added. Later sources override
14//! earlier sources for the same key (unless the property is marked as final).
15//!
16//! # Examples
17//!
18//! ```rust
19//! use qubit_config::source::{
20//!     CompositeConfigSource, ConfigSource, TomlConfigSource,
21//! };
22//! use qubit_config::Config;
23//!
24//! let mut composite = CompositeConfigSource::new();
25//! let temp_dir = tempfile::tempdir().unwrap();
26//! let defaults = temp_dir.path().join("defaults.toml");
27//! let override_file = temp_dir.path().join("config.toml");
28//! std::fs::write(&defaults, "port = 80\n").unwrap();
29//! std::fs::write(&override_file, "port = 8080\n").unwrap();
30//! composite.add(TomlConfigSource::from_file(defaults));
31//! composite.add(TomlConfigSource::from_file(override_file));
32//!
33//! let mut config = Config::new();
34//! composite.load(&mut config).unwrap();
35//! assert_eq!(config.get::<i64>("port").unwrap(), 8080);
36//! ```
37//!
38//! # Author
39//!
40//! Haixing Hu
41
42use crate::{Config, ConfigResult};
43
44use super::ConfigSource;
45
46/// Configuration source that merges multiple sources in order
47///
48/// # Author
49///
50/// Haixing Hu
51pub struct CompositeConfigSource {
52    sources: Vec<Box<dyn ConfigSource>>,
53}
54
55impl CompositeConfigSource {
56    /// Creates a new empty `CompositeConfigSource`.
57    ///
58    /// # Returns
59    ///
60    /// An empty composite with no inner sources.
61    #[inline]
62    pub fn new() -> Self {
63        Self {
64            sources: Vec::new(),
65        }
66    }
67
68    /// Adds a configuration source
69    ///
70    /// Sources are applied in the order they are added. Later sources override
71    /// earlier sources for the same key.
72    ///
73    /// # Parameters
74    ///
75    /// * `source` - The configuration source to add
76    ///
77    /// # Returns
78    ///
79    /// `self` for method chaining.
80    #[inline]
81    pub fn add<S: ConfigSource + 'static>(&mut self, source: S) -> &mut Self {
82        self.sources.push(Box::new(source));
83        self
84    }
85
86    /// Returns the number of sources in this composite.
87    ///
88    /// # Returns
89    ///
90    /// The length of the internal source list.
91    #[inline]
92    pub fn len(&self) -> usize {
93        self.sources.len()
94    }
95
96    /// Returns `true` if this composite has no sources.
97    ///
98    /// # Returns
99    ///
100    /// `true` when [`Self::len`] is zero.
101    #[inline]
102    pub fn is_empty(&self) -> bool {
103        self.sources.is_empty()
104    }
105}
106
107impl Default for CompositeConfigSource {
108    #[inline]
109    fn default() -> Self {
110        Self::new()
111    }
112}
113
114impl ConfigSource for CompositeConfigSource {
115    fn load(&self, config: &mut Config) -> ConfigResult<()> {
116        for source in &self.sources {
117            source.load(config)?;
118        }
119        Ok(())
120    }
121}