1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use std::collections::BTreeMap;
5
6use use_config_key::ConfigPath;
7use use_config_source::ConfigSource;
8use use_config_value::ConfigValue;
9
10#[derive(Clone, Debug, PartialEq)]
12pub struct ConfigLayer {
13 pub source: ConfigSource,
15 values: BTreeMap<ConfigPath, ConfigValue>,
16}
17
18impl ConfigLayer {
19 #[must_use]
21 pub const fn new(source: ConfigSource) -> Self {
22 Self {
23 source,
24 values: BTreeMap::new(),
25 }
26 }
27
28 #[must_use]
30 pub const fn from_values(
31 source: ConfigSource,
32 values: BTreeMap<ConfigPath, ConfigValue>,
33 ) -> Self {
34 Self { source, values }
35 }
36
37 #[must_use]
39 pub const fn source(&self) -> &ConfigSource {
40 &self.source
41 }
42
43 #[must_use]
45 pub const fn values(&self) -> &BTreeMap<ConfigPath, ConfigValue> {
46 &self.values
47 }
48
49 #[must_use]
51 pub fn len(&self) -> usize {
52 self.values.len()
53 }
54
55 #[must_use]
57 pub fn is_empty(&self) -> bool {
58 self.values.is_empty()
59 }
60
61 pub fn insert(&mut self, path: ConfigPath, value: ConfigValue) -> Option<ConfigValue> {
63 self.values.insert(path, value)
64 }
65
66 #[must_use]
68 pub fn get(&self, path: &ConfigPath) -> Option<&ConfigValue> {
69 self.values.get(path)
70 }
71
72 pub fn iter(&self) -> impl Iterator<Item = (&ConfigPath, &ConfigValue)> {
74 self.values.iter()
75 }
76}
77
78#[must_use]
83pub fn merge_two_layers(
84 first: &ConfigLayer,
85 second: &ConfigLayer,
86) -> BTreeMap<ConfigPath, ConfigValue> {
87 merge_layers([first, second])
88}
89
90#[must_use]
97pub fn merge_layers<'a, I>(layers: I) -> BTreeMap<ConfigPath, ConfigValue>
98where
99 I: IntoIterator<Item = &'a ConfigLayer>,
100{
101 let mut ordered_layers: Vec<_> = layers.into_iter().enumerate().collect();
102
103 ordered_layers.sort_by(|(left_index, left), (right_index, right)| {
104 left.source
105 .priority()
106 .cmp(&right.source.priority())
107 .then_with(|| left_index.cmp(right_index))
108 });
109
110 let mut merged = BTreeMap::new();
111
112 for (_, layer) in ordered_layers {
113 for (path, value) in layer.iter() {
114 merged.insert(path.clone(), value.clone());
115 }
116 }
117
118 merged
119}
120
121#[cfg(test)]
122mod tests {
123 use super::{ConfigLayer, merge_layers, merge_two_layers};
124 use use_config_key::ConfigPath;
125 use use_config_source::{ConfigSource, ConfigSourceKind};
126 use use_config_value::ConfigValue;
127
128 #[test]
129 fn lower_priority_default_is_overridden_by_higher_priority_override() {
130 let path = ConfigPath::parse("server.port").expect("path should parse");
131 let mut defaults = ConfigLayer::new(ConfigSource::unnamed(ConfigSourceKind::Default, 0));
132 let mut overrides = ConfigLayer::new(ConfigSource::unnamed(ConfigSourceKind::Override, 10));
133
134 defaults.insert(path.clone(), ConfigValue::from(8080_i64));
135 overrides.insert(path.clone(), ConfigValue::from(9090_i64));
136
137 let merged = merge_two_layers(&defaults, &overrides);
138
139 assert_eq!(merged.get(&path).and_then(ConfigValue::as_i64), Some(9090));
140 }
141
142 #[test]
143 fn later_layer_wins_when_priority_is_equal() {
144 let path = ConfigPath::parse("mode").expect("path should parse");
145 let source = ConfigSource::unnamed(ConfigSourceKind::Runtime, 5);
146 let mut first = ConfigLayer::new(source.clone());
147 let mut second = ConfigLayer::new(source);
148
149 first.insert(path.clone(), ConfigValue::from("first"));
150 second.insert(path.clone(), ConfigValue::from("second"));
151
152 let merged = merge_layers([&first, &second]);
153
154 assert_eq!(
155 merged.get(&path).and_then(ConfigValue::as_str),
156 Some("second")
157 );
158 }
159
160 #[test]
161 fn unrelated_keys_are_preserved() {
162 let host = ConfigPath::parse("server.host").expect("path should parse");
163 let port = ConfigPath::parse("server.port").expect("path should parse");
164 let mut defaults = ConfigLayer::new(ConfigSource::unnamed(ConfigSourceKind::Default, 0));
165 let mut overrides = ConfigLayer::new(ConfigSource::unnamed(ConfigSourceKind::Override, 10));
166
167 defaults.insert(host.clone(), ConfigValue::from("localhost"));
168 overrides.insert(port.clone(), ConfigValue::from(9090_i64));
169
170 let merged = merge_layers([&defaults, &overrides]);
171
172 assert_eq!(
173 merged.get(&host).and_then(ConfigValue::as_str),
174 Some("localhost")
175 );
176 assert_eq!(merged.get(&port).and_then(ConfigValue::as_i64), Some(9090));
177 }
178
179 #[test]
180 fn merge_order_is_deterministic() {
181 let path = ConfigPath::parse("answer").expect("path should parse");
182 let mut low = ConfigLayer::new(ConfigSource::unnamed(ConfigSourceKind::Default, 0));
183 let mut middle = ConfigLayer::new(ConfigSource::unnamed(ConfigSourceKind::Runtime, 5));
184 let mut high = ConfigLayer::new(ConfigSource::unnamed(ConfigSourceKind::Override, 10));
185
186 high.insert(path.clone(), ConfigValue::from(3_i64));
187 low.insert(path.clone(), ConfigValue::from(1_i64));
188 middle.insert(path.clone(), ConfigValue::from(2_i64));
189
190 let merged = merge_layers([&high, &low, &middle]);
191
192 assert_eq!(merged.get(&path).and_then(ConfigValue::as_i64), Some(3));
193 }
194}