diff options
author | Radosław Kot <rdkt13@gmail.com> | 2021-04-02 11:12:59 +0200 |
---|---|---|
committer | Radosław Kot <rdkt13@gmail.com> | 2021-04-02 11:12:59 +0200 |
commit | 58709b048a6a7cdd687ec87d9c16b2db1f0c3059 (patch) | |
tree | 082d31e8f03d3688247ce9139dd9fb3d7e7ecd69 | |
parent | 803344e93380441ba6e5d24029a31c36929e60fb (diff) |
Document builder
-rw-r--r-- | src/builder.rs | 125 | ||||
-rw-r--r-- | src/config.rs | 33 |
2 files changed, 108 insertions, 50 deletions
diff --git a/src/builder.rs b/src/builder.rs index f3cfdfa..68a9dac 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1,68 +1,118 @@ use crate::{ config::Config, + value::Value, source::Source, error, - error::ConfigError + error::ConfigError, + path::Expression }; +use std::collections::HashMap; /** -A mutable config builder with certain guarantees +A configuration builder -Similar to [`merge`](Config::merge) or [`with_merged`](Config::with_merged). -Main difference between `ConfigBuilder` and aforementioned methods is that the former: -* is never frozen (always accepts new [`Source`] instances) -* does not refresh at every [`add_source`](Self::add_source) +It registers ordered sources of configuration to later build consistent [`Config`] from them. +Configuration sources it defines are defaults, [`Source`]s and overrides. -In case of `ConfigBuilder` [`Source`] instances are only read on demand when [`build`](Self::build) is called. +Defaults are alaways loaded first and can be overwritten by any of two other sources. +Overrides are always loaded last, thus cannot be overridden. +Both can be only set explicitly key by key in code +using [`set_default`](Self::set_default) or [`set_override`](Self::set_override). + +An intermediate category, [`Source`], set groups of keys at once implicitly using data coming from external sources +like files, environment variables or others that one implements. Defining a [`Source`] is as simple as implementing +a trait for a struct. + +Adding sources, setting defaults and overrides does not invoke any I/O nor builds a config. +It happens on demand when [`build`](Self::build) is called. +Therefore all errors, related to any of the [`Source`] will only show up then. +```rust +# use config::*; + +let mut builder = ConfigBuilder::default(); + +builder.set_default("default", "1").expect("Key is valid"); +builder.add_source(File::new("config/settings", FileFormat::Json)); +builder.set_override("override", "1").expect("Key is valid"); + +match builder.build() { + Ok(config) => { + // use your config + }, + Err(e) => { + // something went wrong + } +} +``` */ #[derive(Debug, Clone)] pub struct ConfigBuilder { - internal : Config + defaults: HashMap<Expression, Value>, + overrides: HashMap<Expression, Value>, + sources: Vec<Box<dyn Source + Send + Sync>>, } impl ConfigBuilder { - /// Creates an empty `ConfigBuilder` without any [`Source`] registered. + /** + Creates an empty `ConfigBuilder` without any configuration source registered. + + This is the [`Default`] implementation of `ConfigBuilder` + ```rust + # use config::ConfigBuilder; + let builder = ConfigBuilder::empty(); + ``` + */ pub fn empty() -> Self { ConfigBuilder { - internal : Config::default() + defaults: HashMap::new(), + overrides: HashMap::new(), + sources: Vec::new(), } } - /** - Creates a `ConfigBuilder` instance from [`Config`], preserving its state. + /** + Set a default `value` at `key` - [`Config`] cannot be frozen! - */ - pub(crate) fn from_config(config : Config) -> error::Result<Self> { - if config.is_frozen() { - return Err(ConfigError::Frozen) - } + This value can be overwritten by any [`Source`] or override. - Ok(ConfigBuilder { - internal : config - }) + Method can fail if `key` is not valid. + */ + pub fn set_default<T>(&mut self, key: &str, value: T) -> error::Result<&mut ConfigBuilder> + where + T: Into<Value>, + { + self.defaults.insert(key.parse()?, value.into()); + 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. - - # Panics - Explaining reasons for this panic requires leaking implementation details. - `ConfigBuilder` is a thin wrapper around [`Config`]. This method panics if it is frozen. - It shall not happen. */ pub fn add_source<T>(&mut self, source : T) -> &mut Self where T: Source + Send + Sync + 'static { - self.internal.push(source) - .expect("It is illegal that config is frozen inside the builder"); + 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`] + + Method can fail if `key` is not valid. + */ + pub fn set_override<T>(&mut self, key: &str, value: T) -> error::Result<&mut ConfigBuilder> + where + T: Into<Value>, + { + self.overrides.insert(key.parse()?, value.into()); + Ok(self) + } + /** Reads all registered [`Source`]s. @@ -73,9 +123,22 @@ impl ConfigBuilder It does not take ownership of `ConfigBuilder` to allow later reuse. Internally it clones data to achieve it. */ pub fn build(&self) -> error::Result<Config> { - let mut cloned = self.internal.clone(); - cloned.refresh()?; - Ok(cloned) + let mut cache: Value = HashMap::<String, Value>::new().into(); + + // Add defaults + for (key, val) in self.defaults.iter() { + key.set(&mut cache, val.clone()); + } + + // Add sources + self.sources.collect_to(&mut cache)?; + + // Add overrides + for (key, val) in self.overrides.iter() { + key.set(&mut cache, val.clone()); + } + + Ok(Config::new(cache)) } } diff --git a/src/config.rs b/src/config.rs index eb7b581..02bef5f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -58,7 +58,15 @@ impl Default for Config { } impl Config { + pub(crate) fn new(value : Value) -> Self { + Config { + kind : ConfigKind::default(), + cache : value + } + } + /// Merge in a configuration property source. + #[deprecated(since = "0.12.0", note = "please use 'ConfigBuilder' instead")] pub fn merge<T>(&mut self, source: T) -> Result<&mut Config> where T: 'static, @@ -69,6 +77,7 @@ impl Config { } /// Merge in a configuration property source. + #[deprecated(since = "0.12.0", note = "please use 'ConfigBuilder' instead")] pub fn with_merged<T>(mut self, source: T) -> Result<Self> where T: 'static, @@ -84,6 +93,7 @@ impl Config { /// /// Configuration is automatically refreshed after a mutation /// operation (`set`, `merge`, `set_default`, etc.). + #[deprecated(since = "0.12.0", note = "please use 'ConfigBuilder' instead")] pub fn refresh(&mut self) -> Result<&mut Config> { self.cache = match self.kind { // TODO: We need to actually merge in all the stuff @@ -119,6 +129,7 @@ impl Config { } /// Pushes new [`Source`] to Config unless it is frozen + #[deprecated(since = "0.12.0", note = "please use 'ConfigBuilder' instead")] pub(crate) fn push<T>(&mut self, source: T) -> Result<()> where T: 'static, @@ -138,26 +149,8 @@ impl Config { } } - /// Creates a new instance of [`ConfigBuilder`] from this Config - /// - /// [`ConfigBuilder`] guarantees that Config is not refreshed when new [`Source`] is added. - /// What is more, [`ConfigBuilder`] is never frozen, preventing certains runtime errors. - pub fn builder(self) -> Result<ConfigBuilder> { - ConfigBuilder::from_config(self) - } - - /// Informs whether this `Config` is frozen. - /// - /// When `Config` is frozen it no longer accepts new [`Source`] instances. - pub(crate) fn is_frozen(&self) -> bool - { - match self.kind { - ConfigKind::Frozen => true, - _ => false - } - } - /// Set a default `value` at `key` + #[deprecated(since = "0.12.0", note = "please use 'ConfigBuilder' instead")] pub fn set_default<T>(&mut self, key: &str, value: T) -> Result<&mut Config> where T: Into<Value>, @@ -183,6 +176,7 @@ impl Config { /// # Warning /// /// Errors if config is frozen + #[deprecated(since = "0.12.0", note = "please use 'ConfigBuilder' instead")] pub fn set<T>(&mut self, key: &str, value: T) -> Result<&mut Config> where T: Into<Value>, @@ -200,6 +194,7 @@ impl Config { self.refresh() } + #[deprecated(since = "0.12.0", note = "please use 'ConfigBuilder' instead")] pub fn set_once(&mut self, key: &str, value: Value) -> Result<()> { let expr: path::Expression = key.parse()?; |