Auto merge of #57 - matthiasbeyer:add-yaml-header-parser, r=matthiasbeyer
Add yaml header parser I would like to add more tests.
This commit is contained in:
commit
19ef1dd25a
7 changed files with 269 additions and 3 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
@ -19,6 +19,7 @@ dependencies = [
|
||||||
"term_grid 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"term_grid 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"url 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
"url 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
"uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"yaml-rust 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -290,3 +291,8 @@ name = "yaml-rust"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yaml-rust"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
|
|
@ -29,4 +29,5 @@ itertools = "0.4.5"
|
||||||
hoedown = "3.0.3"
|
hoedown = "3.0.3"
|
||||||
ansi_term = "0.7.1"
|
ansi_term = "0.7.1"
|
||||||
rand = "0.3"
|
rand = "0.3"
|
||||||
|
yaml-rust = "0.3.0"
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ extern crate open;
|
||||||
extern crate itertools;
|
extern crate itertools;
|
||||||
extern crate ansi_term;
|
extern crate ansi_term;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
|
extern crate yaml_rust;
|
||||||
|
|
||||||
pub use cli::CliConfig;
|
pub use cli::CliConfig;
|
||||||
pub use configuration::Configuration;
|
pub use configuration::Configuration;
|
||||||
|
|
|
@ -12,7 +12,7 @@ use module::Module;
|
||||||
use runtime::Runtime;
|
use runtime::Runtime;
|
||||||
use storage::file::File;
|
use storage::file::File;
|
||||||
use storage::parser::Parser;
|
use storage::parser::Parser;
|
||||||
use storage::json::parser::JsonHeaderParser;
|
use storage::yaml::parser::YamlHeaderParser;
|
||||||
use module::helpers::cli::create_tag_filter;
|
use module::helpers::cli::create_tag_filter;
|
||||||
use module::helpers::cli::create_hash_filter;
|
use module::helpers::cli::create_hash_filter;
|
||||||
use module::helpers::cli::create_text_header_field_grep_filter;
|
use module::helpers::cli::create_text_header_field_grep_filter;
|
||||||
|
@ -21,7 +21,7 @@ use module::helpers::cli::CliFileFilter;
|
||||||
|
|
||||||
pub struct Notes<'a> {
|
pub struct Notes<'a> {
|
||||||
rt: &'a Runtime<'a>,
|
rt: &'a Runtime<'a>,
|
||||||
parser: Parser<JsonHeaderParser>,
|
parser: Parser<YamlHeaderParser>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Notes<'a> {
|
impl<'a> Notes<'a> {
|
||||||
|
@ -29,7 +29,7 @@ impl<'a> Notes<'a> {
|
||||||
pub fn new(rt: &'a Runtime<'a>) -> Notes<'a> {
|
pub fn new(rt: &'a Runtime<'a>) -> Notes<'a> {
|
||||||
Notes {
|
Notes {
|
||||||
rt: rt,
|
rt: rt,
|
||||||
parser: Parser::new(JsonHeaderParser::new(None)),
|
parser: Parser::new(YamlHeaderParser::new(None)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ pub mod path;
|
||||||
pub mod file;
|
pub mod file;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod json;
|
pub mod json;
|
||||||
|
pub mod yaml;
|
||||||
|
|
||||||
use module::Module;
|
use module::Module;
|
||||||
use storage::file::File;
|
use storage::file::File;
|
||||||
|
|
1
src/storage/yaml/mod.rs
Normal file
1
src/storage/yaml/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod parser;
|
256
src/storage/yaml/parser.rs
Normal file
256
src/storage/yaml/parser.rs
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use yaml_rust::Yaml;
|
||||||
|
|
||||||
|
use storage::parser::{FileHeaderParser, ParserError};
|
||||||
|
use storage::file::header::spec::FileHeaderSpec;
|
||||||
|
use storage::file::header::data::FileHeaderData;
|
||||||
|
|
||||||
|
pub struct YamlHeaderParser {
|
||||||
|
spec: Option<FileHeaderSpec>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl YamlHeaderParser {
|
||||||
|
|
||||||
|
pub fn new(spec: Option<FileHeaderSpec>) -> YamlHeaderParser {
|
||||||
|
YamlHeaderParser {
|
||||||
|
spec: spec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for YamlHeaderParser {
|
||||||
|
|
||||||
|
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
||||||
|
try!(write!(fmt, "YamlHeaderParser"));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for YamlHeaderParser {
|
||||||
|
|
||||||
|
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
||||||
|
try!(write!(fmt, "YamlHeaderParser, Spec: {:?}", self.spec));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileHeaderParser for YamlHeaderParser {
|
||||||
|
|
||||||
|
fn read(&self, string: Option<String>) -> Result<FileHeaderData, ParserError> {
|
||||||
|
use yaml_rust::YamlLoader;
|
||||||
|
if string.is_some() {
|
||||||
|
let s = string.unwrap();
|
||||||
|
YamlLoader::load_from_str(&s[..])
|
||||||
|
.map(|mut vec_yaml| {
|
||||||
|
vec_yaml.pop().map(|f| {
|
||||||
|
visit_yaml(f)
|
||||||
|
}).unwrap()
|
||||||
|
})
|
||||||
|
.map_err(|e| {
|
||||||
|
debug!("YAML parser error: {:?}", e);
|
||||||
|
ParserError::short(&s[..], s.clone(), 0)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(FileHeaderData::Null)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&self, data: &FileHeaderData) -> Result<String, ParserError> {
|
||||||
|
use yaml_rust::YamlEmitter;
|
||||||
|
|
||||||
|
let mut buffer = String::new();
|
||||||
|
let result = {
|
||||||
|
let mut emitter = YamlEmitter::new(&mut buffer);
|
||||||
|
emitter.dump(&visit_header(data))
|
||||||
|
};
|
||||||
|
result
|
||||||
|
.map_err(|e| {
|
||||||
|
error!("Error emitting YAML.");
|
||||||
|
debug!("YAML parser error: {:?}", e);
|
||||||
|
ParserError::short(&buffer[..], buffer.clone(), 0)
|
||||||
|
})
|
||||||
|
.map(|_| buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_yaml(v: Yaml) -> FileHeaderData {
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
match v {
|
||||||
|
Yaml::Real(_) => FileHeaderData::Float(v.as_f64().unwrap()),
|
||||||
|
Yaml::Integer(i) => {
|
||||||
|
if i > 0 {
|
||||||
|
debug!("Castring {} : i64 -> u64", i);
|
||||||
|
FileHeaderData::UInteger(i as u64)
|
||||||
|
} else {
|
||||||
|
FileHeaderData::Integer(i)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Yaml::String(s) => FileHeaderData::Text(s),
|
||||||
|
Yaml::Boolean(b) => FileHeaderData::Bool(b),
|
||||||
|
|
||||||
|
Yaml::Array(vec) => {
|
||||||
|
FileHeaderData::Array {
|
||||||
|
values: Box::new(vec.clone().into_iter().map(|i| visit_yaml(i)).collect())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Yaml::Hash(btree) => {
|
||||||
|
let btree = btree.clone();
|
||||||
|
FileHeaderData::Map{
|
||||||
|
keys: btree.into_iter().map(|(k, v)|
|
||||||
|
FileHeaderData::Key {
|
||||||
|
name: String::from(k.as_str().unwrap()),
|
||||||
|
value: Box::new(visit_yaml(v)),
|
||||||
|
}
|
||||||
|
).collect()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Yaml::Alias(_) => {
|
||||||
|
warn!("YAML::ALIAS is not yet fully supported by rust-yaml");
|
||||||
|
FileHeaderData::Null
|
||||||
|
},
|
||||||
|
|
||||||
|
Yaml::Null => FileHeaderData::Null,
|
||||||
|
|
||||||
|
Yaml::BadValue => {
|
||||||
|
warn!("YAML parsing error");
|
||||||
|
exit(1);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_header(h: &FileHeaderData) -> Yaml {
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
match h {
|
||||||
|
&FileHeaderData::Null => Yaml::Null,
|
||||||
|
&FileHeaderData::Float(f) => Yaml::Real(format!("{}", f)),
|
||||||
|
&FileHeaderData::Integer(i) => Yaml::Integer(i),
|
||||||
|
&FileHeaderData::UInteger(u) => {
|
||||||
|
debug!("Might be losing data now: u64 -> i64 cast");
|
||||||
|
Yaml::Integer(u as i64)
|
||||||
|
},
|
||||||
|
&FileHeaderData::Text(ref s) => Yaml::String(s.clone()),
|
||||||
|
&FileHeaderData::Bool(b) => Yaml::Boolean(b),
|
||||||
|
|
||||||
|
&FileHeaderData::Array{values: ref a} => {
|
||||||
|
Yaml::Array(a.deref().into_iter().map(|e| visit_header(e)).collect())
|
||||||
|
},
|
||||||
|
|
||||||
|
&FileHeaderData::Key{name: _, value: _} => {
|
||||||
|
error!("Something went terribly wrong when trying to emit YAML");
|
||||||
|
exit(1);
|
||||||
|
},
|
||||||
|
|
||||||
|
&FileHeaderData::Map{ref keys} => {
|
||||||
|
let mut map : BTreeMap<Yaml, Yaml> = BTreeMap::new();
|
||||||
|
|
||||||
|
let failed = keys.into_iter().map(|key| {
|
||||||
|
match key {
|
||||||
|
&FileHeaderData::Key{ref name, ref value} => {
|
||||||
|
let k = Yaml::String(name.clone());
|
||||||
|
let v = visit_header(value.deref());
|
||||||
|
|
||||||
|
map.insert(k, v).is_none()
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
error!("Something went terribly wrong when trying to emit YAML");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fold(0, |acc, succeeded : bool| {
|
||||||
|
if !succeeded { acc + 1 } else { acc }
|
||||||
|
});
|
||||||
|
|
||||||
|
debug!("Failed to insert {} keys", failed);
|
||||||
|
Yaml::Hash(map)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use super::YamlHeaderParser;
|
||||||
|
use storage::parser::FileHeaderParser;
|
||||||
|
use storage::file::header::data::FileHeaderData as FHD;
|
||||||
|
use storage::file::header::spec::FileHeaderSpec as FHS;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deserialization() {
|
||||||
|
let text = String::from("a: 1\nb: 2");
|
||||||
|
let spec = FHS::Array { allowed_types: vec![
|
||||||
|
FHS::Map {
|
||||||
|
keys: vec![
|
||||||
|
FHS::Key {
|
||||||
|
name: String::from("a"),
|
||||||
|
value_type: Box::new(FHS::UInteger)
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
let parser = YamlHeaderParser::new(Some(spec));
|
||||||
|
let parsed = parser.read(Some(text));
|
||||||
|
assert!(parsed.is_ok(), "Parsed is not ok: {:?}", parsed);
|
||||||
|
debug!("Parsed: {:?}", parsed);
|
||||||
|
|
||||||
|
match parsed.ok() {
|
||||||
|
Some(FHD::Map{ref keys}) => {
|
||||||
|
keys.into_iter().map(|k| {
|
||||||
|
match k {
|
||||||
|
&FHD::Key{ref name, ref value} => {
|
||||||
|
assert!(name == "a" || name == "b", "Key unknown");
|
||||||
|
match value.deref() {
|
||||||
|
&FHD::UInteger(u) => assert!(u == 1 || u == 2),
|
||||||
|
&FHD::Integer(_) => assert!(false, "Found Integer, expected UInteger"),
|
||||||
|
_ => assert!(false, "Integers are not here"),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
_ => assert!(false, "Key is not a Key"),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.all(|x| x == ());
|
||||||
|
},
|
||||||
|
_ => assert!(false, "Map is not a Map"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_desser() {
|
||||||
|
use yaml_rust::YamlLoader;
|
||||||
|
|
||||||
|
let text = String::from("a: [1, 32, 42]\nb: -2");
|
||||||
|
let parser = YamlHeaderParser::new(None);
|
||||||
|
|
||||||
|
let des = parser.read(Some(text.clone()));
|
||||||
|
assert!(des.is_ok(), "Deserializing failed");
|
||||||
|
|
||||||
|
let ser = parser.write(&des.unwrap());
|
||||||
|
assert!(ser.is_ok(), "Parser error when serializing deserialized text");
|
||||||
|
|
||||||
|
let yaml_text = YamlLoader::load_from_str(&text[..]);
|
||||||
|
let yaml_ser = YamlLoader::load_from_str(&ser.unwrap()[..]);
|
||||||
|
|
||||||
|
assert!(yaml_text.is_ok(), "Could not use yaml_rust to serialize text for comparison");
|
||||||
|
assert!(yaml_ser.is_ok(), "Could not use yaml_rust to serialize serialized-deserialized text for comparison");
|
||||||
|
assert_eq!(yaml_text.unwrap(), yaml_ser.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue