diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Cargo.toml | 24 | ||||
-rw-r--r-- | lib/src/config.rs | 100 | ||||
-rw-r--r-- | lib/src/de.rs | 107 | ||||
-rw-r--r-- | lib/src/error.rs | 155 | ||||
-rw-r--r-- | lib/src/file/format/mod.rs | 60 | ||||
-rw-r--r-- | lib/src/file/format/toml.rs | 64 | ||||
-rw-r--r-- | lib/src/file/mod.rs | 75 | ||||
-rw-r--r-- | lib/src/file/source/file.rs | 129 | ||||
-rw-r--r-- | lib/src/file/source/mod.rs | 12 | ||||
-rw-r--r-- | lib/src/file/source/string.rs | 21 | ||||
-rw-r--r-- | lib/src/lib.rs | 25 | ||||
-rw-r--r-- | lib/src/path/mod.rs | 43 | ||||
-rw-r--r-- | lib/src/path/parser.rs | 120 | ||||
-rw-r--r-- | lib/src/source.rs | 9 | ||||
-rw-r--r-- | lib/src/value.rs | 249 |
15 files changed, 0 insertions, 1193 deletions
diff --git a/lib/Cargo.toml b/lib/Cargo.toml deleted file mode 100644 index 6b04a8e..0000000 --- a/lib/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "config" -version = "0.5.0-pre" -description = "Layered configuration system for Rust applications." -homepage = "https://github.com/mehcode/config-rs" -repository = "https://github.com/mehcode/config-rs" -readme = "README.md" -keywords = ["config", "configuration", "settings", "env", "environment"] -authors = ["Ryan Leckey <leckey.ryan@gmail.com>"] -license = "MIT/Apache-2.0" -workspace = "../" - -[features] -default = ["toml", "json", "yaml"] -json = ["serde_json"] -yaml = ["yaml-rust"] - -[dependencies] -serde = "^0.9" -nom = "^2.1" - -toml = { version = "^0.3", optional = true } -serde_json = { version = "^0.9", optional = true } -yaml-rust = { version = "^0.3.5", optional = true } diff --git a/lib/src/config.rs b/lib/src/config.rs deleted file mode 100644 index 93d7fc0..0000000 --- a/lib/src/config.rs +++ /dev/null @@ -1,100 +0,0 @@ -use std::collections::HashMap; -use serde::de::Deserialize; - -use error::*; -use source::Source; -use value::Value; -use path; - -enum ConfigKind { - // A mutable configuration. This is the default. - Mutable { - defaults: HashMap<String, Value>, - overrides: HashMap<String, Value>, - sources: Vec<Box<Source + Send + Sync>>, - }, - - // A frozen configuration. - // Configuration can no longer be mutated. - Frozen, -} - -impl Default for ConfigKind { - fn default() -> Self { - ConfigKind::Mutable { - defaults: HashMap::new(), - overrides: HashMap::new(), - sources: Vec::new(), - } - } -} - -/// 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)] -pub struct Config { - kind: ConfigKind, - - /// Root of the cached configuration. - pub cache: Value, -} - -impl Config { - /// Merge in a configuration property source. - pub fn merge<T>(&mut self, source: T) -> Result<()> - where T: 'static, - T: Source + Send + Sync - { - match self.kind { - ConfigKind::Mutable { ref mut sources, .. } => { - sources.push(Box::new(source)); - } - - ConfigKind::Frozen => { - return Err(ConfigError::Frozen); - } - } - - self.refresh() - } - - /// Refresh the configuration cache with fresh - /// data from added sources. - /// - /// Configuration is automatically refreshed after a mutation - /// operation (`set`, `merge`, `set_default`, etc.). - pub fn refresh(&mut self) -> Result<()> { - self.cache = match self.kind { - // TODO: We need to actually merge in all the stuff - ConfigKind::Mutable { - ref overrides, - ref sources, - ref defaults, - } => sources[0].collect()?, - - ConfigKind::Frozen => { - return Err(ConfigError::Frozen); - } - }; - - Ok(()) - } - - pub fn get<T: Deserialize>(&self, key: &str) -> Result<T> { - // Parse the key into a path expression - let expr: path::Expression = key.to_lowercase().parse()?; - - // Traverse the cache using the path to (possibly) retrieve a value - let value = expr.get(&self.cache).cloned(); - - match value { - Some(value) => { - // Deserialize the received value into the requested type - T::deserialize(value) - } - - None => Err(ConfigError::NotFound(key.into())), - } - } -} diff --git a/lib/src/de.rs b/lib/src/de.rs deleted file mode 100644 index 89a3bcf..0000000 --- a/lib/src/de.rs +++ /dev/null @@ -1,107 +0,0 @@ -use serde::de; -use value::{Value, ValueKind}; -use error::*; -use std::borrow::Cow; -use std::iter::Peekable; -use std::collections::HashMap; -use std::collections::hash_map::Drain; - -impl de::Deserializer for Value { - type Error = ConfigError; - - #[inline] - fn deserialize<V>(self, visitor: V) -> Result<V::Value> - where V: de::Visitor - { - // Deserialize based on the underlying type - match self.kind { - ValueKind::Integer(i) => visitor.visit_i64(i), - ValueKind::Boolean(b) => visitor.visit_bool(b), - ValueKind::Float(f) => visitor.visit_f64(f), - ValueKind::String(s) => visitor.visit_string(s), - ValueKind::Array(values) => unimplemented!(), - ValueKind::Table(map) => visitor.visit_map(MapVisitor::new(map)), - _ => { - unimplemented!(); - } - } - } - - #[inline] - fn deserialize_option<V>(self, visitor: V) -> Result<V::Value> - where V: de::Visitor - { - // Match an explicit nil as None and everything else as Some - match self.kind { - ValueKind::Nil => visitor.visit_none(), - _ => visitor.visit_some(self), - } - } - - forward_to_deserialize! { - bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq - seq_fixed_size bytes byte_buf map struct unit enum newtype_struct - struct_field ignored_any unit_struct tuple_struct tuple - } -} - -struct StrDeserializer<'a>(&'a str); - -impl<'a> StrDeserializer<'a> { - fn new(key: &'a str) -> Self { - StrDeserializer(key) - } -} - -impl<'a> de::Deserializer for StrDeserializer<'a> { - type Error = ConfigError; - - #[inline] - fn deserialize<V: de::Visitor>(self, visitor: V) -> Result<V::Value> { - visitor.visit_str(self.0) - } - - forward_to_deserialize! { - bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq - seq_fixed_size bytes byte_buf map struct unit enum newtype_struct - struct_field ignored_any unit_struct tuple_struct tuple option - } -} - -struct MapVisitor { - elements: Vec<(String, Value)>, - index: usize, -} - -impl MapVisitor { - fn new(mut table: HashMap<String, Value>) -> Self { - MapVisitor { - elements: table.drain().collect(), - index: 0, - } - } -} - -impl de::MapVisitor for MapVisitor { - type Error = ConfigError; - - fn visit_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>> - where K: de::DeserializeSeed - { - if self.index >= self.elements.len() { - return Ok(None); - } - - let key_s = &self.elements[0].0; - let key_de = StrDeserializer(key_s); - let key = de::DeserializeSeed::deserialize(seed, key_de)?; - - Ok(Some(key)) - } - - fn visit_value_seed<V>(&mut self, seed: V) -> Result<V::Value> - where V: de::DeserializeSeed - { - de::DeserializeSeed::deserialize(seed, self.elements.remove(0).1) - } -} diff --git a/lib/src/error.rs b/lib/src/error.rs deleted file mode 100644 index b97ebac..0000000 --- a/lib/src/error.rs +++ /dev/null @@ -1,155 +0,0 @@ -use std::error::Error; -use std::borrow::Cow; -use std::result; -use std::fmt; -use serde::de; -use nom; - -#[derive(Debug)] -pub enum Unexpected { - Bool(bool), - Integer(i64), - Float(f64), - Str(String), - Unit, - Seq, - Map -} - -impl fmt::Display for Unexpected { - fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { - match *self { - Unexpected::Bool(b) => write!(f, "boolean `{}`", b), - Unexpected::Integer(i) => write!(f, "integer `{}`", i), - Unexpected::Float(v) => write!(f, "floating point `{}`", v), - Unexpected::Str(ref s) => write!(f, "string {:?}", s), - Unexpected::Unit => write!(f, "unit value"), - Unexpected::Seq => write!(f, "sequence"), - Unexpected::Map => write!(f, "map"), - } - } -} - -/// Represents all possible errors that can occur when working with -/// configuration. -pub enum ConfigError { - /// Configuration is frozen and no further mutations can be made. - Frozen, - - /// Configuration property was not found - NotFound(String), - - /// Configuration path could not be parsed. - PathParse(nom::ErrorKind), - - /// Configuration could not be parsed from file. - FileParse { uri: Option<String>, cause: Box<Error> }, - - /// Value could not be converted into the requested type. - Type { - origin: Option<String>, - unexpected: Unexpected, - expected: &'static str, - }, - - /// Custom message - Message(String), - - /// Unadorned error from a foreign source. - Foreign(Box<Error>), -} - -impl ConfigError { - // FIXME: pub(crate) - #[doc(hidden)] - pub fn invalid_type(origin: Option<String>, unexpected: Unexpected, expected: &'static str) -> Self { - ConfigError::Type { - origin: origin, - unexpected: unexpected, - expected: expected - } - } -} - -/// Alias for a `Result` with the error type set to `ConfigError`. -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 { - write!(f, "{}", *self) - } -} - -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::Message(ref s) => { - write!(f, "{}", s) - } - - 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 } => { - write!(f, "invalid type: {}, expected {}", - unexpected, expected)?; - - if let Some(ref origin) = *origin { - write!(f, " in {}", origin)?; - } - - Ok(()) - } - - ConfigError::FileParse { ref cause, ref uri } => { - write!(f, "{}", cause)?; - - if let Some(ref uri) = *uri { - write!(f, " in {}", uri)?; - } - - Ok(()) - } - } - } -} - -impl Error for ConfigError { - fn description(&self) -> &str { - match *self { - ConfigError::Frozen => "configuration is frozen", - ConfigError::NotFound(_) => "configuration property not found", - ConfigError::Type { .. } => "invalid type", - ConfigError::Foreign(ref cause) => cause.description(), - ConfigError::FileParse { ref cause, .. } => cause.description(), - ConfigError::PathParse(ref kind) => kind.description(), - - _ => "configuration error", - } - } - - fn cause(&self) -> Option<&Error> { - match *self { - ConfigError::Foreign(ref cause) => Some(cause.as_ref()), - ConfigError::FileParse { ref cause, .. } => Some(cause.as_ref()), - - _ => None - } - } -} - -impl de::Error for ConfigError { - fn custom<T: fmt::Display>(msg: T) -> Self { - ConfigError::Message(msg.to_string()) - } -} diff --git a/lib/src/file/format/mod.rs b/lib/src/file/format/mod.rs deleted file mode 100644 index 5c97a7f..0000000 --- a/lib/src/file/format/mod.rs +++ /dev/null @@ -1,60 +0,0 @@ -use source::Source; -use value::Value; -use std::error::Error; - -#[cfg(feature = "toml")] -mod toml; - -// #[cfg(feature = "json")] -// mod json; - -// #[cfg(feature = "yaml")] -// mod yaml; - -#[derive(Debug, Clone, Copy)] -pub enum FileFormat { - /// TOML (parsed with toml) - #[cfg(feature = "toml")] - Toml, - - // /// JSON (parsed with serde_json) - // #[cfg(feature = "json")] - // Json, - - // /// YAML (parsed with yaml_rust) - // #[cfg(feature = "yaml")] - // Yaml, -} - -impl FileFormat { - // TODO: pub(crate) - #[doc(hidden)] - pub fn extensions(&self) -> Vec<&'static str> { - match *self { - #[cfg(feature = "toml")] - FileFormat::Toml => vec!["toml"], - - // #[cfg(feature = "json")] - // FileFormat::Json => vec!["json"], - - // #[cfg(feature = "yaml")] - // FileFormat::Yaml => vec!["yaml", "yml"], - } - } - - // TODO: pub(crate) - #[doc(hidden)] - #[allow(unused_variables)] - pub fn parse(&self, uri: Option<&String>, text: &str, namespace: Option<&String>) -> Result<Value, Box<Error>> { - match *self { - #[cfg(feature = "toml")] - FileFormat::Toml => toml::parse(uri, text, namespace), - - // #[cfg(feature = "json")] - // FileFormat::Json => json::Content::parse(text, namespace), - - // #[cfg(feature = "yaml")] - // FileFormat::Yaml => yaml::Content::parse(text, namespace), - } - } -} diff --git a/lib/src/file/format/toml.rs b/lib/src/file/format/toml.rs deleted file mode 100644 index bbe6aa6..0000000 --- a/lib/src/file/format/toml.rs +++ /dev/null @@ -1,64 +0,0 @@ -use toml; -use source::Source; -use std::collections::{HashMap, BTreeMap}; -use std::error::Error; -use value::Value; - -pub fn parse(uri: Option<&String>, text: &str, namespace: Option<&String>) -> Result<Value, Box<Error>> { - // Parse a TOML value from the provided text - let mut root: toml::Value = toml::from_str(text)?; - - // Limit to namespace - if let Some(namespace) = namespace { - root = toml::Value::Table(match root { - toml::Value::Table(ref mut table) => { - if let Some(toml::Value::Table(table)) = table.remove(namespace) { - table - } else { - BTreeMap::new() - } - } - - _ => { - BTreeMap::new() - } - }); - } - - Ok(from_toml_value(uri, &root)) -} - -// TODO: Extend value origin with line/column numbers when able -fn from_toml_value(uri: Option<&String>, value: &toml::Value) -> Value { - match *value { - toml::Value::String(ref value) => Value::new(uri, value.to_string()), - toml::Value::Float(value) => Value::new(uri, value), - toml::Value::Integer(value) => Value::new(uri, value), - toml::Value::Boolean(value) => Value::new(uri, value), - - toml::Value::Table(ref table) => { - let mut m = HashMap::new(); - - for (key, value) in table { - m.insert(key.clone(), from_toml_value(uri, value)); - } - - Value::new(uri, m) - } - - toml::Value::Array(ref array) => { - let mut l = Vec::new(); - - for value in array { - l.push(from_toml_value(uri, value)); - } - - Value::new(uri, l) - } - - _ => { - // TODO: DateTime - unimplemented!(); - } - } -} diff --git a/lib/src/file/mod.rs b/lib/src/file/mod.rs deleted file mode 100644 index 7534ddb..0000000 --- a/lib/src/file/mod.rs +++ /dev/null @@ -1,75 +0,0 @@ -mod format; -pub mod source; - -use source::Source; -use error::*; -use value::Value; - -use self::source::FileSource; -pub use self::format::FileFormat; - -pub struct File<T> - where T: FileSource -{ - source: T, - - /// Namespace to restrict configuration from the file - namespace: Option<String>, - - /// Format of file (which dictates what driver to use). - format: Option<FileFormat>, - - /// A required File will error if it cannot be found - required: bool, -} - -impl File<source::string::FileSourceString> { - pub fn from_str(s: &str, format: FileFormat) -> Self { - File { - format: Some(format), - required: true, - namespace: None, - source: s.into(), - } - } -} - -impl File<source::file::FileSourceFile> { - pub fn new(name: &str, format: FileFormat) -> Self { - File { - format: Some(format), - required: true, - namespace: None, - source: source::file::FileSourceFile::new(name), - } - } -} - -impl<T: FileSource> File<T> { - pub fn required(&mut self, required: bool) -> &mut Self { - self.required = required; - self - } - - pub fn namespace(&mut self, namespace: &str) -> &mut Self { - self.namespace = Some(namespace.into()); - self - } -} - -impl<T: FileSource> Source for File<T> { - fn collect(&self) -> Result<Value> { - // Coerce the file contents to a string - let (uri, contents) = self.source.resolve(self.format).map_err(|err| { - ConfigError::Foreign(err) - })?; - - // Parse the string using the given format - self.format.unwrap().parse(uri.as_ref(), &contents, self.namespace.as_ref()).map_err(|cause| { - ConfigError::FileParse { - uri: uri, - cause: cause - } - }) - } -} diff --git a/lib/src/file/source/file.rs b/lib/src/file/source/file.rs deleted file mode 100644 index 124b7dd..0000000 --- a/lib/src/file/source/file.rs +++ /dev/null @@ -1,129 +0,0 @@ -use std::str::FromStr; -use std::result; -use std::error::Error; - -use std::path::{PathBuf, Path}; -use std::io::{self, Read}; -use std::fs; -use std::env; - -use source::Source; -use super::{FileFormat, FileSource}; - -/// Describes a file sourced from a file -pub struct FileSourceFile { - /// Basename of configuration file - name: String, - - /// Directory where configuration file is found - /// When not specified, the current working directory (CWD) is considered - path: Option<String>, -} - -impl FileSourceFile { - pub fn new(name: &str) -> FileSourceFile { - FileSourceFile { - name: name.into(), - path: None, - } - } - - fn find_file(&self, format_hint: Option<FileFormat>) -> Result<PathBuf, Box<Error>> { - // Build expected configuration file - let mut basename = PathBuf::new(); - let extensions = format_hint.unwrap().extensions(); - - if let Some(ref path) = self.path { - basename.push(path.clone()); - } - - basename.push(self.name.clone()); - - // Find configuration file (algorithm similar to .git detection by git) - let mut dir = env::current_dir()?; - let mut filename = dir.as_path().join(basename.clone()); - - loop { - for ext in &extensions { - filename.set_extension(ext); - - if filename.is_file() { - // File exists and is a file - return Ok(filename); - } - } - - // Not found.. travse up via the dir - if !dir.pop() { - // Failed to find the configuration file - return Err(Box::new(io::Error::new(io::ErrorKind::NotFound, - format!("configuration file \"{}\" not found", - basename.to_string_lossy())) - )); - } - } - } -} - -impl FileSource for FileSourceFile { - fn resolve(&self, format_hint: Option<FileFormat>) -> Result<(Option<String>, String), Box<Error>> { - // Find file - let filename = self.find_file(format_hint)?; - - // Attempt to use a relative path for the URI - let base = env::current_dir()?; - let uri = match path_relative_from(&filename, &base) { - Some(value) => value, - None => filename.clone(), - }; - - // Read contents from file - let mut file = fs::File::open(filename.clone())?; - let mut text = String::new(); - file.read_to_string(&mut text)?; - - Ok((Some(uri.to_string_lossy().into_owned()), text)) - } -} - -// TODO: This should probably be a crate -// https://github.com/rust-lang/rust/blob/master/src/librustc_trans/back/rpath.rs#L128 -fn path_relative_from(path: &Path, base: &Path) -> Option<PathBuf> { - use std::path::Component; - - if path.is_absolute() != base.is_absolute() { - if path.is_absolute() { - Some(PathBuf::from(path)) - } else { - None - } - } else { - let mut ita = path.components(); - let mut itb = base.components(); - let mut comps: Vec<Component> = vec![]; - loop { - match (ita.next(), itb.next()) { - (None, None) => break, - (Some(a), None) => { - comps.push(a); - comps.extend(ita.by_ref()); - break; - } - (None, _) => comps.push(Component::ParentDir), - (Some(a), Some(b)) if comps.is_empty() && a == b => (), - (Some(a), Some(b)) if b == Component::CurDir => comps.push(a), - (Some(_), Some(b)) if b == Component::ParentDir => return None, - (Some(a), Some(_)) => { - comps.push(Component::ParentDir); - for _ in itb { - comps.push(Component::ParentDir); - } - comps.push(a); - comps.extend(ita.by_ref()); - break; - } - } - } - Some(comps.iter().map(|c| c.as_os_str()).collect()) - } -} diff --git a/lib/src/file/source/mod.rs b/lib/src/file/source/mod.rs deleted file mode 100644 index 4aeafa5..0000000 --- a/lib/src/file/source/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub mod file; -pub mod string; - -use std::error::Error; - -use source::Source; -use super::FileFormat; - -/// Describes where the file is sourced -pub trait FileSource { - fn resolve(&self, format_hint: Option<FileFormat>) -> Result<(Option<String>, String), Box<Error>>; -} diff --git a/lib/src/file/source/string.rs b/lib/src/file/source/string.rs deleted file mode 100644 index e1d9f64..0000000 --- a/lib/src/file/source/string.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::str::FromStr; -use std::result; -use std::error::Error; - -use source::Source; -use super::{FileSource, FileFormat}; - -/// Describes a file sourced from a string -pub struct FileSourceString(String); - -impl<'a> From<&'a str> for FileSourceString { - fn from(s: &'a str) -> Self { - FileSourceString(s.into()) - } -} - -impl FileSource for FileSourceString { - fn resolve(&self, _: Option<FileFormat>) -> Result<(Option<String>, String), Box<Error>> { - Ok((None, self.0.clone())) - } -} diff --git a/lib/src/lib.rs b/lib/src/lib.rs deleted file mode 100644 index 212e621..0000000 --- a/lib/src/lib.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![allow(dead_code)] -#![allow(unused_imports)] -#![allow(unused_variables)] - -#[macro_use] -extern crate serde; - -extern crate nom; - -#[cfg(feature = "toml")] -extern crate toml; - -mod error; -mod value; -mod de; -mod path; -mod source; -mod config; -mod file; - -pub use config::Config; -pub use error::ConfigError; -pub use value::Value; -pub use source::Source; -pub use file::{File, FileFormat}; diff --git a/lib/src/path/mod.rs b/lib/src/path/mod.rs deleted file mode 100644 index 46e2290..0000000 --- a/lib/src/path/mod.rs +++ /dev/null @@ -1,43 +0,0 @@ -use std::str::FromStr; -use nom::ErrorKind; -use error::*; -use value::{Value, ValueKind}; - -mod parser; - -#[derive(Debug, Eq, PartialEq, Clone, Hash)] -pub enum Expression { - Identifier(String), - Child(Box<Expression>, String), - Subscript(Box<Expression>, i32), -} - -impl FromStr for Expression { - type Err = ConfigError; - - fn from_str(s: &str) -> Result<Expression> { - parser::from_str(s.as_bytes()).to_result().map_err(|kind| { - ConfigError::PathParse(kind) - }) - } -} - -impl Expression { - pub fn get<'a>(self, root: &'a Value) -> Option<&'a Value> { - match self { - Expression::Identifier(id) => { - match root.kind { - // `x` access on a table is equivalent to: map[x] - ValueKind::Table(ref map) => map.get(&id), - - // all other variants return None - _ => None, - } - } - - _ => { - unimplemented!(); - } - } - } -} diff --git a/lib/src/path/parser.rs b/lib/src/path/parser.rs deleted file mode 100644 index ad7ab91..0000000 --- a/lib/src/path/parser.rs +++ /dev/null @@ -1,120 +0,0 @@ -use nom::*; -use std::str::{FromStr, from_utf8}; -use super::Expression; - -named!(ident_<String>, - map!( - map_res!(is_a!( - "abcdefghijklmnopqrstuvwxyz \ - ABCDEFGHIJKLMNOPQRSTUVWXYZ \ - 0123456789 \ - _-" - ), from_utf8), - |s: &str| { - s.to_string() - } - ) -); - -named!(integer <i32>, - map_res!( - map_res!( - ws!(digit), - from_utf8 - ), - FromStr::from_str - ) -); - -named!(ident<Expression>, map!(ident_, Expression::Identifier)); - -fn postfix(expr: Expression) -> Box<Fn(&[u8]) -> IResult<&[u8], Expression>> { - return Box::new(move |i: &[u8]| { - alt!(i, - do_parse!( - tag!(".") >> - id: ident_ >> - (Expression::Child(Box::new(expr.clone()), id)) - ) | - delimited!( - char!('['), - do_parse!( - negative: opt!(tag!("-")) >> - num: integer >> - (Expression::Subscript( - Box::new(expr.clone()), - num * (if negative.is_none() { 1 } else { -1 }) - )) - ), - char!(']') - ) - ) - }); -} - -pub fn from_str(input: &[u8]) -> IResult<&[u8], Expression> { - match ident(input) { - IResult::Done(mut rem, mut expr) => { - while rem.len() > 0 { - match postfix(expr)(rem) { - IResult::Done(rem_, expr_) => { - rem = rem_; - expr = expr_; - } - - // Forward Incomplete and Error - result @ _ => { - return result; - } - } - } - - IResult::Done(&[], expr) - } - - // Forward Incomplete and Error - result @ _ => result, - } -} - -#[cfg(test)] -mod test { - use super::*; - use super::Expression::*; - - #[test] - fn test_id() { - let parsed: Expression = from_str("abcd").unwrap(); - assert_eq!(parsed, Identifier("abcd".into())); - } - - #[test] - fn test_id_dash() { - let parsed: Expression = from_str("abcd-efgh").unwrap(); - assert_eq!(parsed, Identifier("abcd-efgh".into())); - } - - #[test] - fn test_child() { - let parsed: Expression = from_str("abcd.efgh").unwrap(); - let expected = Child(Box::new(Identifier("abcd".into())), "efgh".into()); - - assert_eq!(parsed, expected); - } - - #[test] - fn test_subscript() { - let parsed: Expression = from_str("abcd[12]").unwrap(); - let expected = Subscript(Box::new(Identifier("abcd".into())), 12); - - assert_eq!(parsed, expected); - } - - #[test] - fn test_subscript_neg() { - let parsed: Expression = from_str("abcd[-1]").unwrap(); - let expected = Subscript(Box::new(Identifier("abcd".into())), -1); - - assert_eq!(parsed, expected); - } -} diff --git a/lib/src/source.rs b/lib/src/source.rs deleted file mode 100644 index 7519438..0000000 --- a/lib/src/source.rs +++ /dev/null @@ -1,9 +0,0 @@ -use error::*; -use value::Value; - -/// Describes a generic _source_ of configuration properties. -pub trait Source { - /// Collect all configuration properties available from this source and return - /// a top-level Value (which we expected to be a Table). - fn collect(&self) -> Result<Value>; -} diff --git a/lib/src/value.rs b/lib/src/value.rs deleted file mode 100644 index 5b0014a..0000000 --- a/lib/src/value.rs +++ /dev/null @@ -1,249 +0,0 @@ -use std::collections::HashMap; -use std::fmt::Display; -use error::*; - -/// Underlying kind of the configuration value. -#[derive(Debug, Clone)] -pub enum ValueKind { - Nil, - Boolean(bool), - Integer(i64), - Float(f64), - String(String), - Table(Table), - Array(Array), -} - -pub type Array = Vec<Value>; -pub type Table = HashMap<String, Value>; - -impl Default for ValueKind { - fn default() -> Self { - ValueKind::Nil - } -} - -impl<T> From<Option<T>> for ValueKind |