diff options
Diffstat (limited to 'src/builder.rs')
-rw-r--r-- | src/builder.rs | 241 |
1 files changed, 216 insertions, 25 deletions
diff --git a/src/builder.rs b/src/builder.rs index 627ba2a..bb88f44 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1,7 +1,9 @@ use std::str::FromStr; use std::{collections::HashMap, iter::IntoIterator}; -use crate::{config::Config, error, path::Expression, source::Source, value::Value}; +use crate::error::Result; +use crate::source::AsyncSource; +use crate::{config::Config, path::Expression, source::Source, value::Value}; /// A configuration builder /// @@ -21,15 +23,24 @@ use crate::{config::Config, error, path::Expression, source::Source, value::Valu /// 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() { @@ -44,12 +55,15 @@ use crate::{config::Config, error, path::Expression, source::Source, value::Valu /// # } /// ``` /// +/// 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)); @@ -57,22 +71,86 @@ use crate::{config::Config, error, path::Expression, source::Source, value::Valu /// # 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) -> error::Result<ConfigBuilder> + pub fn set_default<S, T>(mut self, key: S, value: T) -> Result<ConfigBuilder<St>> where S: AsRef<str>, T: Into<Value>, @@ -82,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) -> error::Result<ConfigBuilder> + pub fn set_override<S, T>(mut self, key: S, value: T) -> Result<ConfigBuilder<St>> where S: AsRef<str>, T: Into<Value>, @@ -109,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. /// @@ -118,8 +223,8 @@ impl ConfigBuilder { /// # 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 fn build(self) -> error::Result<Config> { - Self::build_internal(self.defaults, self.overrides, &self.sources) + pub fn build(self) -> Result<Config> { + Self::build_internal(self.defaults, self.overrides, &self.state.sources) } /// Reads all registered [`Source`]s. @@ -130,15 +235,19 @@ impl ConfigBuilder { /// # 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 fn build_cloned(&self) -> error::Result<Config> { - Self::build_internal(self.defaults.clone(), self.overrides.clone(), &self.sources) + pub fn build_cloned(&self) -> Result<Config> { + Self::build_internal( + self.defaults.clone(), + self.overrides.clone(), + &self.state.sources, + ) } fn build_internal( defaults: HashMap<Expression, Value>, overrides: HashMap<Expression, Value>, sources: &[Box<dyn Source + Send + Sync>], - ) -> error::Result<Config> { + ) -> Result<Config> { let mut cache: Value = HashMap::<String, Value>::new().into(); // Add defaults @@ -157,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)) + } +} |