From 6bfaf90fdf67197c511a7594b37d835e964edccd Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Thu, 22 Jun 2017 17:10:47 -0700 Subject: Implement Source for Vec and From for File --- examples/pattern/Cargo.toml | 6 +++ examples/pattern/conf/00-default.toml | 1 + examples/pattern/conf/05-some.yml | 2 + examples/pattern/conf/99-extra.json | 5 +++ examples/pattern/src/main.rs | 50 +++++++++++++++++++++++++ src/config.rs | 22 +---------- src/file/mod.rs | 31 ++++++++++++++-- src/file/source/file.rs | 28 +++----------- src/source.rs | 69 ++++++++++++++++++++++++++++++++++- tests/errors.rs | 6 +-- 10 files changed, 170 insertions(+), 50 deletions(-) create mode 100644 examples/pattern/Cargo.toml create mode 100644 examples/pattern/conf/00-default.toml create mode 100644 examples/pattern/conf/05-some.yml create mode 100644 examples/pattern/conf/99-extra.json create mode 100644 examples/pattern/src/main.rs 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::>().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::>().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::>()) + .unwrap(); + + // Print out our settings (as a HashMap) + println!("\n{:?} \n\n-----------", + settings.deserialize::>().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 { 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 { 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 { + fn from(path: &'a Path) -> Self { + File { + format: None, + required: true, + source: source::file::FileSourceFile::new(path.to_path_buf()), + } + } +} + +impl From for File { + fn from(path: PathBuf) -> Self { + File { + format: None, + required: true, + source: source::file::FileSourceFile::new(path), } } } impl File { + 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, + /// 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) -> Result<(PathBuf, FileFormat), Box> { - // 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>; + + 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 { @@ -17,3 +40,47 @@ impl Clone for Box { self.clone_into_box() } } + +impl Source for Vec> { + fn clone_into_box(&self) -> Box { + Box::new((*self).clone()) + } + + fn collect(&self) -> Result> { + let mut cache: Value = HashMap::::new().into(); + + for source in self { + source.collect_to(&mut cache)?; + } + + if let ValueKind::Table(table) = cache.kind { + Ok(table) + } else { + unreachable!(); + } + } +} + +impl Source for Vec + where T: Source + Sync + Send, + T: Clone, + T: 'static +{ + fn clone_into_box(&self) -> Box { + Box::new((*self).clone()) + } + + fn collect(&self) -> Result> { + let mut cache: Value = HashMap::::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()); } -- cgit v1.2.3