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