diff options
32 files changed, 536 insertions, 575 deletions
diff --git a/examples/global/Cargo.toml b/examples/global/Cargo.toml new file mode 100644 index 0000000..ec24740 --- /dev/null +++ b/examples/global/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "global" +version = "0.1.0" + +[dependencies] +config = { path = "../../" } +lazy_static = "^0.2.8" diff --git a/examples/global/src/main.rs b/examples/global/src/main.rs new file mode 100644 index 0000000..4fe0864 --- /dev/null +++ b/examples/global/src/main.rs @@ -0,0 +1,26 @@ +#[macro_use] +extern crate lazy_static; + +extern crate config; + +use std::error::Error; +use std::sync::RwLock; +use config::Config; + +lazy_static! { + static ref SETTINGS: RwLock<Config> = RwLock::new(Config::default()); +} + +fn try_main() -> Result<(), Box<Error>> { + // Set property + SETTINGS.write()?.set("property", 42)?; + + // Get property + println!("property: {}", SETTINGS.read()?.get::<i32>("property")?); + + Ok(()) +} + +fn main() { + try_main().unwrap() +} diff --git a/examples/hierarchical-env/src/settings.rs b/examples/hierarchical-env/src/settings.rs index 43897b3..9e07054 100644 --- a/examples/hierarchical-env/src/settings.rs +++ b/examples/hierarchical-env/src/settings.rs @@ -1,5 +1,5 @@ use std::env; -use config::{Config, File, Environment}; +use config::{ConfigError, Config, File, Environment}; #[derive(Debug, Deserialize)] struct Database { @@ -37,34 +37,34 @@ pub struct Settings { } impl Settings { - pub fn new() -> Self { + pub fn new() -> Result<Self, ConfigError> { let mut s = Config::new(); // Start off by merging in the "default" configuration file - s.merge(File::with_name("config/default")).unwrap(); + s.merge(File::with_name("config/default"))?; // Add in the current environment file // Default to 'development' env // Note that this file is _optional_ let env = env::var("RUN_MODE").unwrap_or("development".into()); - s.merge(File::with_name(&format!("config/{}", env)).required(false)).unwrap(); + s.merge(File::with_name(&format!("config/{}", env)).required(false))?; // Add in a local configuration file // This file shouldn't be checked in to git - s.merge(File::with_name("config/local").required(false)).unwrap(); + s.merge(File::with_name("config/local").required(false))?; // Add in settings from the environment (with a prefix of APP) // Eg.. `APP_DEBUG=1 ./target/app` would set the `debug` key - s.merge(Environment::with_prefix("app")).unwrap(); + s.merge(Environment::with_prefix("app"))?; // You may also programmatically change settings - s.set("database.url", "postgres://").unwrap(); + s.set("database.url", "postgres://")?; // Now that we're done, let's access our configuration println!("debug: {:?}", s.get_bool("debug")); println!("database: {:?}", s.get::<String>("database.url")); // You can deserialize (and thus freeze) the entire configuration as - s.deserialize().unwrap() + s.deserialize() } } diff --git a/examples/pattern/src/main.rs b/examples/pattern/src/main.rs index aaf164d..f88f1ed 100644 --- a/examples/pattern/src/main.rs +++ b/examples/pattern/src/main.rs @@ -10,12 +10,12 @@ fn main() { // Option 1 // -------- // Gather all conf files from conf/ manually - let settings = Config::default() + let mut settings = Config::default(); + settings // 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(); + .merge(File::with_name("conf/00-default.toml")).unwrap() + .merge(File::from(Path::new("conf/05-some.yml"))).unwrap() + .merge(File::from(Path::new("conf/99-extra.json"))).unwrap(); // Print out our settings (as a HashMap) println!("\n{:?} \n\n-----------", @@ -24,7 +24,8 @@ fn main() { // Option 2 // -------- // Gather all conf files from conf/ manually, but put in 1 merge call. - let settings = Config::default() + let mut settings = Config::default(); + settings .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"))]) @@ -37,7 +38,8 @@ fn main() { // Option 3 // -------- // Gather all conf files from conf/ using glob and put in 1 merge call. - let settings = Config::default() + let mut settings = Config::default(); + settings .merge(glob("conf/*") .unwrap() .map(|path| File::from(path.unwrap())) diff --git a/examples/simple/src/main.rs b/examples/simple/src/main.rs index debad02..fb66e03 100644 --- a/examples/simple/src/main.rs +++ b/examples/simple/src/main.rs @@ -3,13 +3,13 @@ extern crate config; use std::collections::HashMap; fn main() { - let settings = config::Config::default() + let mut settings = config::Config::default(); + settings // Add in `./Settings.toml` - .merge(config::File::with_name("Settings")) + .merge(config::File::with_name("Settings")).unwrap() // Add in settings from the environment (with a prefix of APP) // Eg.. `APP_DEBUG=1 ./target/app` would set the `debug` key - .merge(config::Environment::with_prefix("APP")) - .unwrap(); + .merge(config::Environment::with_prefix("APP")).unwrap(); // Print out our settings (as a HashMap) println!("{:?}", diff --git a/examples/watch/Settings.toml b/examples/watch/Settings.toml index f7881bb..8443f7b 100644 --- a/examples/watch/Settings.toml +++ b/examples/watch/Settings.toml @@ -1,3 +1,3 @@ -debug = true +debug = false port = 8080 host = "0.0.0.0" diff --git a/examples/watch/src/main.rs b/examples/watch/src/main.rs index 70fb7ef..0976f74 100644 --- a/examples/watch/src/main.rs +++ b/examples/watch/src/main.rs @@ -12,10 +12,12 @@ use std::sync::mpsc::channel; use std::time::Duration; lazy_static! { - static ref SETTINGS: RwLock<Config> = RwLock::new(Config::default() - .merge(File::with_name("Settings.toml")) - .unwrap() - ); + static ref SETTINGS: RwLock<Config> = RwLock::new({ + let mut settings = Config::default(); + settings.merge(File::with_name("Settings.toml")).unwrap(); + + settings + }); } fn show() { diff --git a/src/config.rs b/src/config.rs index 5b669a0..0ff2601 100644 --- a/src/config.rs +++ b/src/config.rs @@ -51,17 +51,20 @@ impl Config { } /// Merge in a configuration property source. - pub fn merge<T>(&mut self, source: T) -> ConfigResult - where T: 'static, - T: Source + Send + Sync + pub fn merge<T>(&mut self, source: T) -> Result<&mut Config> + where + T: 'static, + T: Source + Send + Sync, { match self.kind { - ConfigKind::Mutable { ref mut sources, .. } => { + ConfigKind::Mutable { + ref mut sources, .. + } => { sources.push(Box::new(source)); } ConfigKind::Frozen => { - return ConfigResult::Err(ConfigError::Frozen); + return Err(ConfigError::Frozen); } } @@ -73,7 +76,7 @@ impl Config { /// /// Configuration is automatically refreshed after a mutation /// operation (`set`, `merge`, `set_default`, etc.). - pub fn refresh(&mut self) -> ConfigResult { + pub fn refresh(&mut self) -> Result<&mut Config> { self.cache = match self.kind { // TODO: We need to actually merge in all the stuff ConfigKind::Mutable { @@ -89,9 +92,7 @@ impl Config { } // Add sources - if let Err(error) = sources.collect_to(&mut cache) { - return ConfigResult::Err(error); - } + sources.collect_to(&mut cache)?; // Add overrides for (key, val) in overrides { @@ -102,11 +103,11 @@ impl Config { } ConfigKind::Frozen => { - return ConfigResult::Err(ConfigError::Frozen); + return Err(ConfigError::Frozen); } }; - ConfigResult::Ok(self) + Ok(self) } /// Deserialize the entire configuration. @@ -114,41 +115,35 @@ impl Config { T::deserialize(self.cache.clone()) } - pub fn set_default<T>(&mut self, key: &str, value: T) -> ConfigResult - where T: Into<Value> + pub fn set_default<T>(&mut self, key: &str, value: T) -> Result<&mut Config> + where + T: Into<Value>, { match self.kind { - ConfigKind::Mutable { ref mut defaults, .. } => { - defaults.insert(match key.to_lowercase().parse() { - Ok(expr) => expr, - Err(error) => { - return ConfigResult::Err(error); - } - }, - value.into()); + ConfigKind::Mutable { + ref mut defaults, .. + } => { + defaults.insert(key.to_lowercase().parse()?, value.into()); } - ConfigKind::Frozen => return ConfigResult::Err(ConfigError::Frozen), + ConfigKind::Frozen => return Err(ConfigError::Frozen), }; self.refresh() } - pub fn set<T>(&mut self, key: &str, value: T) -> ConfigResult - where T: Into<Value> + pub fn set<T>(&mut self, key: &str, value: T) -> Result<&mut Config> + where + T: Into<Value>, { match self.kind { - ConfigKind::Mutable { ref mut overrides, .. } => { - overrides.insert(match key.to_lowercase().parse() { - Ok(expr) => expr, - Err(error) => { - return ConfigResult::Err(error); - } - }, - value.into()); + ConfigKind::Mutable { + ref mut overrides, .. + } => { + overrides.insert(key.to_lowercase().parse()?, value.into()); } - ConfigKind::Frozen => return ConfigResult::Err(ConfigError::Frozen), + ConfigKind::Frozen => return Err(ConfigError::Frozen), }; self.refresh() @@ -195,210 +190,3 @@ impl Config { self.get(key).and_then(Value::into_array) } } - -/// Holds the result of configuration alteration functions. -/// A manual alias of Result to enable a chained API and error forwarding. -pub enum ConfigResult<'a> { - Ok(&'a mut Config), - Err(ConfigError), -} - -#[inline] -fn unwrap_failed<E: Debug>(msg: &str, error: E) -> ! { - panic!("{}: {:?}", msg, error) -} - -impl<'a> ConfigResult<'a> { - /// Forwards `Config::merge` - pub fn merge<T>(self, source: T) -> ConfigResult<'a> - where T: 'static, - T: Source + Send + Sync - { - match self { - // If OK, Proceed to nested method - ConfigResult::Ok(instance) => instance.merge(source), - - // Else, Forward the error - error => error, - } - } - - /// Forwards `Config::set_default` - pub fn set_default<T>(self, key: &str, value: T) -> ConfigResult<'a> - where T: Into<Value>, - T: 'static - { - match self { - // If OK, Proceed to nested method - ConfigResult::Ok(instance) => instance.set_default(key, value), - - // Else, Forward the error - error => error, - } - } - - /// Forwards `Config::set` - pub fn set<T>(self, key: &str, value: T) -> ConfigResult<'a> - where T: Into<Value>, - T: 'static - { - match self { - // If OK, Proceed to nested method - ConfigResult::Ok(instance) => instance.set(key, value), - - // Else, Forward the error - error => error, - } - } - - /// Deserialize the entire configuration. - pub fn deserialize<'de, T: Deserialize<'de>>(self) -> Result<T> { - self.and_then(|instance| instance.deserialize()) - } - - /// Creates a `Result` out of this `ConfigResult` - #[inline] - pub fn to_result(self) -> Result<Config> { - match self { - ConfigResult::Ok(value) => Ok(value.clone()), - ConfigResult::Err(error) => Err(error), - } - } - - /// Forwards `Result::is_ok` - #[inline] - pub fn is_ok(&self) -> bool { - match *self { - ConfigResult::Ok(_) => true, - ConfigResult::Err(_) => false, - } - } - - /// Forwards `Result::is_err` - #[inline] - pub fn is_err(&self) -> bool { - !self.is_ok() - } - - /// Forwards `Result::ok` - #[inline] - pub fn ok(self) -> Option<Config> { - match self { - ConfigResult::Ok(x) => Some(x.clone()), - ConfigResult::Err(_) => None, - } - } - - /// Forwards `Result::err` - #[inline] - pub fn err(self) -> Option<ConfigError> { - match self { - ConfigResult::Ok(_) => None, - ConfigResult::Err(x) => Some(x), - } - } - - /// Forwards `Result::map` - #[inline] - pub fn map<U, F>(self, op: F) -> Result<U> - where - F: FnOnce(Config) -> U, - { - match self { - ConfigResult::Ok(x) => Ok(op(x.clone())), - ConfigResult::Err(error) => Err(error), - } - } - - /// Forwards `Result::map_err` - #[inline] - pub fn map_err<F, O>(self, op: O) -> ::std::result::Result<Config, F> - where - O: FnOnce(ConfigError) -> F, - { - match self { - ConfigResult::Err(error) => Err(op(error)), - ConfigResult::Ok(value) => Ok(value.clone()), - } - } - - /// Forwards `Result::and` - #[inline] - pub fn and<U>(self, res: Result<U>) -> Result<U> - { - match self { - ConfigResult::Ok(_) => res, - ConfigResult::Err(error) => Err(error), - } - } - - /// Forwards `Result::and_then` - #[inline] - pub fn and_then<U, F>(self, op: F) -> Result<U> - where - F: FnOnce(Config) -> Result<U>, - { - match self { - ConfigResult::Ok(value) => op(value.clone()), - ConfigResult::Err(error) => Err(error), - } - } - - /// Forwards `Result::or` - #[inline] - pub fn or<F>(self, res: ::std::result::Result<Config, F>) -> ::std::result::Result<Config, F> - { - match self { - ConfigResult::Ok(value) => Ok(value.clone()), - ConfigResult::Err(_) => res, - } - } - - /// Forwards `Result::or_else` - #[inline] - pub fn or_else<F, O>(self, op: O) -> ::std::result::Result<Config, F> - where - O: FnOnce(ConfigError) -> ::std::result::Result<Config, F>, - { - match self { - ConfigResult::Ok(value) => Ok(value.clone()), - ConfigResult::Err(error) => op(error), - } - } - - /// Forwards `Result::unwrap` - #[inline] - pub fn unwrap(self) -> Config { - match self { - ConfigResult::Ok(instance) => instance.clone(), - ConfigResult::Err(error) => unwrap_failed("called `ConfigResult::unwrap()` on an `Err` value", error), - } - } - - /// Forwards `Result::expect` - #[inline] - pub fn expect(self, msg: &str) -> Config { - match self { - ConfigResult::Ok(instance) => instance.clone(), - ConfigResult::Err(error) => unwrap_failed(msg, error), - } - } - - /// Forwards `Result::unwrap_err` - #[inline] - pub fn unwrap_err(self) -> ConfigError { - match self { - ConfigResult::Ok(t) => unwrap_failed("called `ConfigResult::unwrap_err()` on an `Ok` value", t), - ConfigResult::Err(e) => e, - } - } - - /// Forwards `Result::expect_err` - #[inline] - pub fn expect_err(self, msg: &str) -> ConfigError { - match self { - ConfigResult::Ok(t) => unwrap_failed(msg, t), - ConfigResult::Err(e) => e, - } - } -} @@ -1,5 +1,5 @@ use serde::de; -use value::{Value, ValueWithKey, ValueKind}; +use value::{Value, ValueKind, ValueWithKey}; use error::*; use std::borrow::Cow; use std::iter::Peekable; @@ -13,7 +13,8 @@ impl<'de> de::Deserializer<'de> for ValueWithKey<'de> { #[inline] fn deserialize_any<V>(self, visitor: V) -> Result<V::Value> - where V: de::Visitor<'de> + where + V: de::Visitor<'de>, { // Deserialize based on the underlying type match self.0.kind { @@ -101,7 +102,8 @@ impl<'de> de::Deserializer<'de> for ValueWithKey<'de> { #[inline] fn deserialize_option<V>(self, visitor: V) -> Result<V::Value> - where V: de::Visitor<'de> + where + V: de::Visitor<'de>, { // Match an explicit nil as None and everything else as Some match self.0.kind { @@ -122,7 +124,8 @@ impl<'de> de::Deserializer<'de> for Value { #[inline] fn deserialize_any<V>(self, visitor: V) -> Result<V::Value> - where V: de::Visitor<'de> + where + V: de::Visitor<'de>, { // Deserialize based on the underlying type match self.kind { @@ -210,7 +213,8 @@ impl<'de> de::Deserializer<'de> for Value { #[inline] fn deserialize_option<V>(self, visitor: V) -> Result<V::Value> - where V: de::Visitor<'de> + where + V: de::Visitor<'de>, { // Match an explicit nil as None and everything else as Some match self.kind { @@ -265,7 +269,8 @@ impl<'de> de::SeqAccess<'de> for SeqAccess { type Error = ConfigError; fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>> - where T: de::DeserializeSeed<'de> + where + T: de::DeserializeSeed<'de>, { match self.elements.next() { Some(value) => seed.deserialize(value).map(Some), @@ -299,7 +304,8 @@ impl<'de> de::MapAccess<'de> for MapAccess { type Error = ConfigError; fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>> - where K: de::DeserializeSeed<'de> + where + K: de::DeserializeSeed<'de>, { if self.index >= self.elements.len() { return Ok(None); @@ -313,7 +319,8 @@ impl<'de> de::MapAccess<'de> for MapAccess { } fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value> - where V: de::DeserializeSeed<'de> + where + V: de::DeserializeSeed<'de>, { de::DeserializeSeed::deserialize(seed, self.elements.remove(0).1) } @@ -88,8 +88,10 @@ impl Source for Environment { // Replace `separator` with `.` key = key.replace(&self.separator, "."); - m.insert(key.to_lowercase(), - Value::new(Some(&uri), ValueKind::String(value))); + m.insert( + key.to_lowercase(), + Value::new(Some(&uri), ValueKind::String(value)), + ); } Ok(m) diff --git a/src/error.rs b/src/error.rs index 514a5e2..eff3c7c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -13,7 +13,7 @@ pub enum Unexpected { Str(String), Unit, Seq, - Map + Map, } impl fmt::Display for Unexpected { @@ -50,7 +50,7 @@ pub enum ConfigError { /// The captured error from attempting to parse the file in its desired format. /// This is the actual error object from the library used for the parsing. - cause: Box<Error + Send + Sync> + cause: Box<Error + Send + Sync>, }, /// Value could not be converted into the requested type. @@ -81,27 +81,34 @@ pub enum ConfigError { impl ConfigError { // FIXME: pub(crate) #[doc(hidden)] - pub fn invalid_type(origin: Option<String>, unexpected: Unexpected, expected: &'static str) -> Self { + pub fn invalid_type( + origin: Option<String>, + unexpected: Unexpected, + expected: &'static str, + ) -> Self { ConfigError::Type { origin: origin, unexpected: unexpected, expected: expected, key: None, - } + } } // FIXME: pub(crate) #[doc(hidden)] pub fn extend_with_key(self, key: &str) -> Self { match self { - ConfigError::Type { origin, unexpected, expected, .. } => { - ConfigError::Type { - origin: origin, - unexpected: unexpected, - expected: expected, - key: Some(key.into()), - } - } + ConfigError::Type { + origin, + unexpected, + expected, + .. + } => ConfigError::Type { + origin: origin, + unexpected: unexpected, + expected: expected, + key: Some(key.into()), + }, _ => self, } @@ -113,7 +120,7 @@ pub type Result<T> = result::Result<T, ConfigError>; // Forward Debug to Display for readable panic! messages impl fmt::Debug for ConfigError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", *self) } } @@ -121,25 +128,23 @@ impl fmt::Debug for ConfigError { impl fmt::Display for ConfigError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - ConfigError::Frozen | ConfigError::PathParse(_) => { - write!(f, "{}", self.description()) - } + ConfigError::Frozen | ConfigError::PathParse(_) => write!(f, "{}", self.description()), - ConfigError::Message(ref s) => { - write!(f, "{}", s) - } + ConfigError::Message(ref s) => write!(f, "{}", s), - ConfigError::Foreign(ref cause) => { - write!(f, "{}", cause) - } + ConfigError::Foreign(ref cause) => write!(f, "{}", cause), ConfigError::NotFound(ref key) => { write!(f, "configuration property {:?} not found", key) } - ConfigError::Type { ref origin, ref unexpected, expected, ref key } => { - write!(f, "invalid type: {}, expected {}", - unexpected, expected)?; + ConfigError::Type { + ref origin, + ref unexpected, + expected, + ref key, + } => { + write!(f, "invalid type: {}, expected {}", unexpected, expected)?; if let Some(ref key) = *key { write!(f, " for key `{}`", key)?; @@ -171,7 +176,9 @@ impl Error for ConfigError { ConfigError::Frozen => "configuration is frozen", ConfigError::NotFound(_) => "configuration property not found", ConfigError::Type { .. } => "invalid type", - ConfigError::Foreign(ref cause) | ConfigError::FileParse { ref cause, .. } => cause.description(), + ConfigError::Foreign(ref cause) | ConfigError::FileParse { ref cause, .. } => { + cause.description() + } ConfigError::PathParse(ref kind) => kind.description(), _ => "configuration error", @@ -180,9 +187,11 @@ impl Error for ConfigError { fn cause(&self) -> Option<&Error> { match *self { - ConfigError::Foreign(ref cause) | ConfigError::FileParse { ref cause, .. } => Some(cause.as_ref()), + ConfigError::Foreign(ref cause) | ConfigError::FileParse { ref cause, .. } => { + Some(cause.as_ref()) + } - _ => None + _ => None, } } } diff --git a/src/file/format/json.rs b/src/file/format/json.rs index ead99f8..caf62f5 100644 --- a/src/file/format/json.rs +++ b/src/file/format/json.rs @@ -4,7 +4,10 @@ use std::collections::HashMap; use std::error::Error; use value::{Value, ValueKind}; -pub fn parse(uri: Option<&String>, text: &str) -> Result<HashMap<String, Value>, Box<Error + Send + Sync>> { +pub fn parse( + uri: Option<&String>, + text: &str, +) -> Result<HashMap<String, Value>, Box<Error + Send + Sync>> { // Parse a JSON object value from the text // TODO: Have a proper error fire if the root of a file is ever not a Table let value = from_json_value(uri, &serde_json::from_str(text)?); @@ -19,15 +22,13 @@ fn from_json_value(uri: Option<&String>, value: &serde_json::Value) -> Value { match *value { serde_json::Value::String(ref value) => Value::new(uri, ValueKind::String(value.clone())), - serde_json::Value::Number(ref value) => { - if let Some(value) = value.as_i64() { - Value::new(uri, ValueKind::Integer(value)) - } else if let Some(value) = value.as_f64() { - Value::new(uri, ValueKind::Float(value)) - } else { - unreachable!(); - } - } + serde_json::Value::Number(ref value) => if let Some(value) = value.as_i64() { + Value::new(uri, ValueKind::Integer(value)) + } else if let Some(value) = value.as_f64() { + Value::new(uri, ValueKind::Float(value)) + } else { + unreachable!(); + }, serde_json::Value::Bool(value) => Value::new(uri, ValueKind::Boolean(value)), diff --git a/src/file/format/mod.rs b/src/file/format/mod.rs <index 5aa1acb..a90dfda 100644 --- a/src/file/format/mod.rs +++ b/src/file/format/mod.rs |