summaryrefslogtreecommitdiffstats
path: root/src/builder.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/builder.rs')
-rw-r--r--src/builder.rs232
1 files changed, 211 insertions, 21 deletions
diff --git a/src/builder.rs b/src/builder.rs
index a64040a..bb88f44 100644
--- a/src/builder.rs
+++ b/src/builder.rs
@@ -2,6 +2,7 @@ use std::str::FromStr;
use std::{collections::HashMap, iter::IntoIterator};
use crate::error::Result;
+use crate::source::AsyncSource;
use crate::{config::Config, path::Expression, source::Source, value::Value};
/// A configuration builder
@@ -22,15 +23,24 @@ use crate::{config::Config, path::Expression, source::Source, value::Value};
/// It happens on demand when [`build`](Self::build) (or its alternative) is called.
/// Therefore all errors, related to any of the [`Source`] will only show up then.
///
+/// # Sync and async builder
+///
+/// [`ConfigBuilder`] uses type parameter to keep track of builder state.
+///
+/// In [`DefaultState`] builder only supports [`Source`]s
+///
+/// In [`AsyncState`] it supports both [`Source`]s and [`AsyncSource`]s at the price of building using `async fn`.
+///
/// # Examples
///
/// ```rust
/// # use config::*;
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
-/// let mut builder = ConfigBuilder::default()
+/// let mut builder = Config::builder()
/// .set_default("default", "1")?
/// .add_source(File::new("config/settings", FileFormat::Json))
+/// // .add_async_source(...)
/// .set_override("override", "1")?;
///
/// match builder.build() {
@@ -45,12 +55,15 @@ use crate::{config::Config, path::Expression, source::Source, value::Value};
/// # }
/// ```
///
+/// If any [`AsyncSource`] is used, the builder will transition to [`AsyncState`].
+/// In such case, it is required to _await_ calls to [`build`](Self::build) and its non-consuming sibling.
+///
/// Calls can be not chained as well
/// ```rust
/// # use std::error::Error;
/// # use config::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
-/// let mut builder = ConfigBuilder::default();
+/// let mut builder = Config::builder();
/// builder = builder.set_default("default", "1")?;
/// builder = builder.add_source(File::new("config/settings", FileFormat::Json));
/// builder = builder.add_source(File::new("config/settings.prod", FileFormat::Json));
@@ -58,22 +71,86 @@ use crate::{config::Config, path::Expression, source::Source, value::Value};
/// # Ok(())
/// # }
/// ```
+///
+/// Calling [`Config::builder`](Config::builder) yields builder in the default state.
+/// If having an asynchronous state as the initial state is desired, _turbofish_ notation needs to be used.
+/// ```rust
+/// # use config::{*, builder::AsyncState};
+/// let mut builder = ConfigBuilder::<AsyncState>::default();
+/// ```
+///
+/// If for some reason acquiring builder in default state is required without calling [`Config::builder`](Config::builder)
+/// it can also be achieved.
+/// ```rust
+/// # use config::{*, builder::DefaultState};
+/// let mut builder = ConfigBuilder::<DefaultState>::default();
+/// ```
#[derive(Debug, Clone, Default)]
-pub struct ConfigBuilder {
+pub struct ConfigBuilder<St: BuilderState> {
defaults: HashMap<Expression, Value>,
overrides: HashMap<Expression, Value>,
+ state: St,
+}
+
+/// Represents [`ConfigBuilder`] state.
+pub trait BuilderState {}
+
+/// Represents data specific to builder in default, sychronous state, without support for async.
+#[derive(Debug, Default)]
+pub struct DefaultState {
sources: Vec<Box<dyn Source + Send + Sync>>,
}
-impl ConfigBuilder {
+/// The asynchronous configuration builder.
+///
+/// Similar to a [`ConfigBuilder`] it maintains a set of defaults, a set of sources, and overrides.
+///
+/// Defaults do not override anything, sources override defaults, and overrides override anything else.
+/// Within those three groups order of adding them at call site matters - entities added later take precedence.
+///
+/// For more detailed description and examples see [`ConfigBuilder`].
+/// [`AsyncConfigBuilder`] is just an extension of it that takes async functions into account.
+///
+/// To obtain a [`Config`] call [`build`](AsyncConfigBuilder::build) or [`build_cloned`](AsyncConfigBuilder::build_cloned)
+///
+/// # Example
+/// Since this library does not implement any [`AsyncSource`] an example in rustdocs cannot be given.
+/// Detailed explanation about why such a source is not implemented is in [`AsyncSource`]'s documentation.
+///
+/// Refer to [`ConfigBuilder`] for similar API sample usage or to the examples folder of the crate, where such a source is implemented.
+#[derive(Debug, Clone, Default)]
+pub struct AsyncConfigBuilder {
+ defaults: HashMap<Expression, Value>,
+ overrides: HashMap<Expression, Value>,
+ sources: Vec<SourceType>,
+}
+
+/// Represents data specific to builder in asychronous state, with support for async.
+#[derive(Debug, Default)]
+pub struct AsyncState {
+ sources: Vec<SourceType>,
+}
+
+#[derive(Debug, Clone)]
+enum SourceType {
+ Sync(Box<dyn Source + Send + Sync>),
+ Async(Box<dyn AsyncSource + Send + Sync>),
+}
+
+impl BuilderState for DefaultState {}
+impl BuilderState for AsyncState {}
+
+impl<St: BuilderState> ConfigBuilder<St> {
+ // operations allowed in any state
+
/// Set a default `value` at `key`
///
- /// This value can be overwritten by any [`Source`] or override.
+ /// This value can be overwritten by any [`Source`], [`AsyncSource`] or override.
///
/// # Errors
///
/// Fails if `Expression::from_str(key)` fails.
- pub fn set_default<S, T>(mut self, key: S, value: T) -> Result<ConfigBuilder>
+ pub fn set_default<S, T>(mut self, key: S, value: T) -> Result<ConfigBuilder<St>>
where
S: AsRef<str>,
T: Into<Value>,
@@ -83,25 +160,14 @@ impl ConfigBuilder {
Ok(self)
}
- /// Registers new [`Source`] in this builder.
- ///
- /// Calling this method does not invoke any I/O. [`Source`] is only saved in internal register for later use.
- pub fn add_source<T>(mut self, source: T) -> Self
- where
- T: Source + Send + Sync + 'static,
- {
- self.sources.push(Box::new(source));
- self
- }
-
/// Set an override
///
- /// This function sets an overwrite value. It will not be altered by any default or [`Source`]
+ /// This function sets an overwrite value. It will not be altered by any default, [`Source`] nor [`AsyncSource`]
///
/// # Errors
///
/// Fails if `Expression::from_str(key)` fails.
- pub fn set_override<S, T>(mut self, key: S, value: T) -> Result<ConfigBuilder>
+ pub fn set_override<S, T>(mut self, key: S, value: T) -> Result<ConfigBuilder<St>>
where
S: AsRef<str>,
T: Into<Value>,
@@ -110,6 +176,44 @@ impl ConfigBuilder {
.insert(Expression::from_str(key.as_ref())?, value.into());
Ok(self)
}
+}
+
+impl ConfigBuilder<DefaultState> {
+ // operations allowed in sync state
+
+ /// Registers new [`Source`] in this builder.
+ ///
+ /// Calling this method does not invoke any I/O. [`Source`] is only saved in internal register for later use.
+ pub fn add_source<T>(mut self, source: T) -> Self
+ where
+ T: Source + Send + Sync + 'static,
+ {
+ self.state.sources.push(Box::new(source));
+ self
+ }
+
+ /// Registers new [`AsyncSource`] in this builder and forces transition to [`AsyncState`].
+ ///
+ /// Calling this method does not invoke any I/O. [`AsyncSource`] is only saved in internal register for later use.
+ pub fn add_async_source<T>(self, source: T) -> ConfigBuilder<AsyncState>
+ where
+ T: AsyncSource + Send + Sync + 'static,
+ {
+ let async_state = ConfigBuilder {
+ state: AsyncState {
+ sources: self
+ .state
+ .sources
+ .into_iter()
+ .map(|s| SourceType::Sync(s))
+ .collect(),
+ },
+ defaults: self.defaults,
+ overrides: self.overrides,
+ };
+
+ async_state.add_async_source(source)
+ }
/// Reads all registered [`Source`]s.
///
@@ -120,7 +224,7 @@ impl ConfigBuilder {
/// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
/// this method returns error.
pub fn build(self) -> Result<Config> {
- Self::build_internal(self.defaults, self.overrides, &self.sources)
+ Self::build_internal(self.defaults, self.overrides, &self.state.sources)
}
/// Reads all registered [`Source`]s.
@@ -132,7 +236,11 @@ impl ConfigBuilder {
/// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
/// this method returns error.
pub fn build_cloned(&self) -> Result<Config> {
- Self::build_internal(self.defaults.clone(), self.overrides.clone(), &self.sources)
+ Self::build_internal(
+ self.defaults.clone(),
+ self.overrides.clone(),
+ &self.state.sources,
+ )
}
fn build_internal(
@@ -158,3 +266,85 @@ impl ConfigBuilder {
Ok(Config::new(cache))
}
}
+
+impl ConfigBuilder<AsyncState> {
+ // operations allowed in async state
+
+ /// Registers new [`Source`] in this builder.
+ ///
+ /// Calling this method does not invoke any I/O. [`Source`] is only saved in internal register for later use.
+ pub fn add_source<T>(mut self, source: T) -> ConfigBuilder<AsyncState>
+ where
+ T: Source + Send + Sync + 'static,
+ {
+ self.state.sources.push(SourceType::Sync(Box::new(source)));
+ self
+ }
+
+ /// Registers new [`AsyncSource`] in this builder.
+ ///
+ /// Calling this method does not invoke any I/O. [`AsyncSource`] is only saved in internal register for later use.
+ pub fn add_async_source<T>(mut self, source: T) -> ConfigBuilder<AsyncState>
+ where
+ T: AsyncSource + Send + Sync + 'static,
+ {
+ self.state.sources.push(SourceType::Async(Box::new(source)));
+ self
+ }
+
+ /// Reads all registered defaults, [`Source`]s, [`AsyncSource`]s and overrides.
+ ///
+ /// This is the method that invokes all I/O operations.
+ /// For a non consuming alternative see [`build_cloned`](Self::build_cloned)
+ ///
+ /// # Errors
+ /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
+ /// this method returns error.
+ pub async fn build(self) -> Result<Config> {
+ Self::build_internal(self.defaults, self.overrides, &self.state.sources).await
+ }
+
+ /// Reads all registered defaults, [`Source`]s, [`AsyncSource`]s and overrides.
+ ///
+ /// Similar to [`build`](Self::build), but it does not take ownership of `ConfigBuilder` to allow later reuse.
+ /// Internally it clones data to achieve it.
+ ///
+ /// # Errors
+ /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
+ /// this method returns error.
+ pub async fn build_cloned(&self) -> Result<Config> {
+ Self::build_internal(
+ self.defaults.clone(),
+ self.overrides.clone(),
+ &self.state.sources,
+ )
+ .await
+ }
+
+ async fn build_internal(
+ defaults: HashMap<Expression, Value>,
+ overrides: HashMap<Expression, Value>,
+ sources: &[SourceType],
+ ) -> Result<Config> {
+ let mut cache: Value = HashMap::<String, Value>::new().into();
+
+ // Add defaults
+ for (key, val) in defaults.into_iter() {
+ key.set(&mut cache, val);
+ }
+
+ for source in sources.iter() {
+ match source {
+ SourceType::Sync(source) => source.collect_to(&mut cache)?,
+ SourceType::Async(source) => source.collect_to(&mut cache).await?,
+ }
+ }
+
+ // Add overrides
+ for (key, val) in overrides.into_iter() {
+ key.set(&mut cache, val);
+ }
+
+ Ok(Config::new(cache))
+ }
+}