summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRadosław Kot <rdkt13@gmail.com>2021-04-02 11:12:59 +0200
committerRadosław Kot <rdkt13@gmail.com>2021-04-02 11:12:59 +0200
commit58709b048a6a7cdd687ec87d9c16b2db1f0c3059 (patch)
tree082d31e8f03d3688247ce9139dd9fb3d7e7ecd69
parent803344e93380441ba6e5d24029a31c36929e60fb (diff)
Document builder
-rw-r--r--src/builder.rs125
-rw-r--r--src/config.rs33
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()?;