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}