diff options
author | Ryan Leckey <ryan@launchbadge.com> | 2017-06-13 17:36:41 -0700 |
---|---|---|
committer | Ryan Leckey <ryan@launchbadge.com> | 2017-06-13 17:36:45 -0700 |
commit | 3fdb2a3a19bc0248763d2bf15a152b8661534a82 (patch) | |
tree | ff5cf5cf263b1b65635ff875b4bb8d6ab18b5f9a /src/file | |
parent | 3c3d1e860eded0027f1efe3bb25e0fa6fa8b8940 (diff) |
Add YAML
Diffstat (limited to 'src/file')
-rw-r--r-- | src/file/format/mod.rs | 18 | ||||
-rw-r--r-- | src/file/format/yaml.rs | 93 |
2 files changed, 102 insertions, 9 deletions
diff --git a/src/file/format/mod.rs b/src/file/format/mod.rs index c9b1012..02cafe7 100644 --- a/src/file/format/mod.rs +++ b/src/file/format/mod.rs @@ -9,8 +9,8 @@ mod toml; #[cfg(feature = "json")] mod json; -// #[cfg(feature = "yaml")] -// mod yaml; +#[cfg(feature = "yaml")] +mod yaml; #[derive(Debug, Clone, Copy)] pub enum FileFormat { @@ -22,9 +22,9 @@ pub enum FileFormat { #[cfg(feature = "json")] Json, - // /// YAML (parsed with yaml_rust) - // #[cfg(feature = "yaml")] - // Yaml, + /// YAML (parsed with yaml_rust) + #[cfg(feature = "yaml")] + Yaml, } impl FileFormat { @@ -38,8 +38,8 @@ impl FileFormat { #[cfg(feature = "json")] FileFormat::Json => vec!["json"], - // #[cfg(feature = "yaml")] - // FileFormat::Yaml => vec!["yaml", "yml"], + #[cfg(feature = "yaml")] + FileFormat::Yaml => vec!["yaml", "yml"], } } @@ -54,8 +54,8 @@ impl FileFormat { #[cfg(feature = "json")] FileFormat::Json => json::parse(uri, text, namespace), - // #[cfg(feature = "yaml")] - // FileFormat::Yaml => yaml::Content::parse(text, namespace), + #[cfg(feature = "yaml")] + FileFormat::Yaml => yaml::parse(uri, text, namespace), } } } diff --git a/src/file/format/yaml.rs b/src/file/format/yaml.rs new file mode 100644 index 0000000..2840438 --- /dev/null +++ b/src/file/format/yaml.rs @@ -0,0 +1,93 @@ +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, ValueKind}; + +pub fn parse(uri: Option<&String>, text: &str, namespace: Option<&String>) -> Result<HashMap<String, Value>, Box<Error>> { + let mut docs = yaml::YamlLoader::load_from_str(text)?; + + // Designate root + let mut root = match docs.len() { + 0 => yaml::Yaml::Hash(BTreeMap::new()), + 1 => mem::replace(&mut docs[0], yaml::Yaml::Null), + n => { + return Err(Box::new(MultipleDocumentsError(n))); + } + }; + + // Limit to namespace + if let Some(namespace) = namespace { + root = yaml::Yaml::Hash(match root { + yaml::Yaml::Hash(ref mut table) => { + if let Some(yaml::Yaml::Hash(table)) = table.remove(&yaml::Yaml::String(namespace.clone())) { + table + } else { + BTreeMap::new() + } + } + + _ => { + BTreeMap::new() + } + }); + }; + + // TODO: Have a proper error fire if the root of a file is ever not a Table + let value = from_yaml_value(uri, &root); + match value.kind { + ValueKind::Table(map) => Ok(map), + + _ => Ok(HashMap::new()), + } +} + +fn from_yaml_value(uri: Option<&String>, value: &yaml::Yaml) -> Value { + match *value { + yaml::Yaml::String(ref value) => Value::new(uri, ValueKind::String(value.clone())), + yaml::Yaml::Real(ref value) => Value::new(uri, ValueKind::Float(value.parse::<f64>().unwrap())), + yaml::Yaml::Integer(value) => Value::new(uri, ValueKind::Integer(value)), + yaml::Yaml::Boolean(value) => Value::new(uri, ValueKind::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(uri, value)); + } + // TODO: should we do anything for non-string keys? + } + Value::new(uri, ValueKind::Table(m)) + } + yaml::Yaml::Array(ref array) => { + let mut l = Vec::new(); + + for value in array { + l.push(from_yaml_value(uri, value)); + } + + Value::new(uri, ValueKind::Array(l)) + } + // TODO: how should we handle Null and BadValue? + _ => { + unimplemented!(); + } + + } +} + +#[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" + } +} |