From 2b438ed9b53fee5689032f3b5fcdda8d15becd5f Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Thu, 22 Jun 2017 14:14:29 -0700 Subject: Add builder API to Config --- src/config.rs | 185 +++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 158 insertions(+), 27 deletions(-) (limited to 'src/config.rs') diff --git a/src/config.rs b/src/config.rs index 44c51d1..c8fc2e4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,7 @@ use std::collections::HashMap; +use std::ops::Deref; use std::str::FromStr; +use std::fmt::Debug; use serde::de::Deserialize; use error::*; @@ -8,6 +10,7 @@ use source::Source; use value::{Value, ValueWithKey}; use path; +#[derive(Clone, Debug)] enum ConfigKind { // A mutable configuration. This is the default. Mutable { @@ -34,7 +37,7 @@ impl Default for ConfigKind { /// A prioritized configuration repository. It maintains a set of /// configuration sources, fetches values to populate those, and provides /// them according to the source's priority. -#[derive(Default)] +#[derive(Default, Clone, Debug)] pub struct Config { kind: ConfigKind, @@ -48,7 +51,7 @@ impl Config { } /// Merge in a configuration property source. - pub fn merge(&mut self, source: T) -> Result<()> + pub fn merge(&mut self, source: T) -> ConfigResult where T: 'static, T: Source + Send + Sync { @@ -58,7 +61,7 @@ impl Config { } ConfigKind::Frozen => { - return Err(ConfigError::Frozen); + return ConfigResult(Err(ConfigError::Frozen)); } } @@ -70,7 +73,7 @@ impl Config { /// /// Configuration is automatically refreshed after a mutation /// operation (`set`, `merge`, `set_default`, etc.). - pub fn refresh(&mut self) -> Result<()> { + pub fn refresh(&mut self) -> ConfigResult { self.cache = match self.kind { // TODO: We need to actually merge in all the stuff ConfigKind::Mutable { @@ -87,14 +90,23 @@ impl Config { // Add sources for source in sources { - let props = source.collect()?; + 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()) + _ => { + path::Expression::Identifier(key.clone()) + .set(&mut cache, val.clone()) + } } } } @@ -105,14 +117,14 @@ impl Config { } cache - }, + } ConfigKind::Frozen => { - return Err(ConfigError::Frozen); + return ConfigResult(Err(ConfigError::Frozen)); } }; - Ok(()) + ConfigResult(Ok(self)) } /// Deserialize the entire configuration. @@ -120,39 +132,41 @@ impl Config { T::deserialize(self.cache.clone()) } - pub fn set_default(&mut self, key: &str, value: T) -> Result<()> + pub fn set_default(&mut self, key: &str, value: T) -> ConfigResult where T: Into { match self.kind { - ConfigKind::Mutable { - ref mut defaults, - .. - } => { - defaults.insert(key.to_lowercase().parse()?, value.into()); + ConfigKind::Mutable { ref mut defaults, .. } => { + defaults.insert(match key.to_lowercase().parse() { + Ok(expr) => expr, + Err(error) => { + return ConfigResult(Err(error)); + } + }, + value.into()); } - ConfigKind::Frozen => { - return Err(ConfigError::Frozen) - } + ConfigKind::Frozen => return ConfigResult(Err(ConfigError::Frozen)), }; self.refresh() } - pub fn set(&mut self, key: &str, value: T) -> Result<()> + pub fn set(&mut self, key: &str, value: T) -> ConfigResult where T: Into { match self.kind { - ConfigKind::Mutable { - ref mut overrides, - .. - } => { - overrides.insert(key.to_lowercase().parse()?, value.into()); + ConfigKind::Mutable { ref mut overrides, .. } => { + overrides.insert(match key.to_lowercase().parse() { + Ok(expr) => expr, + Err(error) => { + return ConfigResult(Err(error)); + } + }, + value.into()); } - ConfigKind::Frozen => { - return Err(ConfigError::Frozen) - } + ConfigKind::Frozen => return ConfigResult(Err(ConfigError::Frozen)), }; self.refresh() @@ -199,3 +213,120 @@ impl Config { self.get(key).and_then(Value::into_array) } } + +pub struct ConfigResult<'a>(Result<&'a mut Config>); + +#[inline] +fn unwrap_failed(msg: &str, error: E) -> ! { + panic!("{}: {:?}", msg, error) +} + +impl<'a> ConfigResult<'a> { + pub fn merge(self, source: T) -> ConfigResult<'a> + where T: 'static, + T: Source + Send + Sync + { + match self.0 { + // If OK, Proceed to nested method + Ok(instance) => instance.merge(source), + + // Else, Forward the error + error => ConfigResult(error), + } + } + + pub fn set_default(self, key: &str, value: T) -> ConfigResult<'a> + where T: Into, + T: 'static + { + match self.0 { + // If OK, Proceed to nested method + Ok(instance) => instance.set_default(key, value), + + // Else, Forward the error + error => ConfigResult(error), + } + } + + pub fn set(self, key: &str, value: T) -> ConfigResult<'a> + where T: Into, + T: 'static + { + match self.0 { + // If OK, Proceed to nested method + Ok(instance) => instance.set(key, value), + + // Else, Forward the error + error => ConfigResult(error), + } + } + + /// Forwards `Result::is_ok` + #[inline] + pub fn is_ok(&self) -> bool { + match self.0 { + Ok(_) => true, + 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 { + match self.0 { + Ok(x) => Some(x.clone()), + Err(_) => None, + } + } + + /// Forwards `Result::err` + #[inline] + pub fn err(self) -> Option { + match self.0 { + Ok(_) => None, + Err(x) => Some(x), + } + } + + /// Forwards `Result::unwrap` + #[inline] + pub fn unwrap(self) -> Config { + match self.0 { + Ok(instance) => instance.clone(), + Err(error) => unwrap_failed("called `Result::unwrap()` on an `Err` value", error), + } + } + + /// Forwards `Result::expect` + #[inline] + pub fn expect(self, msg: &str) -> Config { + match self.0 { + Ok(instance) => instance.clone(), + Err(error) => unwrap_failed(msg, error), + } + } + + /// Forwards `Result::unwrap_err` + #[inline] + pub fn unwrap_err(self) -> ConfigError { + match self.0 { + Ok(t) => unwrap_failed("called `Result::unwrap_err()` on an `Ok` value", t), + Err(e) => e, + } + } + + /// Forwards `Result::expect_err` + #[inline] + pub fn expect_err(self, msg: &str) -> ConfigError { + match self.0 { + Ok(t) => unwrap_failed(msg, t), + Err(e) => e, + } + } +} -- cgit v1.2.3