Skip to main content

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