From 40a44a459fde4c4042d5188faedea718a34e2b96 Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Mon, 23 Jan 2017 18:58:19 -0800 Subject: Initial commit. --- src/lib.rs | 241 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 src/lib.rs (limited to 'src/lib.rs') diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..0be9063 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,241 @@ +#![feature(try_from)] + +mod value; + +use value::Value; + +use std::env; +use std::convert::TryFrom; +use std::collections::HashMap; + +#[derive(Default)] +pub struct Config { + defaults: HashMap, + overrides: HashMap, + environ: HashMap, +} + +impl Config { + pub fn new() -> Config { + Default::default() + } + + pub fn set_default(&mut self, key: &str, value: T) + where T: Into + { + self.defaults.insert(key.into(), value.into()); + } + + pub fn set(&mut self, key: &str, value: T) + where T: Into + { + self.overrides.insert(key.into(), value.into()); + } + + pub fn get<'a, T>(&'a mut self, key: &str) -> Option + where T: TryFrom<&'a mut Value>, + T: Default + { + if let Some(value) = self.overrides.get_mut(key) { + T::try_from(value).ok() + } else if let Ok(value) = env::var(key.to_uppercase()) { + // Store the environment variable into an environ + // hash map; we want to return references + + // TODO: Key name needs to go through a transform + self.environ.insert(key.to_lowercase().into(), value.into()); + + T::try_from(self.environ.get_mut(key).unwrap()).ok() + } else if let Some(value) = self.defaults.get_mut(key) { + T::try_from(value).ok() + } else { + None + } + } + + pub fn get_str<'a>(&'a mut self, key: &str) -> Option<&'a str> { + self.get(key) + } + + pub fn get_int(&mut self, key: &str) -> Option { + self.get(key) + } + + pub fn get_float(&mut self, key: &str) -> Option { + self.get(key) + } + + pub fn get_bool(&mut self, key: &str) -> Option { + self.get(key) + } +} + +#[cfg(test)] +mod test { + use std::env; + + // Retrieval of a non-existent key + #[test] + fn test_not_found() { + let mut c = super::Config::new(); + + assert_eq!(c.get_int("key"), None); + } + + // Environment override + #[test] + fn test_env_override() { + let mut c = super::Config::new(); + + c.set_default("key_1", false); + + env::set_var("KEY_1", "1"); + + assert_eq!(c.get_bool("key_1"), Some(true)); + } + + // Explicit override + #[test] + fn test_default_override() { + let mut c = super::Config::new(); + + c.set_default("key_1", false); + c.set_default("key_2", false); + + assert!(!c.get_bool("key_1").unwrap()); + assert!(!c.get_bool("key_2").unwrap()); + + c.set("key_2", true); + + assert!(!c.get_bool("key_1").unwrap()); + assert!(c.get_bool("key_2").unwrap()); + } + + // Storage and retrieval of String values + #[test] + fn test_str() { + let mut c = super::Config::new(); + + 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 + #[test] + fn test_bool() { + let mut c = super::Config::new(); + + c.set("key", true); + + assert_eq!(c.get_bool("key").unwrap(), true); + assert!(false != c.get("key").unwrap()); + } + + // Storage and retrieval of Float values + #[test] + fn test_float() { + let mut c = super::Config::new(); + + 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 + #[test] + fn test_int() { + let mut c = super::Config::new(); + + c.set("key", 42); + + assert_eq!(c.get_int("key").unwrap(), 42); + assert!(42 == c.get::("key").unwrap()); + } + + // Storage of various values and retrieval as String + #[test] + fn test_retrieve_str() { + let mut c = super::Config::new(); + + c.set("key_1", 115); + 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")); + } + + // Storage of various values and retrieval as Integer + #[test] + fn test_retrieve_int() { + let mut c = super::Config::new(); + + c.set("key_1", "121"); + c.set("key_2", 5.12); + c.set("key_3", 5.72); + c.set("key_4", false); + c.set("key_5", true); + c.set("key_6", "asga"); + + assert_eq!(c.get_int("key_1"), Some(121)); + assert_eq!(c.get_int("key_2"), Some(5)); + assert_eq!(c.get_int("key_3"), Some(6)); + assert_eq!(c.get_int("key_4"), Some(0)); + assert_eq!(c.get_int("key_5"), Some(1)); + assert_eq!(c.get_int("key_6"), None); + } + + // Storage of various values and retrieval as Float + #[test] + fn test_retrieve_float() { + let mut c = super::Config::new(); + + c.set("key_1", "121"); + c.set("key_2", "121.512"); + c.set("key_3", 5); + c.set("key_4", false); + c.set("key_5", true); + c.set("key_6", "asga"); + + assert_eq!(c.get_float("key_1"), Some(121.0)); + assert_eq!(c.get_float("key_2"), Some(121.512)); + assert_eq!(c.get_float("key_3"), Some(5.0)); + assert_eq!(c.get_float("key_4"), Some(0.0)); + assert_eq!(c.get_float("key_5"), Some(1.0)); + assert_eq!(c.get_float("key_6"), None); + } + + // Storage of various values and retrieval as Boolean + #[test] + fn test_retrieve_bool() { + let mut c = super::Config::new(); + + c.set("key_1", "121"); + c.set("key_2", "1"); + c.set("key_3", "0"); + c.set("key_4", "true"); + c.set("key_5", ""); + c.set("key_6", 51); + c.set("key_7", 0); + c.set("key_8", 12.12); + c.set("key_9", 1.0); + c.set("key_10", 0.0); + c.set("key_11", "asga"); + + assert_eq!(c.get_bool("key_1"), Some(false)); + assert_eq!(c.get_bool("key_2"), Some(true)); + assert_eq!(c.get_bool("key_3"), Some(false)); + assert_eq!(c.get_bool("key_4"), Some(true)); + assert_eq!(c.get_bool("key_5"), Some(false)); + assert_eq!(c.get_bool("key_6"), Some(true)); + assert_eq!(c.get_bool("key_7"), Some(false)); + assert_eq!(c.get_bool("key_8"), Some(true)); + assert_eq!(c.get_bool("key_9"), Some(true)); + assert_eq!(c.get_bool("key_10"), Some(false)); + assert_eq!(c.get_bool("key_11"), Some(false)); + } +} -- cgit v1.2.3