summaryrefslogtreecommitdiffstats
path: root/src/file
diff options
context:
space:
mode:
authorRyan Leckey <leckey.ryan@gmail.com>2017-02-07 17:15:23 -0800
committerGitHub <noreply@github.com>2017-02-07 17:15:23 -0800
commit788a08756ab88b48c117257685ad7b93b8fab641 (patch)
treebf7eac8cabb2244bff0fa18f2c3c78c5c3daba35 /src/file
parent09b030a2b2b536d2af1c70bda560f6c0d6a36d41 (diff)
parentf82ca02c15b675ba889143aa7f7cd3c69e3f1810 (diff)
Merge pull request #6 from tmccombs/yaml
Add support for YAML
Diffstat (limited to 'src/file')
-rw-r--r--src/file/mod.rs13
-rw-r--r--src/file/yaml.rs85
2 files changed, 98 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"
+ }
+}