From a6fb2f92dc8d53660c1d2d066f146ef261052330 Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Thu, 26 Jan 2017 00:09:41 -0800 Subject: Add some examples --- examples/basic-file/Cargo.toml | 7 ++ examples/basic-file/src/main.rs | 9 +++ examples/basic/Cargo.toml | 7 ++ examples/basic/src/main.rs | 31 +++++++++ src/file.rs | 143 ++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 7 +- src/source.rs | 127 ----------------------------------- src/value.rs | 11 ++-- 8 files changed, 206 insertions(+), 136 deletions(-) create mode 100644 examples/basic-file/Cargo.toml create mode 100644 examples/basic-file/src/main.rs create mode 100644 examples/basic/Cargo.toml create mode 100644 examples/basic/src/main.rs create mode 100644 src/file.rs diff --git a/examples/basic-file/Cargo.toml b/examples/basic-file/Cargo.toml new file mode 100644 index 0000000..ffef864 --- /dev/null +++ b/examples/basic-file/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "basic-file" +version = "0.1.0" +authors = ["Ryan Leckey "] + +[dependencies] +config = { path = "../.." } diff --git a/examples/basic-file/src/main.rs b/examples/basic-file/src/main.rs new file mode 100644 index 0000000..ae394f9 --- /dev/null +++ b/examples/basic-file/src/main.rs @@ -0,0 +1,9 @@ +extern crate config; + +fn main() { + // Read configuration from $(cwd)/Cargo.toml + config::merge(config::File::with_name("Cargo")).unwrap(); + + println!("package.name = {:?}", config::get_str("package.name")); + println!("package.version = {:?}", config::get_str("package.version")); +} diff --git a/examples/basic/Cargo.toml b/examples/basic/Cargo.toml new file mode 100644 index 0000000..2cb273b --- /dev/null +++ b/examples/basic/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "basic" +version = "0.1.0" +authors = ["Ryan Leckey "] + +[dependencies] +config = { path = "../.." } diff --git a/examples/basic/src/main.rs b/examples/basic/src/main.rs new file mode 100644 index 0000000..a0e40bf --- /dev/null +++ b/examples/basic/src/main.rs @@ -0,0 +1,31 @@ +extern crate config; + +fn main() { + // Set defaults for `window.width` and `window.height` + config::set_default("window.title", "Basic"); + config::set_default("window.width", 640); + config::set_default("window.height", 480); + config::set_default("debug", true); + + // Note that you can retrieve the stored values as any type as long + // as there exists a reasonable conversion + println!("window.title : {:?}", config::get_str("window.title")); + println!("window.width : {:?}", config::get_str("window.width")); + println!("window.width : {:?}", config::get_int("window.width")); + println!("debug : {:?}", config::get_bool("debug")); + println!("debug : {:?}", config::get_str("debug")); + println!("debug : {:?}", config::get_int("debug")); + + // Attempting to get a value as a type that cannot be reasonably + // converted to will return None + println!("window.title : {:?}", config::get_bool("window.title")); + + // Instead of using a get_* function you can get the variant + // directly + println!("debug : {:?}", config::get("debug")); + println!("debug : {:?}", + config::get("debug").unwrap().as_int()); + + // Attempting to get a value that does not exist will return None + println!("not-found : {:?}", config::get("not-found")); +} diff --git a/src/file.rs b/src/file.rs new file mode 100644 index 0000000..2ffc836 --- /dev/null +++ b/src/file.rs @@ -0,0 +1,143 @@ +use std::fs; +use std::env; +use std::error::Error; +use std::io::Read; +use std::collections::HashMap; + +use toml; + +use value::Value; +use source::Source; + +#[derive(Default)] +pub struct File { + /// Basename of configuration file + name: String, + + /// Directory where configuration file is found + /// When not specified, the current working directory (CWD) is considered + path: Option, + + /// Namespace to restrict configuration from the file + namespace: Option, + + /// A required File will error if it cannot be found + required: bool, +} + +impl File { + pub fn with_name(name: &str) -> File { + File { + name: name.into(), + required: true, + + ..Default::default() + } + } + + pub fn path(&mut self, path: &str) -> &mut File { + self.path = Some(path.into()); + self + } + + pub fn namespace(&mut self, namespace: &str) -> &mut File { + self.namespace = Some(namespace.into()); + self + } + + pub fn required(&mut self, required: bool) -> &mut File { + self.required = required; + self + } +} + +fn toml_collect(content: &mut HashMap, + table: &toml::Table, + prefix: Option) { + for (key, value) in table { + // Construct full key from prefix + let key = if let Some(ref prefix) = prefix { + prefix.clone() + "." + key + } else { + key.clone() + }; + + match *value { + // Recurse into nested table + toml::Value::Table(ref table) => toml_collect(content, table, Some(key)), + + toml::Value::String(ref value) => { + content.insert(key, value.clone().into()); + } + + toml::Value::Integer(value) => { + content.insert(key, value.into()); + } + + toml::Value::Float(value) => { + content.insert(key, value.into()); + } + + toml::Value::Boolean(value) => { + content.insert(key, value.into()); + } + + _ => { + // Unhandled + } + } + } +} + +impl Source for File { + fn build(&mut self) -> Result, Box> { + let mut content = HashMap::new(); + + // Find file + // TODO: Use a nearest algorithm rather than strictly CWD + let cwd = match env::current_dir() { + Ok(cwd) => cwd, + Err(err) => { + if self.required { + return Err(From::from(err)); + } else { + return Ok(content); + } + } + }; + + let filename = cwd.join(self.name.clone() + ".toml"); + + // Read contents from file + let mut file = match fs::File::open(filename) { + Ok(file) => file, + Err(err) => { + if self.required { + return Err(From::from(err)); + } else { + return Ok(content); + } + } + }; + + let mut buffer = String::new(); + let res = file.read_to_string(&mut buffer); + if res.is_err() { + if self.required { + return Err(From::from(res.err().unwrap())); + } else { + return Ok(content); + } + } + + // Parse + let mut parser = toml::Parser::new(&buffer); + // TODO: Get a solution to make this return an Error-able + let document = parser.parse().unwrap(); + + // Iterate through document and fill content + toml_collect(&mut content, &document, None); + + Ok(content) + } +} diff --git a/src/lib.rs b/src/lib.rs index 0ae525b..426b526 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ extern crate toml; mod value; mod source; +mod file; mod config; use std::error::Error; @@ -12,7 +13,7 @@ use std::borrow::Cow; use std::sync::{Once, ONCE_INIT}; pub use source::Source; -pub use source::File; +pub use file::File; pub use value::Value; @@ -25,9 +26,7 @@ static CONFIG_INIT: Once = ONCE_INIT; // Get the global configuration instance fn global() -> &'static mut Config { unsafe { - CONFIG_INIT.call_once(|| { - CONFIG = Some(Default::default()); - }); + CONFIG_INIT.call_once(|| { CONFIG = Some(Default::default()); }); CONFIG.as_mut().unwrap() } diff --git a/src/source.rs b/src/source.rs index b608097..95785a6 100644 --- a/src/source.rs +++ b/src/source.rs @@ -1,135 +1,8 @@ -use std::fs; -use std::env; use std::error::Error; -use std::io::Read; use std::collections::HashMap; -use toml; - use value::Value; pub trait Source { fn build(&mut self) -> Result, Box>; } - -#[derive(Default)] -pub struct File { - // Basename of configuration file - name: String, - - // Namespace to restrict configuration from the file - namespace: Option, - - // A required File will error if it cannot be found - required: bool, -} - -impl File { - pub fn with_name(name: &str) -> File { - File { - name: name.into(), - required: true, - - ..Default::default() - } - } - - pub fn namespace(&mut self, namespace: &str) -> &mut File { - self.namespace = Some(namespace.into()); - self - } - - pub fn required(&mut self, required: bool) -> &mut File { - self.required = required; - self - } -} - -fn collect(content: &mut HashMap, table: &toml::Table, prefix: Option) { - for (key, value) in table { - // Construct full key from prefix - let key = if let Some(ref prefix) = prefix { - prefix.clone() + "." + key - } else { - key.clone() - }; - - match *value { - // Recurse into nested table - toml::Value::Table(ref table) => collect(content, table, Some(key)), - - toml::Value::String(ref value) => { - content.insert(key, value.clone().into()); - } - - toml::Value::Integer(value) => { - content.insert(key, value.into()); - } - - toml::Value::Float(value) => { - content.insert(key, value.into()); - } - - toml::Value::Boolean(value) => { - content.insert(key, value.into()); - } - - _ => { - // Unhandled - } - } - } -} - -impl Source for File { - fn build(&mut self) -> Result, Box> { - let mut content = HashMap::new(); - - // Find file - // TODO: Use a nearest algorithm rather than strictly CWD - let cwd = match env::current_dir() { - Ok(cwd) => cwd, - Err(err) => { - if self.required { - return Err(From::from(err)); - } else { - return Ok(content); - } - } - }; - - let filename = cwd.join(self.name.clone() + ".toml"); - - // Read contents from file - let mut file = match fs::File::open(filename) { - Ok(file) => file, - Err(err) => { - if self.required { - return Err(From::from(err)); - } else { - return Ok(content); - } - } - }; - - let mut buffer = String::new(); - let res = file.read_to_string(&mut buffer); - if res.is_err() { - if self.required { - return Err(From::from(res.err().unwrap())); - } else { - return Ok(content); - } - } - - // Parse - let mut parser = toml::Parser::new(&buffer); - // TODO: Get a solution to make this return an Error-able - let document = parser.parse().unwrap(); - - // Iterate through document and fill content - collect(&mut content, &document, None); - - Ok(content) - } -} diff --git a/src/value.rs b/src/value.rs index 192632b..52a2103 100644 --- a/src/value.rs +++ b/src/value.rs @@ -5,7 +5,7 @@ use std::borrow::Cow; /// /// Has an underlying or native type that comes from the configuration source /// but will be coerced into the requested type. -#[derive(Clone)] +#[derive(Debug, Clone)] pub enum Value { String(String), Integer(i64), @@ -35,10 +35,11 @@ impl Value { 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, - }) + match value.to_lowercase().as_ref() { + "1" | "true" | "on" | "yes" => Some(true), + "0" | "false" | "off" | "no" => Some(false), + _ => None, + } } else if let Value::Integer(value) = *self { Some(value != 0) } else if let Value::Float(value) = *self { -- cgit v1.2.3