diff options
author | Ryan Leckey <leckey.ryan@gmail.com> | 2017-02-07 17:15:23 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-07 17:15:23 -0800 |
commit | 788a08756ab88b48c117257685ad7b93b8fab641 (patch) | |
tree | bf7eac8cabb2244bff0fa18f2c3c78c5c3daba35 /src | |
parent | 09b030a2b2b536d2af1c70bda560f6c0d6a36d41 (diff) | |
parent | f82ca02c15b675ba889143aa7f7cd3c69e3f1810 (diff) |
Merge pull request #6 from tmccombs/yaml
Add support for YAML
Diffstat (limited to 'src')
-rw-r--r-- | src/file/mod.rs | 13 | ||||
-rw-r--r-- | src/file/yaml.rs | 85 | ||||
-rw-r--r-- | src/lib.rs | 3 |
3 files changed, 101 insertions, 0 deletions
diff --git a/src/file/mod.rs b/src/file/mod.rs index 7e9d1a2..8f9578d 100644 --- a/src/file/mod.rs +++ b/src/file/mod.rs @@ -14,6 +14,9 @@ mod toml; #[cfg(feature = "json")] mod json; +#[cfg(feature = "yaml")] +mod yaml; + #[derive(Clone, Copy)] pub enum FileFormat { /// TOML (parsed with toml) @@ -23,6 +26,10 @@ pub enum FileFormat { /// JSON (parsed with serde_json) #[cfg(feature = "json")] Json, + + /// YAML (parsed with yaml_rust) + #[cfg(feature = "yaml")] + Yaml, } impl FileFormat { @@ -33,6 +40,9 @@ impl FileFormat { #[cfg(feature = "json")] FileFormat::Json => vec!["json"], + + #[cfg(feature = "yaml")] + FileFormat::Yaml => vec!["yaml", "yml"], } } @@ -44,6 +54,9 @@ impl FileFormat { #[cfg(feature = "json")] FileFormat::Json => json::Content::parse(text), + + #[cfg(feature = "yaml")] + FileFormat::Yaml => yaml::Content::parse(text), } } } diff --git a/src/file/yaml.rs b/src/file/yaml.rs new file mode 100644 index 0000000..1c3ccbb --- /dev/null +++ b/src/file/yaml.rs @@ -0,0 +1,85 @@ +use yaml_rust as yaml; + +use source::Source; +use std::error::Error; +use std::fmt; +use std::collections::{BTreeMap, HashMap}; +use std::mem; +use value::Value; + + +pub struct Content { + // Root table of the YAML document + root: yaml::Yaml +} + +impl Content { + pub fn parse(text: &str) -> Result<Box<Source>, Box<Error>> { + let mut docs = yaml::YamlLoader::load_from_str(text)?; + + match docs.len() { + 0 => Ok(Box::new(Content { root: yaml::Yaml::Hash(BTreeMap::new()) })), + 1 => Ok(Box::new(Content { + root: mem::replace(&mut docs[0], yaml::Yaml::Null) + })), + n => Err(Box::new(MultipleDocumentsError(n))) + } + } + + pub fn from_yaml(doc: yaml::Yaml) -> Content { + Content { root: doc } + } +} + +fn from_yaml_value<'a>(value: &yaml::Yaml) -> Value { + match *value { + yaml::Yaml::String(ref value) => Value::String(value.clone()), + yaml::Yaml::Real(ref value) => Value::Float(value.parse::<f64>().unwrap()), + yaml::Yaml::Integer(value) => Value::Integer(value), + yaml::Yaml::Boolean(value) => Value::Boolean(value), + yaml::Yaml::Hash(ref table) => { + let mut m = HashMap::new(); + for (key, value) in table { + if let Some(k) = key.as_str() { + m.insert(k.to_owned(), from_yaml_value(value)); + } + // TODO: should we do anything for non-string keys? + } + Value::Table(m) + } + yaml::Yaml::Array(ref array) => { + let l: Vec<Value> = array.iter().map(from_yaml_value).collect(); + Value::Array(l) + } + // TODO: how should we handle Null and BadValue? + _ => { unimplemented!(); } + + } +} + +impl Source for Content { + fn collect(&self) -> HashMap<String, Value> { + if let Value::Table(table) = from_yaml_value(&self.root) { + table + } else { + // TODO: Better handle a non-object at root + // NOTE: I never want to support that but a panic is bad + panic!("expected object at YAML root"); + } + } +} + +#[derive(Debug, Copy, Clone)] +struct MultipleDocumentsError(usize); + +impl fmt::Display for MultipleDocumentsError { + fn fmt(&self, format: &mut fmt::Formatter) -> fmt::Result { + write!(format, "Got {} YAML documents, expected 1", self.0) + } +} + +impl Error for MultipleDocumentsError { + fn description(&self) -> &str { + "More than one YAML document provided" + } +} @@ -50,6 +50,9 @@ extern crate toml; #[cfg(feature = "json")] extern crate serde_json; +#[cfg(feature = "yaml")] +extern crate yaml_rust; + mod value; mod source; mod file; |