storm_config/builder.rs
1use std::str::FromStr;
2
3use crate::config::Config;
4use crate::errors::Result;
5use crate::map::Map;
6use crate::path::Expression;
7#[cfg(feature = "async")]
8use crate::source::AsyncSource;
9use crate::source::Source;
10use crate::value::Value;
11
12/// A configuration builder
13///
14/// It registers ordered sources of configuration to later build consistent [`Config`] from them.
15/// Configuration sources it defines are defaults, [`Source`]s and overrides.
16///
17/// Defaults are always loaded first and can be overwritten by any of two other sources.
18/// Overrides are always loaded last, thus cannot be overridden.
19/// Both can be only set explicitly key by key in code
20/// using [`set_default`](Self::set_default) or [`set_override`](Self::set_override).
21///
22/// An intermediate category, [`Source`], set groups of keys at once implicitly using data coming from external sources
23/// like files, environment variables or others that one implements. Defining a [`Source`] is as simple as implementing
24/// a trait for a struct.
25///
26/// Adding sources, setting defaults and overrides does not invoke any I/O nor builds a config.
27/// It happens on demand when [`build`](Self::build) (or its alternative) is called.
28/// Therefore all errors, related to any of the [`Source`] will only show up then.
29///
30/// # Sync and async builder
31///
32/// [`ConfigBuilder`] uses type parameter to keep track of builder state.
33///
34/// In [`DefaultState`] builder only supports [`Source`]s
35///
36/// In [`AsyncState`] it supports both [`Source`]s and [`AsyncSource`]s at the price of building using `async fn`.
37///
38/// # Examples
39///
40/// ```rust
41/// use storm_config::*;
42/// use std::error::Error;
43/// fn main() -> Result<(), Box<dyn Error>> {
44/// let mut builder = Config::builder()
45/// .set_default("default", "1")?
46/// .add_source(File::new("config/settings", FileFormat::Json))
47/// // .add_async_source(...)
48/// .set_override("override", "1")?;
49///
50/// match builder.build() {
51/// Ok(config) => {
52/// // use your config
53/// },
54/// Err(e) => {
55/// // something went wrong
56/// }
57/// }
58/// Ok(())
59/// }
60/// ```
61///
62/// If any [`AsyncSource`] is used, the builder will transition to [`AsyncState`].
63/// In such case, it is required to _await_ calls to [`build`](Self::build) and its non-consuming sibling.
64///
65/// Calls can be not chained as well
66/// ```rust
67/// use std::error::Error;
68/// use storm_config::*;
69/// fn main() -> Result<(), Box<dyn Error>> {
70/// let mut builder = Config::builder();
71/// builder = builder.set_default("default", "1")?;
72/// builder = builder.add_source(File::new("config/settings", FileFormat::Json));
73/// builder = builder.add_source(File::new("config/settings.prod", FileFormat::Json));
74/// builder = builder.set_override("override", "1")?;
75/// Ok(())
76/// }
77/// ```
78///
79/// Calling [`Config::builder`](Config::builder) yields builder in the default state.
80/// If having an asynchronous state as the initial state is desired, _turbofish_ notation needs to be used.
81/// ```rust
82/// use storm_config::{builder::ConfigBuilder, builder::AsyncState};
83/// let mut builder = ConfigBuilder::<AsyncState>::default();
84/// ```
85///
86/// If for some reason acquiring builder in default state is required without calling [`Config::builder`](Config::builder)
87/// it can also be achieved.
88/// ```rust
89/// use storm_config::{builder::ConfigBuilder, builder::DefaultState};
90/// let mut builder = ConfigBuilder::<DefaultState>::default();
91/// ```
92#[derive(Debug, Clone, Default)]
93#[must_use]
94pub struct ConfigBuilder<St: BuilderState> {
95 defaults: Map<Expression, Value>,
96 overrides: Map<Expression, Value>,
97 state: St,
98}
99
100/// Represents [`ConfigBuilder`] state.
101pub trait BuilderState {}
102
103/// Represents data specific to builder in default, sychronous state, without support for async.
104#[derive(Debug, Default, Clone)]
105pub struct DefaultState {
106 sources: Vec<Box<dyn Source + Send + Sync>>,
107}
108
109// Dummy useless struct
110//
111// This struct exists only to avoid the semver break
112// which would be implied by removing it.
113//
114// This struct cannot be used for anything useful.
115// (Nor can it be extended without a semver break, either.)
116//
117// In a future release, we should have
118// type AsyncConfigBuilder = ConfigBuilder<AsyncState>;
119#[deprecated = "AsyncConfigBuilder is useless. Use ConfigBuilder<AsyncState>"]
120#[doc(hidden)]
121#[derive(Debug, Clone, Default)]
122pub struct AsyncConfigBuilder {}
123
124/// Represents data specific to builder in asychronous state, with support for async.
125#[derive(Debug, Default, Clone)]
126pub struct AsyncState {
127 sources: Vec<SourceType>,
128}
129
130#[derive(Debug, Clone)]
131enum SourceType {
132 Sync(Box<dyn Source + Send + Sync>),
133 #[cfg(feature = "async")]
134 Async(Box<dyn AsyncSource + Send + Sync>),
135}
136
137impl BuilderState for DefaultState {}
138impl BuilderState for AsyncState {}
139
140impl<St: BuilderState> ConfigBuilder<St> {
141 // operations allowed in any state
142
143 /// Set a default `value` at `key`
144 ///
145 /// This value can be overwritten by any [`Source`], [`AsyncSource`] or override.
146 ///
147 /// # Errors
148 ///
149 /// Fails if `Expression::from_str(key)` fails.
150 pub fn set_default<S, T>(mut self, key: S, value: T) -> Result<Self>
151 where
152 S: AsRef<str>,
153 T: Into<Value>,
154 {
155 self.defaults.insert(Expression::from_str(key.as_ref())?, value.into());
156 Ok(self)
157 }
158
159 /// Set an override
160 ///
161 /// This function sets an overwrite value. It will not be altered by any default, [`Source`] nor [`AsyncSource`]
162 ///
163 /// # Errors
164 ///
165 /// Fails if `Expression::from_str(key)` fails.
166 pub fn set_override<S, T>(mut self, key: S, value: T) -> Result<Self>
167 where
168 S: AsRef<str>,
169 T: Into<Value>,
170 {
171 self.overrides.insert(Expression::from_str(key.as_ref())?, value.into());
172 Ok(self)
173 }
174
175 /// Sets an override if value is Some(_)
176 ///
177 /// This function sets an overwrite value if Some(_) is passed. If None is passed, this function does nothing.
178 /// It will not be altered by any default, [`Source`] nor [`AsyncSource`]
179 ///
180 /// # Errors
181 ///
182 /// Fails if `Expression::from_str(key)` fails.
183 pub fn set_override_option<S, T>(mut self, key: S, value: Option<T>) -> Result<Self>
184 where
185 S: AsRef<str>,
186 T: Into<Value>,
187 {
188 if let Some(value) = value {
189 self.overrides.insert(Expression::from_str(key.as_ref())?, value.into());
190 }
191 Ok(self)
192 }
193}
194
195impl ConfigBuilder<DefaultState> {
196 // operations allowed in sync state
197
198 /// Registers new [`Source`] in this builder.
199 ///
200 /// Calling this method does not invoke any I/O. [`Source`] is only saved in internal register for later use.
201 pub fn add_source<T>(mut self, source: T) -> Self
202 where
203 T: Source + Send + Sync + 'static,
204 {
205 self.state.sources.push(Box::new(source));
206 self
207 }
208
209 /// Registers new [`AsyncSource`] in this builder and forces transition to [`AsyncState`].
210 ///
211 /// Calling this method does not invoke any I/O. [`AsyncSource`] is only saved in internal register for later use.
212 #[cfg(feature = "async")]
213 pub fn add_async_source<T>(self, source: T) -> ConfigBuilder<AsyncState>
214 where
215 T: AsyncSource + Send + Sync + 'static,
216 {
217 let async_state = ConfigBuilder {
218 state: AsyncState { sources: self.state.sources.into_iter().map(SourceType::Sync).collect() },
219 defaults: self.defaults,
220 overrides: self.overrides,
221 };
222
223 async_state.add_async_source(source)
224 }
225
226 /// Reads all registered [`Source`]s.
227 ///
228 /// This is the method that invokes all I/O operations.
229 /// For a non consuming alternative see [`build_cloned`](Self::build_cloned)
230 ///
231 /// # Errors
232 /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
233 /// this method returns error.
234 pub fn build(self) -> Result<Config> {
235 Self::build_internal(self.defaults, self.overrides, &self.state.sources)
236 }
237
238 /// Reads all registered [`Source`]s.
239 ///
240 /// Similar to [`build`](Self::build), but it does not take ownership of `ConfigBuilder` to allow later reuse.
241 /// Internally it clones data to achieve it.
242 ///
243 /// # Errors
244 /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
245 /// this method returns error.
246 pub fn build_cloned(&self) -> Result<Config> {
247 Self::build_internal(self.defaults.clone(), self.overrides.clone(), &self.state.sources)
248 }
249
250 fn build_internal(
251 defaults: Map<Expression, Value>,
252 overrides: Map<Expression, Value>,
253 sources: &[Box<dyn Source + Send + Sync>],
254 ) -> Result<Config> {
255 let mut cache: Value = Map::<String, Value>::new().into();
256
257 // Add defaults
258 for (key, val) in defaults {
259 key.set(&mut cache, val);
260 }
261
262 // Add sources
263 sources.collect_to(&mut cache)?;
264
265 // Add overrides
266 for (key, val) in overrides {
267 key.set(&mut cache, val);
268 }
269
270 Ok(Config::new(cache))
271 }
272}
273
274impl ConfigBuilder<AsyncState> {
275 // operations allowed in async state
276
277 /// Registers new [`Source`] in this builder.
278 ///
279 /// Calling this method does not invoke any I/O. [`Source`] is only saved in internal register for later use.
280 pub fn add_source<T>(mut self, source: T) -> Self
281 where
282 T: Source + Send + Sync + 'static,
283 {
284 self.state.sources.push(SourceType::Sync(Box::new(source)));
285 self
286 }
287
288 /// Registers new [`AsyncSource`] in this builder.
289 ///
290 /// Calling this method does not invoke any I/O. [`AsyncSource`] is only saved in internal register for later use.
291 #[cfg(feature = "async")]
292 pub fn add_async_source<T>(mut self, source: T) -> Self
293 where
294 T: AsyncSource + Send + Sync + 'static,
295 {
296 self.state.sources.push(SourceType::Async(Box::new(source)));
297 self
298 }
299
300 /// Reads all registered defaults, [`Source`]s, [`AsyncSource`]s and overrides.
301 ///
302 /// This is the method that invokes all I/O operations.
303 /// For a non consuming alternative see [`build_cloned`](Self::build_cloned)
304 ///
305 /// # Errors
306 /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
307 /// this method returns error.
308 pub async fn build(self) -> Result<Config> {
309 Self::build_internal(self.defaults, self.overrides, &self.state.sources).await
310 }
311
312 /// Reads all registered defaults, [`Source`]s, [`AsyncSource`]s and overrides.
313 ///
314 /// Similar to [`build`](Self::build), but it does not take ownership of `ConfigBuilder` to allow later reuse.
315 /// Internally it clones data to achieve it.
316 ///
317 /// # Errors
318 /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
319 /// this method returns error.
320 pub async fn build_cloned(&self) -> Result<Config> {
321 Self::build_internal(self.defaults.clone(), self.overrides.clone(), &self.state.sources).await
322 }
323
324 async fn build_internal(
325 defaults: Map<Expression, Value>,
326 overrides: Map<Expression, Value>,
327 sources: &[SourceType],
328 ) -> Result<Config> {
329 let mut cache: Value = Map::<String, Value>::new().into();
330
331 // Add defaults
332 for (key, val) in defaults {
333 key.set(&mut cache, val);
334 }
335
336 for source in sources.iter() {
337 match source {
338 SourceType::Sync(source) => source.collect_to(&mut cache)?,
339 #[cfg(feature = "async")]
340 SourceType::Async(source) => source.collect_to(&mut cache).await?,
341 }
342 }
343
344 // Add overrides
345 for (key, val) in overrides {
346 key.set(&mut cache, val);
347 }
348
349 Ok(Config::new(cache))
350 }
351}