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}