Skip to content

Commit 82d23c7

Browse files
committed
Add ConfigBuilder
1 parent 79883ff commit 82d23c7

File tree

3 files changed

+185
-0
lines changed

3 files changed

+185
-0
lines changed

src/builder.rs

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
use std::str::FromStr;
2+
use std::{collections::HashMap, iter::IntoIterator};
3+
4+
use crate::{
5+
config::Config, error, error::ConfigError, path::Expression, source::Source, value::Value,
6+
};
7+
8+
/// A configuration builder
9+
///
10+
/// It registers ordered sources of configuration to later build consistent [`Config`] from them.
11+
/// Configuration sources it defines are defaults, [`Source`]s and overrides.
12+
///
13+
/// Defaults are alaways loaded first and can be overwritten by any of two other sources.
14+
/// Overrides are always loaded last, thus cannot be overridden.
15+
/// Both can be only set explicitly key by key in code
16+
/// using [`set_default`](Self::set_default) or [`set_override`](Self::set_override).
17+
///
18+
/// An intermediate category, [`Source`], set groups of keys at once implicitly using data coming from external sources
19+
/// like files, environment variables or others that one implements. Defining a [`Source`] is as simple as implementing
20+
/// a trait for a struct.
21+
///
22+
/// Adding sources, setting defaults and overrides does not invoke any I/O nor builds a config.
23+
/// It happens on demand when [`build`](Self::build) (or its alternative) is called.
24+
/// Therefore all errors, related to any of the [`Source`] will only show up then.
25+
///
26+
/// # Examples
27+
///
28+
/// ```rust
29+
/// # use config::*;
30+
/// # use std::error::Error;
31+
/// # fn main() -> Result<(), Box<dyn Error>> {
32+
/// let mut builder = ConfigBuilder::default();
33+
///
34+
/// builder.set_default("default", "1")?;
35+
/// builder.add_source(File::new("config/settings", FileFormat::Json));
36+
/// builder.set_override("override", "1")?;
37+
///
38+
/// match builder.build() {
39+
/// Ok(config) => {
40+
/// // use your config
41+
/// },
42+
/// Err(e) => {
43+
/// // something went wrong
44+
/// }
45+
/// }
46+
/// # Ok(())
47+
/// # }
48+
/// ```
49+
///
50+
/// Calls can be chained as well
51+
/// ```rust
52+
/// # use std::error::Error;
53+
/// # use config::*;
54+
/// # fn main() -> Result<(), Box<dyn Error>> {
55+
/// let mut builder = ConfigBuilder::default();
56+
///
57+
/// builder
58+
/// .set_default("default", "1")?
59+
/// .add_source(File::new("config/settings", FileFormat::Json))
60+
/// .add_source(File::new("config/settings.prod", FileFormat::Json))
61+
/// .set_override("override", "1")?;
62+
/// # Ok(())
63+
/// # }
64+
/// ```
65+
#[derive(Debug, Clone, Default)]
66+
pub struct ConfigBuilder {
67+
defaults: HashMap<Expression, Value>,
68+
overrides: HashMap<Expression, Value>,
69+
sources: Vec<Box<dyn Source + Send + Sync>>,
70+
}
71+
72+
impl ConfigBuilder {
73+
/// Set a default `value` at `key`
74+
///
75+
/// This value can be overwritten by any [`Source`] or override.
76+
///
77+
/// # Errors
78+
///
79+
/// Fails if `Expression::from_str(key)` fails.
80+
pub fn set_default<S, T>(&mut self, key: S, value: T) -> error::Result<&mut ConfigBuilder>
81+
where
82+
S: AsRef<str>,
83+
T: Into<Value>,
84+
{
85+
self.defaults
86+
.insert(Expression::from_str(key.as_ref())?, value.into());
87+
Ok(self)
88+
}
89+
90+
/// Registers new [`Source`] in this builder.
91+
///
92+
/// Calling this method does not invoke any I/O. [`Source`] is only saved in internal register for later use.
93+
pub fn add_source<T>(&mut self, source: T) -> &mut Self
94+
where
95+
T: Source + Send + Sync + 'static,
96+
{
97+
self.sources.push(Box::new(source));
98+
self
99+
}
100+
101+
/// Set an override
102+
///
103+
/// This function sets an overwrite value. It will not be altered by any default or [`Source`]
104+
///
105+
/// # Errors
106+
///
107+
/// Fails if `Expression::from_str(key)` fails.
108+
pub fn set_override<S, T>(&mut self, key: S, value: T) -> error::Result<&mut ConfigBuilder>
109+
where
110+
S: AsRef<str>,
111+
T: Into<Value>,
112+
{
113+
self.overrides
114+
.insert(Expression::from_str(key.as_ref())?, value.into());
115+
Ok(self)
116+
}
117+
118+
/// Reads all registered [`Source`]s.
119+
///
120+
/// This is the method that invokes all I/O operations.
121+
/// For a non consuming alternative see [`build_cloned`](Self::build_cloned)
122+
///
123+
/// # Errors
124+
/// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
125+
/// this method returns error.
126+
pub fn build(self) -> error::Result<Config> {
127+
Self::build_internal(self.defaults, self.overrides, &self.sources)
128+
}
129+
130+
/// Reads all registered [`Source`]s.
131+
///
132+
/// Similar to [`build`](Self::build), but it does not take ownership of `ConfigBuilder` to allow later reuse.
133+
/// Internally it clones data to achieve it.
134+
///
135+
/// # Errors
136+
/// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
137+
/// this method returns error.
138+
pub fn build_cloned(&self) -> error::Result<Config> {
139+
Self::build_internal(self.defaults.clone(), self.overrides.clone(), &self.sources)
140+
}
141+
142+
fn build_internal(
143+
defaults: HashMap<Expression, Value>,
144+
overrides: HashMap<Expression, Value>,
145+
sources: &Vec<Box<dyn Source + Send + Sync>>,
146+
) -> error::Result<Config> {
147+
let mut cache: Value = HashMap::<String, Value>::new().into();
148+
149+
// Add defaults
150+
for (key, val) in defaults.into_iter() {
151+
key.set(&mut cache, val);
152+
}
153+
154+
// Add sources
155+
sources.collect_to(&mut cache)?;
156+
157+
// Add overrides
158+
for (key, val) in overrides.into_iter() {
159+
key.set(&mut cache, val);
160+
}
161+
162+
Ok(Config::new(cache))
163+
}
164+
}

src/config.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::collections::HashMap;
22
use std::fmt::Debug;
33

4+
use builder::ConfigBuilder;
45
use serde::de::Deserialize;
56
use serde::ser::Serialize;
67

@@ -35,7 +36,20 @@ impl Default for Config {
3536
}
3637

3738
impl Config {
39+
pub(crate) fn new(value: Value) -> Self {
40+
Config {
41+
cache: value,
42+
..Default::default()
43+
}
44+
}
45+
46+
/// Creates new [`ConfigBuilder`] instance
47+
pub fn builder() -> ConfigBuilder {
48+
ConfigBuilder::default()
49+
}
50+
3851
/// Merge in a configuration property source.
52+
#[deprecated(since = "0.12.0", note = "please use 'ConfigBuilder' instead")]
3953
pub fn merge<T>(&mut self, source: T) -> Result<&mut Config>
4054
where
4155
T: 'static,
@@ -46,6 +60,7 @@ impl Config {
4660
}
4761

4862
/// Merge in a configuration property source.
63+
#[deprecated(since = "0.12.0", note = "please use 'ConfigBuilder' instead")]
4964
pub fn with_merged<T>(mut self, source: T) -> Result<Self>
5065
where
5166
T: 'static,
@@ -61,6 +76,7 @@ impl Config {
6176
///
6277
/// Configuration is automatically refreshed after a mutation
6378
/// operation (`set`, `merge`, `set_default`, etc.).
79+
#[deprecated(since = "0.12.0", note = "please use 'ConfigBuilder' instead")]
6480
pub fn refresh(&mut self) -> Result<&mut Config> {
6581
self.cache = {
6682
let mut cache: Value = HashMap::<String, Value>::new().into();
@@ -85,6 +101,7 @@ impl Config {
85101
}
86102

87103
/// Set a default `value` at `key`
104+
#[deprecated(since = "0.12.0", note = "please use 'ConfigBuilder' instead")]
88105
pub fn set_default<T>(&mut self, key: &str, value: T) -> Result<&mut Config>
89106
where
90107
T: Into<Value>,
@@ -101,6 +118,7 @@ impl Config {
101118
/// # Warning
102119
///
103120
/// Errors if config is frozen
121+
#[deprecated(since = "0.12.0", note = "please use 'ConfigBuilder' instead")]
104122
pub fn set<T>(&mut self, key: &str, value: T) -> Result<&mut Config>
105123
where
106124
T: Into<Value>,
@@ -109,6 +127,7 @@ impl Config {
109127
self.refresh()
110128
}
111129

130+
#[deprecated(since = "0.12.0", note = "please use 'ConfigBuilder' instead")]
112131
pub fn set_once(&mut self, key: &str, value: Value) -> Result<()> {
113132
let expr: path::Expression = key.parse()?;
114133

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ extern crate ini;
5151
#[cfg(feature = "ron")]
5252
extern crate ron;
5353

54+
mod builder;
5455
mod config;
5556
mod de;
5657
mod env;
@@ -61,6 +62,7 @@ mod ser;
6162
mod source;
6263
mod value;
6364

65+
pub use crate::builder::ConfigBuilder;
6466
pub use crate::config::Config;
6567
pub use crate::env::Environment;
6668
pub use crate::error::ConfigError;

0 commit comments

Comments
 (0)