summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRyan Leckey <leckey.ryan@gmail.com>2017-01-25 18:07:12 -0800
committerRyan Leckey <leckey.ryan@gmail.com>2017-01-25 18:07:12 -0800
commit91f72c5c2a20c5a9b6b17b5d9ee626645783e5db (patch)
treec420add20f11d7cee3a1dadee060b4e1cd3fe017
parent286703d802a433b78ebb334f864136b942f86b5c (diff)
Big cleanup of the Value API
-rw-r--r--src/config.rs85
-rw-r--r--src/lib.rs13
-rw-r--r--src/value.rs189
3 files changed, 139 insertions, 148 deletions
diff --git a/src/config.rs b/src/config.rs
index 8590a98..fcdc2e1 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -3,8 +3,8 @@ use source::Source;
use std::env;
use std::error::Error;
-use std::convert::TryFrom;
use std::collections::HashMap;
+use std::borrow::Cow;
#[derive(Default)]
pub struct Config {
@@ -16,6 +16,26 @@ pub struct Config {
sources: Vec<HashMap<String, Value>>,
}
+trait ConfigGetResult {
+ type Type;
+}
+
+impl<'a> ConfigGetResult for &'a str {
+ type Type = Cow<'a, str>;
+}
+
+impl ConfigGetResult for i64 {
+ type Type = i64;
+}
+
+impl ConfigGetResult for f64 {
+ type Type = f64;
+}
+
+impl ConfigGetResult for bool {
+ type Type = bool;
+}
+
impl Config {
pub fn new() -> Config {
Default::default()
@@ -54,14 +74,11 @@ impl Config {
self.overrides.insert(key.to_lowercase(), value.into());
}
- pub fn get<'a, T>(&'a mut self, key: &str) -> Option<T>
- where T: TryFrom<&'a mut Value>,
- T: Default
- {
+ pub fn get<'a>(&'a mut self, key: &str) -> Option<&'a Value> {
// Check explicit override
- if let Some(value) = self.overrides.get_mut(key) {
- return T::try_from(value).ok();
+ if let Some(value) = self.overrides.get(key) {
+ return Some(value);
}
// Check environment
@@ -78,44 +95,58 @@ impl Config {
env_key.push_str(&key.to_uppercase());
if let Ok(value) = env::var(env_key.clone()) {
- // Store the environment variable into an environ
- // hash map; we want to return references
- self.environ.insert(key.to_lowercase().into(), value.into());
-
- return T::try_from(self.environ.get_mut(key).unwrap()).ok();
+ // TODO: Find a better way to do this?
+ self.environ.insert(key.into(), value.into());
+ return self.environ.get(key);
}
// Check sources
- for source in &mut self.sources.iter_mut().rev() {
- if let Some(value) = source.get_mut(key) {
- return T::try_from(value).ok();
+ for source in &mut self.sources.iter().rev() {
+ if let Some(value) = source.get(key) {
+ return Some(value);
}
}
// Check explicit defaults
- if let Some(value) = self.defaults.get_mut(key) {
- return T::try_from(value).ok();
+ if let Some(value) = self.defaults.get(key) {
+ return Some(value);
}
None
}
- pub fn get_str<'a>(&'a mut self, key: &str) -> Option<&'a str> {
- self.get(key)
+ pub fn get_str<'a>(&'a mut self, key: &str) -> Option<Cow<'a, str>> {
+ if let Some(value) = self.get(key) {
+ value.as_str()
+ } else {
+ None
+ }
}
pub fn get_int(&mut self, key: &str) -> Option<i64> {
- self.get(key)
+ if let Some(value) = self.get(key) {
+ value.as_int()
+ } else {
+ None
+ }
}
pub fn get_float(&mut self, key: &str) -> Option<f64> {
- self.get(key)
+ if let Some(value) = self.get(key) {
+ value.as_float()
+ } else {
+ None
+ }
}
pub fn get_bool(&mut self, key: &str) -> Option<bool> {
- self.get(key)
+ if let Some(value) = self.get(key) {
+ value.as_bool()
+ } else {
+ None
+ }
}
}
@@ -190,7 +221,6 @@ mod test {
c.set("key", "value");
assert_eq!(c.get_str("key").unwrap(), "value");
- assert!("value" == c.get::<&str>("key").unwrap());
}
// Storage and retrieval of Boolean values
@@ -201,7 +231,6 @@ mod test {
c.set("key", true);
assert_eq!(c.get_bool("key").unwrap(), true);
- assert!(false != c.get("key").unwrap());
}
// Storage and retrieval of Float values
@@ -212,7 +241,6 @@ mod test {
c.set("key", 3.14);
assert_eq!(c.get_float("key").unwrap(), 3.14);
- assert!(3.14 >= c.get("key").unwrap());
}
// Storage and retrieval of Integer values
@@ -223,7 +251,6 @@ mod test {
c.set("key", 42);
assert_eq!(c.get_int("key").unwrap(), 42);
- assert!(42 == c.get::<i64>("key").unwrap());
}
// Storage of various values and retrieval as String
@@ -235,9 +262,9 @@ mod test {
c.set("key_2", 1.23);
c.set("key_3", false);
- assert_eq!(c.get_str("key_1"), Some("115"));
- assert_eq!(c.get_str("key_2"), Some("1.23"));
- assert_eq!(c.get_str("key_3"), Some("false"));
+ assert_eq!(c.get_str("key_1").unwrap(), "115");
+ assert_eq!(c.get_str("key_2").unwrap(), "1.23");
+ assert_eq!(c.get_str("key_3").unwrap(), "false");
}
// Storage of various values and retrieval as Integer
diff --git a/src/lib.rs b/src/lib.rs
index 55266f4..0ae525b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,5 @@
-#![feature(try_from)]
#![feature(drop_types_in_const)]
+#![allow(unknown_lints)]
extern crate toml;
@@ -8,13 +8,13 @@ mod source;
mod config;
use std::error::Error;
-use std::convert::TryFrom;
+use std::borrow::Cow;
use std::sync::{Once, ONCE_INIT};
pub use source::Source;
pub use source::File;
-use value::Value;
+pub use value::Value;
pub use config::Config;
@@ -55,14 +55,11 @@ pub fn set<T>(key: &str, value: T)
global().set(key, value)
}
-pub fn get<'a, T>(key: &str) -> Option<T>
- where T: TryFrom<&'a mut Value>,
- T: Default
-{
+pub fn get<'a>(key: &str) -> Option<&'a Value> {
global().get(key)
}
-pub fn get_str<'a>(key: &str) -> Option<&'a str> {
+pub fn get_str<'a>(key: &str) -> Option<Cow<'a, str>> {
global().get_str(key)
}
diff --git a/src/value.rs b/src/value.rs
index 295c7c0..a2b037c 100644
--- a/src/value.rs
+++ b/src/value.rs
@@ -1,16 +1,84 @@
-use std::convert::{From, TryFrom};
+use std::convert::From;
+use std::borrow::Cow;
// Variant for a configuration Value
-// The additional Option<String> is used for the textual representation of the
-// underlying type (to cache the string generation) but only if requested.
+#[derive(Clone)]
pub enum Value {
String(String),
- Integer(i64, Option<String>),
- Float(f64, Option<String>),
- Boolean(bool, Option<String>),
+ Integer(i64),
+ Float(f64),
+ Boolean(bool),
}
-// Conversion from type into variant
+impl Value {
+ /// Gets the underyling value as a string, performing a conversion only if neccessary.
+ #[allow(needless_lifetimes)]
+ pub fn as_str<'a>(&'a self) -> Option<Cow<'a, str>> {
+ if let Value::String(ref value) = *self {
+ Some(Cow::Borrowed(value))
+ } else if let Value::Integer(value) = *self {
+ Some(Cow::Owned(value.to_string()))
+ } else if let Value::Float(value) = *self {
+ Some(Cow::Owned(value.to_string()))
+ } else if let Value::Boolean(value) = *self {
+ Some(Cow::Owned(value.to_string()))
+ } else {
+ None
+ }
+ }
+
+ /// Gets the underlying type as a boolean, performing a conversion only if neccessary.
+ pub fn as_bool(&self) -> Option<bool> {
+ if let Value::Boolean(value) = *self {
+ Some(value)
+ } else if let Value::String(ref value) = *self {
+ Some(match value.to_lowercase().as_ref() {
+ "1" | "true" | "on" | "yes" => true,
+ _ => false,
+ })
+ } else if let Value::Integer(value) = *self {
+ Some(value != 0)
+ } else if let Value::Float(value) = *self {
+ Some(value != 0.0)
+ } else {
+ None
+ }
+ }
+
+ /// Gets the underlying type as an integer, performing a conversion only if neccessary.
+ pub fn as_int(&self) -> Option<i64> {
+ if let Value::Integer(value) = *self {
+ Some(value)
+ } else if let Value::String(ref value) = *self {
+ value.parse().ok()
+ } else if let Value::Boolean(value) = *self {
+ Some(if value { 1 } else { 0 })
+ } else if let Value::Float(value) = *self {
+ Some(value.round() as i64)
+ } else {
+ None
+ }
+ }
+
+ /// Gets the underlying type as a floating-point, performing a conversion only if neccessary.
+ pub fn as_float(&self) -> Option<f64> {
+ if let Value::Float(value) = *self {
+ Some(value)
+ } else if let Value::String(ref value) = *self {
+ value.parse().ok()
+ } else if let Value::Integer(value) = *self {
+ Some(value as f64)
+ } else if let Value::Boolean(value) = *self {
+ Some(if value { 1.0 } else { 0.0 })
+ } else {
+ None
+ }
+ }
+}
+
+// Generalized construction from type into variant is needed
+// for setting configuration values
+
impl From<String> for Value {
fn from(value: String) -> Value {
Value::String(value)
@@ -25,119 +93,18 @@ impl<'a> From<&'a str> for Value {
impl From<i64> for Value {
fn from(value: i64) -> Value {
- Value::Integer(value, None)
+ Value::Integer(value)
}
}
impl From<f64> for Value {
fn from(value: f64) -> Value {
- Value::Float(value, None)
+ Value::Float(value)
}
}
impl From<bool> for Value {
fn from(value: bool) -> Value {
- Value::Boolean(value, None)
- }
-}
-
-// Conversion from variant into type
-impl<'a> TryFrom<&'a mut Value> for &'a str {
- type Err = ();
-
- fn try_from(value: &mut Value) -> Result<&str, ()> {
- // When converting a non-string value into a string;
- // cache the conversion and return a reference
-
- if let Value::String(ref value) = *value {
- Ok(value)
- } else if let Value::Integer(value, ref mut text) = *value {
- if let Some(ref text) = *text {
- Ok(text)
- } else {
- *text = Some(value.to_string());
-
- Ok(text.as_ref().unwrap())
- }
- } else if let Value::Float(value, ref mut text) = *value {
- if let Some(ref text) = *text {
- Ok(text)
- } else {
- *text = Some(value.to_string());
-
- Ok(text.as_ref().unwrap())
- }
- } else if let Value::Boolean(value, ref mut text) = *value {
- if let Some(ref text) = *text {
- Ok(text)
- } else {
- *text = Some(value.to_string());
-
- Ok(text.as_ref().unwrap())
- }
- } else {
- Err(())
- }
- }
-}
-
-impl<'a> TryFrom<&'a mut Value> for i64 {
- type Err = ();
-
- fn try_from(value: &mut Value) -> Result<i64, ()> {
- if let Value::Integer(value, ..) = *value {
- Ok(value)
- } else if let Value::String(ref value) = *value {
- value.parse().map_err(|_| {
- // Drop specific error
- })
- } else if let Value::Boolean(value, ..) = *value {
- Ok(if value { 1 } else { 0 })
- } else if let Value::Float(value, ..) = *value {
- Ok(value.round() as i64)
- } else {
- Err(())
- }
- }
-}
-
-impl<'a> TryFrom<&'a mut Value> for f64 {
- type Err = ();
-
- fn try_from(value: &mut Value) -> Result<f64, ()> {
- if let Value::Float(value, ..) = *value {
- Ok(value)
- } else if let Value::String(ref value) = *value {
- value.parse().map_err(|_| {
- // Drop specific error
- })
- } else if let Value::Integer(value, ..) = *value {
- Ok(value as f64)
- } else if let Value::Boolean(value, ..) = *value {
- Ok(if value { 1.0 } else { 0.0 })
- } else {
- Err(())
- }
- }
-}
-
-impl<'a> TryFrom<&'a mut Value> for bool {
- type Err = ();
-
- fn try_from(value: &mut Value) -> Result<bool, ()> {
- if let Value::Boolean(value, ..) = *value {
- Ok(value)
- } else if let Value::String(ref value) = *value {
- Ok(match value.to_lowercase().as_ref() {
- "1" | "true" | "on" | "yes" => true,
- _ => false,
- })
- } else if let Value::Integer(value, ..) = *value {
- Ok(value != 0)
- } else if let Value::Float(value, ..) = *value {
- Ok(value != 0.0)
- } else {
- Err(())
- }
+ Value::Boolean(value)
}
}