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