summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/pattern/Cargo.toml6
-rw-r--r--examples/pattern/conf/00-default.toml1
-rw-r--r--examples/pattern/conf/05-some.yml2
-rw-r--r--examples/pattern/conf/99-extra.json5
-rw-r--r--examples/pattern/src/main.rs50
-rw-r--r--src/config.rs22
-rw-r--r--src/file/mod.rs31
-rw-r--r--src/file/source/file.rs28
-rw-r--r--src/source.rs69
-rw-r--r--tests/errors.rs6
10 files changed, 170 insertions, 50 deletions
diff --git a/examples/pattern/Cargo.toml b/examples/pattern/Cargo.toml
new file mode 100644
index 0000000..98c5330
--- /dev/null
+++ b/examples/pattern/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "pattern"
+version = "0.1.0"
+
+[dependencies]
+config = { path = "../../", features = ["glob"] }
diff --git a/examples/pattern/conf/00-default.toml b/examples/pattern/conf/00-default.toml
new file mode 100644
index 0000000..7b95e7a
--- /dev/null
+++ b/examples/pattern/conf/00-default.toml
@@ -0,0 +1 @@
+debug = false
diff --git a/examples/pattern/conf/05-some.yml b/examples/pattern/conf/05-some.yml
new file mode 100644
index 0000000..52555a0
--- /dev/null
+++ b/examples/pattern/conf/05-some.yml
@@ -0,0 +1,2 @@
+secret: THIS IS SECRET
+debug: true
diff --git a/examples/pattern/conf/99-extra.json b/examples/pattern/conf/99-extra.json
new file mode 100644
index 0000000..26f908c
--- /dev/null
+++ b/examples/pattern/conf/99-extra.json
@@ -0,0 +1,5 @@
+{
+ "that": 3,
+ "this": 1230,
+ "key": "sdgnjklsdjklgds"
+}
diff --git a/examples/pattern/src/main.rs b/examples/pattern/src/main.rs
new file mode 100644
index 0000000..aaf164d
--- /dev/null
+++ b/examples/pattern/src/main.rs
@@ -0,0 +1,50 @@
+extern crate glob;
+extern crate config;
+
+use std::path::Path;
+use std::collections::HashMap;
+use config::*;
+use glob::glob;
+
+fn main() {
+ // Option 1
+ // --------
+ // Gather all conf files from conf/ manually
+ let settings = Config::default()
+ // File::with_name(..) is shorthand for File::from(Path::new(..))
+ .merge(File::with_name("conf/00-default.toml"))
+ .merge(File::from(Path::new("conf/05-some.yml")))
+ .merge(File::from(Path::new("conf/99-extra.json")))
+ .unwrap();
+
+ // Print out our settings (as a HashMap)
+ println!("\n{:?} \n\n-----------",
+ settings.deserialize::<HashMap<String, String>>().unwrap());
+
+ // Option 2
+ // --------
+ // Gather all conf files from conf/ manually, but put in 1 merge call.
+ let settings = Config::default()
+ .merge(vec![File::with_name("conf/00-default.toml"),
+ File::from(Path::new("conf/05-some.yml")),
+ File::from(Path::new("conf/99-extra.json"))])
+ .unwrap();
+
+ // Print out our settings (as a HashMap)
+ println!("\n{:?} \n\n-----------",
+ settings.deserialize::<HashMap<String, String>>().unwrap());
+
+ // Option 3
+ // --------
+ // Gather all conf files from conf/ using glob and put in 1 merge call.
+ let settings = Config::default()
+ .merge(glob("conf/*")
+ .unwrap()
+ .map(|path| File::from(path.unwrap()))
+ .collect::<Vec<_>>())
+ .unwrap();
+
+ // Print out our settings (as a HashMap)
+ println!("\n{:?} \n\n-----------",
+ settings.deserialize::<HashMap<String, String>>().unwrap());
+}
diff --git a/src/config.rs b/src/config.rs
index c8fc2e4..f1d5e93 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -89,26 +89,8 @@ impl Config {
}
// Add sources
- for source in sources {
- let props = match source.collect() {
- Ok(props) => props,
- Err(error) => {
- return ConfigResult(Err(error));
- }
- };
-
- for (key, val) in &props {
- match path::Expression::from_str(key) {
- // Set using the path
- Ok(expr) => expr.set(&mut cache, val.clone()),
-
- // Set diretly anyway
- _ => {
- path::Expression::Identifier(key.clone())
- .set(&mut cache, val.clone())
- }
- }
- }
+ if let Err(error) = sources.collect_to(&mut cache) {
+ return ConfigResult(Err(error));
}
// Add overrides
diff --git a/src/file/mod.rs b/src/file/mod.rs
index 6c45cb7..45deb92 100644
--- a/src/file/mod.rs
+++ b/src/file/mod.rs
@@ -5,7 +5,7 @@ use source::Source;
use error::*;
use value::Value;
use std::collections::HashMap;
-use std::path::Path;
+use std::path::{Path, PathBuf};
use self::source::FileSource;
pub use self::format::FileFormat;
@@ -38,7 +38,7 @@ impl File<source::file::FileSourceFile> {
File {
format: Some(format),
required: true,
- source: source::file::FileSourceFile::new(name),
+ source: source::file::FileSourceFile::new(name.into()),
}
}
@@ -48,12 +48,37 @@ impl File<source::file::FileSourceFile> {
File {
format: None,
required: true,
- source: source::file::FileSourceFile::new(name),
+ source: source::file::FileSourceFile::new(name.into()),
+ }
+ }
+}
+
+impl<'a> From<&'a Path> for File<source::file::FileSourceFile> {
+ fn from(path: &'a Path) -> Self {
+ File {
+ format: None,
+ required: true,
+ source: source::file::FileSourceFile::new(path.to_path_buf()),
+ }
+ }
+}
+
+impl From<PathBuf> for File<source::file::FileSourceFile> {
+ fn from(path: PathBuf) -> Self {
+ File {
+ format: None,
+ required: true,
+ source: source::file::FileSourceFile::new(path),
}
}
}
impl<T: FileSource> File<T> {
+ pub fn format(mut self, format: FileFormat) -> Self {
+ self.format = Some(format);
+ self
+ }
+
pub fn required(mut self, required: bool) -> Self {
self.required = required;
self
diff --git a/src/file/source/file.rs b/src/file/source/file.rs
index 426c0b5..790933f 100644
--- a/src/file/source/file.rs
+++ b/src/file/source/file.rs
@@ -15,36 +15,20 @@ use super::{FileFormat, FileSource};
/// Describes a file sourced from a file
#[derive(Clone, Debug)]
pub struct FileSourceFile {
- /// Basename of configuration file
- name: String,
-
- /// Directory where configuration file is found
- /// When not specified, the current working directory (CWD) is considered
- path: Option<String>,
+ /// Path of configuration file
+ name: PathBuf,
}
impl FileSourceFile {
- pub fn new(name: &str) -> FileSourceFile {
- FileSourceFile {
- name: name.into(),
- path: None,
- }
+ pub fn new(name: PathBuf) -> FileSourceFile {
+ FileSourceFile { name: name }
}
fn find_file(&self,
format_hint: Option<FileFormat>)
-> Result<(PathBuf, FileFormat), Box<Error>> {
- // Build expected configuration file
- let mut basename = PathBuf::new();
-
- if let Some(ref path) = self.path {
- basename.push(path.clone());
- }
-
- basename.push(self.name.clone());
-
// First check for an _exact_ match
- let mut filename = env::current_dir()?.as_path().join(basename.clone());
+ let mut filename = env::current_dir()?.as_path().join(self.name.clone());
if filename.is_file() {
return match format_hint {
Some(format) => Ok((filename, format)),
@@ -92,7 +76,7 @@ impl FileSourceFile {
Err(Box::new(io::Error::new(io::ErrorKind::NotFound,
format!("configuration file \"{}\" not found",
- basename.to_string_lossy()))))
+ self.name.to_string_lossy()))))
}
}
diff --git a/src/source.rs b/src/source.rs
index c8a0e83..9bf28c5 100644
--- a/src/source.rs
+++ b/src/source.rs
@@ -1,7 +1,9 @@
use error::*;
use std::fmt::Debug;
-use value::Value;
+use std::str::FromStr;
+use value::{Value, ValueKind};
use std::collections::HashMap;
+use path;
/// Describes a generic _source_ of configuration properties.
pub trait Source: Debug {
@@ -10,6 +12,27 @@ pub trait Source: Debug {
/// Collect all configuration properties available from this source and return
/// a HashMap.
fn collect(&self) -> Result<HashMap<String, Value>>;
+
+ fn collect_to(&self, cache: &mut Value) -> Result<()> {
+ let props = match self.collect() {
+ Ok(props) => props,
+ Err(error) => {
+ return Err(error);
+ }
+ };
+
+ for (key, val) in &props {
+ match path::Expression::from_str(key) {
+ // Set using the path
+ Ok(expr) => expr.set(cache, val.clone()),
+
+ // Set diretly anyway
+ _ => path::Expression::Identifier(key.clone()).set(cache, val.clone()),
+ }
+ }
+
+ Ok(())
+ }
}
impl Clone for Box<Source + Send + Sync> {
@@ -17,3 +40,47 @@ impl Clone for Box<Source + Send + Sync> {
self.clone_into_box()
}
}
+
+impl Source for Vec<Box<Source + Send + Sync>> {
+ fn clone_into_box(&self) -> Box<Source + Send + Sync> {
+ Box::new((*self).clone())
+ }
+
+ fn collect(&self) -> Result<HashMap<String, Value>> {
+ let mut cache: Value = HashMap::<String, Value>::new().into();
+
+ for source in self {
+ source.collect_to(&mut cache)?;
+ }
+
+ if let ValueKind::Table(table) = cache.kind {
+ Ok(table)
+ } else {
+ unreachable!();
+ }
+ }
+}
+
+impl<T> Source for Vec<T>
+ where T: Source + Sync + Send,
+ T: Clone,
+ T: 'static
+{
+ fn clone_into_box(&self) -> Box<Source + Send + Sync> {
+ Box::new((*self).clone())
+ }
+
+ fn collect(&self) -> Result<HashMap<String, Value>> {
+ let mut cache: Value = HashMap::<String, Value>::new().into();
+
+ for source in self {
+ source.collect_to(&mut cache)?;
+ }
+
+ if let ValueKind::Table(table) = cache.kind {
+ Ok(table)
+ } else {
+ unreachable!();
+ }
+ }
+}
diff --git a/tests/errors.rs b/tests/errors.rs
index fa3b5ca..835ead1 100644
--- a/tests/errors.rs
+++ b/tests/errors.rs
@@ -17,8 +17,7 @@ fn test_error_parse() {
assert!(res.is_err());
assert_eq!(res.unwrap_err().to_string(),
- "invalid number at line 2 in tests/Settings-invalid.toml"
- .to_string());
+ "invalid number at line 2 in tests/Settings-invalid.toml".to_string());
}
#[test]
@@ -42,6 +41,5 @@ fn test_error_type_detached() {
assert!(res.is_err());
assert_eq!(res.unwrap_err().to_string(),
- "invalid type: string \"fals\", expected a boolean"
- .to_string());
+ "invalid type: string \"fals\", expected a boolean".to_string());
}