Skip to content

Commit 266f504

Browse files
Merge pull request #196 from szarykott/builder
Create the ConfigBuilder
2 parents 79883ff + c4d2964 commit 266f504

33 files changed

+2045
-202
lines changed

src/builder.rs

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

src/config.rs

Lines changed: 27 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 crate::builder::ConfigBuilder;
45
use serde::de::Deserialize;
56
use serde::ser::Serialize;
67

@@ -35,23 +36,41 @@ 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,
4256
T: Source + Send + Sync,
4357
{
4458
self.sources.push(Box::new(source));
59+
60+
#[allow(deprecated)]
4561
self.refresh()
4662
}
4763

4864
/// Merge in a configuration property source.
65+
#[deprecated(since = "0.12.0", note = "please use 'ConfigBuilder' instead")]
4966
pub fn with_merged<T>(mut self, source: T) -> Result<Self>
5067
where
5168
T: 'static,
5269
T: Source + Send + Sync,
5370
{
5471
self.sources.push(Box::new(source));
72+
73+
#[allow(deprecated)]
5574
self.refresh()?;
5675
Ok(self)
5776
}
@@ -61,6 +80,7 @@ impl Config {
6180
///
6281
/// Configuration is automatically refreshed after a mutation
6382
/// operation (`set`, `merge`, `set_default`, etc.).
83+
#[deprecated(since = "0.12.0", note = "please use 'ConfigBuilder' instead")]
6484
pub fn refresh(&mut self) -> Result<&mut Config> {
6585
self.cache = {
6686
let mut cache: Value = HashMap::<String, Value>::new().into();
@@ -85,11 +105,14 @@ impl Config {
85105
}
86106

87107
/// Set a default `value` at `key`
108+
#[deprecated(since = "0.12.0", note = "please use 'ConfigBuilder' instead")]
88109
pub fn set_default<T>(&mut self, key: &str, value: T) -> Result<&mut Config>
89110
where
90111
T: Into<Value>,
91112
{
92113
self.defaults.insert(key.parse()?, value.into());
114+
115+
#[allow(deprecated)]
93116
self.refresh()
94117
}
95118

@@ -101,14 +124,18 @@ impl Config {
101124
/// # Warning
102125
///
103126
/// Errors if config is frozen
127+
#[deprecated(since = "0.12.0", note = "please use 'ConfigBuilder' instead")]
104128
pub fn set<T>(&mut self, key: &str, value: T) -> Result<&mut Config>
105129
where
106130
T: Into<Value>,
107131
{
108132
self.overrides.insert(key.parse()?, value.into());
133+
134+
#[allow(deprecated)]
109135
self.refresh()
110136
}
111137

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

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;

src/ser.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ impl ConfigSerializer {
2727
)))
2828
}
2929
};
30+
31+
#[allow(deprecated)]
3032
self.output.set(&key, value.into())?;
3133
Ok(())
3234
}

src/source.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,26 @@ impl Source for Vec<Box<dyn Source + Send + Sync>> {
5555
}
5656
}
5757

58+
impl Source for [Box<dyn Source + Send + Sync>] {
59+
fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
60+
Box::new(self.to_owned())
61+
}
62+
63+
fn collect(&self) -> Result<HashMap<String, Value>> {
64+
let mut cache: Value = HashMap::<String, Value>::new().into();
65+
66+
for source in self {
67+
source.collect_to(&mut cache)?;
68+
}
69+
70+
if let ValueKind::Table(table) = cache.kind {
71+
Ok(table)
72+
} else {
73+
unreachable!();
74+
}
75+
}
76+
}
77+
5878
impl<T> Source for Vec<T>
5979
where
6080
T: Source + Sync + Send,

tests/datetime.rs

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,56 +14,51 @@ use chrono::{DateTime, TimeZone, Utc};
1414
use config::*;
1515

1616
fn make() -> Config {
17-
Config::default()
18-
.merge(File::from_str(
17+
Config::builder()
18+
.add_source(File::from_str(
1919
r#"
2020
{
2121
"json_datetime": "2017-05-10T02:14:53Z"
2222
}
2323
"#,
2424
FileFormat::Json,
2525
))
26-
.unwrap()
27-
.merge(File::from_str(
26+
.add_source(File::from_str(
2827
r#"
2928
yaml_datetime: 2017-06-12T10:58:30Z
3029
"#,
3130
FileFormat::Yaml,
3231
))
33-
.unwrap()
34-
.merge(File::from_str(
32+
.add_source(File::from_str(
3533
r#"
3634
toml_datetime = 2017-05-11T14:55:15Z
3735
"#,
3836
FileFormat::Toml,
3937
))
40-
.unwrap()
41-
.merge(File::from_str(
38+
.add_source(File::from_str(
4239
r#"
4340
{
4441
"hjson_datetime": "2017-05-10T02:14:53Z"
4542
}
4643
"#,
4744
FileFormat::Hjson,
4845
))
49-
.unwrap()
50-
.merge(File::from_str(
46+
.add_source(File::from_str(
5147
r#"
5248
ini_datetime = 2017-05-10T02:14:53Z
5349
"#,
5450
FileFormat::Ini,
5551
))
56-
.unwrap()
57-
.merge(File::from_str(
52+
.add_source(File::from_str(
5853
r#"
5954
(
6055
ron_datetime: "2021-04-19T11:33:02Z"
6156
)
6257
"#,
6358
FileFormat::Ron,
6459
))
60+
.build()
6561
.unwrap()
66-
.clone()
6762
}
6863

6964
#[test]

0 commit comments

Comments
 (0)