saphyr-serde/saphyr/src/yaml.rs

422 lines
12 KiB
Rust
Raw Normal View History

2015-05-24 18:16:28 +00:00
use std::collections::BTreeMap;
use std::ops::Index;
2015-05-24 06:27:42 +00:00
use std::string;
2015-05-24 19:29:52 +00:00
use std::str::FromStr;
2015-05-26 18:50:51 +00:00
use std::mem;
use parser::*;
2015-05-30 14:39:50 +00:00
use scanner::{TScalarStyle, ScanError, TokenType};
2015-05-24 06:27:42 +00:00
2015-05-31 09:59:43 +00:00
/// An YAML node is store as this `Yaml` enumeration, it provides an easy way to
/// access your YAML document.
2015-06-29 16:31:22 +00:00
///
2015-05-31 09:59:43 +00:00
/// # Examples
2015-06-29 16:31:22 +00:00
///
2015-05-31 09:59:43 +00:00
/// ```
/// use yaml_rust::Yaml;
/// let foo = Yaml::from_str("-123"); // convert the string to the appropriate YAML type
/// assert_eq!(foo.as_i64().unwrap(), -123);
2015-06-29 16:31:22 +00:00
///
2015-05-31 09:59:43 +00:00
/// // iterator over an Array
/// let vec = Yaml::Array(vec![Yaml::Integer(1), Yaml::Integer(2)]);
/// for v in vec.as_vec().unwrap() {
/// assert!(v.as_i64().is_some());
/// }
/// ```
2015-05-24 17:34:18 +00:00
#[derive(Clone, PartialEq, PartialOrd, Debug, Eq, Ord)]
2015-05-24 06:27:42 +00:00
pub enum Yaml {
2015-05-30 14:39:50 +00:00
/// float types are stored as String, and parsed on demand.
/// Note that f64 does NOT implement Eq trait and can NOT be stored in BTreeMap
Real(string::String),
2015-05-31 09:59:43 +00:00
/// Yaml int is stored as i64.
2015-05-30 14:39:50 +00:00
Integer(i64),
2015-05-31 09:59:43 +00:00
/// Yaml scalar.
2015-05-24 06:27:42 +00:00
String(string::String),
2015-05-31 09:59:43 +00:00
/// Yaml bool, e.g. `true` or `false`.
2015-05-24 06:27:42 +00:00
Boolean(bool),
2015-05-31 09:59:43 +00:00
/// Yaml array, can be access as a `Vec`.
2015-05-24 06:27:42 +00:00
Array(self::Array),
2015-05-31 09:59:43 +00:00
/// Yaml hash, can be access as a `BTreeMap`.
2015-05-24 06:27:42 +00:00
Hash(self::Hash),
2015-05-31 09:59:43 +00:00
/// Alias, not fully supported yet.
2015-05-28 17:56:03 +00:00
Alias(usize),
2015-05-31 09:59:43 +00:00
/// Yaml bool, e.g. `null` or `~`.
2015-05-24 06:27:42 +00:00
Null,
2015-05-31 09:59:43 +00:00
/// Access non-exist node by Index trait will return `BadValue`.
/// This simplifies error handling of user. Invalid type conversion
/// also return `BadValue`.
2015-05-24 18:16:28 +00:00
BadValue,
2015-05-24 06:27:42 +00:00
}
pub type Array = Vec<Yaml>;
2015-05-24 17:34:18 +00:00
pub type Hash = BTreeMap<Yaml, Yaml>;
2015-05-24 06:27:42 +00:00
2015-05-26 18:50:51 +00:00
pub struct YamlLoader {
docs: Vec<Yaml>,
// states
2015-12-16 07:10:02 +00:00
// (current node, anchor_id) tuple
doc_stack: Vec<(Yaml, usize)>,
2015-05-26 18:50:51 +00:00
key_stack: Vec<Yaml>,
2015-12-16 07:10:02 +00:00
anchor_map: BTreeMap<usize, Yaml>,
2015-05-26 18:50:51 +00:00
}
impl EventReceiver for YamlLoader {
fn on_event(&mut self, ev: &Event) {
2015-05-30 14:39:50 +00:00
// println!("EV {:?}", ev);
2015-05-26 18:50:51 +00:00
match *ev {
Event::DocumentStart => {
// do nothing
},
Event::DocumentEnd => {
match self.doc_stack.len() {
// empty document
0 => self.docs.push(Yaml::BadValue),
2015-12-16 07:10:02 +00:00
1 => self.docs.push(self.doc_stack.pop().unwrap().0),
2015-05-26 18:50:51 +00:00
_ => unreachable!()
}
},
2015-12-16 07:10:02 +00:00
Event::SequenceStart(aid) => {
self.doc_stack.push((Yaml::Array(Vec::new()), aid));
2015-05-26 18:50:51 +00:00
},
Event::SequenceEnd => {
let node = self.doc_stack.pop().unwrap();
self.insert_new_node(node);
},
2015-12-16 07:10:02 +00:00
Event::MappingStart(aid) => {
self.doc_stack.push((Yaml::Hash(Hash::new()), aid));
2015-05-26 18:50:51 +00:00
self.key_stack.push(Yaml::BadValue);
},
Event::MappingEnd => {
self.key_stack.pop().unwrap();
let node = self.doc_stack.pop().unwrap();
self.insert_new_node(node);
},
2015-12-16 07:10:02 +00:00
Event::Scalar(ref v, style, aid, ref tag) => {
2015-05-26 18:50:51 +00:00
let node = if style != TScalarStyle::Plain {
Yaml::String(v.clone())
} else {
2015-05-30 14:39:50 +00:00
match tag {
&Some(TokenType::TagToken(ref handle, ref suffix)) => {
// XXX tag:yaml.org,2002:
if handle == "!!" {
match suffix.as_ref() {
"bool" => {
// "true" or "false"
match v.parse::<bool>() {
Err(_) => Yaml::BadValue,
Ok(v) => Yaml::Boolean(v)
}
},
"int" => {
match v.parse::<i64>() {
Err(_) => Yaml::BadValue,
Ok(v) => Yaml::Integer(v)
}
},
"float" => {
match v.parse::<f64>() {
Err(_) => Yaml::BadValue,
Ok(_) => Yaml::Real(v.clone())
}
},
"null" => {
match v.as_ref() {
"~" | "null" => Yaml::Null,
_ => Yaml::BadValue,
}
}
_ => Yaml::String(v.clone()),
}
} else {
Yaml::String(v.clone())
}
},
// Datatype is not specified, or unrecognized
_ => { Yaml::from_str(v.as_ref()) }
2015-05-26 18:50:51 +00:00
}
};
2015-12-16 07:10:02 +00:00
self.insert_new_node((node, aid));
2015-05-26 18:50:51 +00:00
},
2015-05-28 17:56:03 +00:00
Event::Alias(id) => {
2015-12-16 07:10:02 +00:00
let n = match self.anchor_map.get(&id) {
Some(v) => v.clone(),
None => Yaml::BadValue,
};
self.insert_new_node((n, 0));
2015-05-28 17:56:03 +00:00
}
2015-05-26 18:50:51 +00:00
_ => { /* ignore */ }
}
// println!("DOC {:?}", self.doc_stack);
}
}
impl YamlLoader {
2015-12-16 07:10:02 +00:00
fn insert_new_node(&mut self, node: (Yaml, usize)) {
// valid anchor id starts from 1
if node.1 > 0 {
self.anchor_map.insert(node.1, node.0.clone());
}
2015-05-26 18:50:51 +00:00
if !self.doc_stack.is_empty() {
let parent = self.doc_stack.last_mut().unwrap();
match *parent {
2015-12-16 07:10:02 +00:00
(Yaml::Array(ref mut v), _) => v.push(node.0),
(Yaml::Hash(ref mut h), _) => {
2015-05-26 18:50:51 +00:00
let mut cur_key = self.key_stack.last_mut().unwrap();
// current node is a key
if cur_key.is_badvalue() {
2015-12-16 07:10:02 +00:00
*cur_key = node.0;
2015-05-26 18:50:51 +00:00
// current node is a value
} else {
let mut newkey = Yaml::BadValue;
mem::swap(&mut newkey, cur_key);
2015-12-16 07:10:02 +00:00
h.insert(newkey, node.0);
2015-05-26 18:50:51 +00:00
}
},
_ => unreachable!(),
}
} else {
self.doc_stack.push(node);
}
}
pub fn load_from_str(source: &str) -> Result<Vec<Yaml>, ScanError>{
let mut loader = YamlLoader {
docs: Vec::new(),
doc_stack: Vec::new(),
key_stack: Vec::new(),
2015-12-16 07:10:02 +00:00
anchor_map: BTreeMap::new(),
2015-05-26 18:50:51 +00:00
};
let mut parser = Parser::new(source.chars());
try!(parser.load(&mut loader, true));
Ok(loader.docs)
}
}
2015-05-24 17:34:18 +00:00
macro_rules! define_as (
($name:ident, $t:ident, $yt:ident) => (
pub fn $name(&self) -> Option<$t> {
match *self {
Yaml::$yt(v) => Some(v),
_ => None
}
}
);
);
macro_rules! define_as_ref (
($name:ident, $t:ty, $yt:ident) => (
pub fn $name(&self) -> Option<$t> {
match *self {
Yaml::$yt(ref v) => Some(v),
_ => None
}
}
);
);
impl Yaml {
define_as!(as_bool, bool, Boolean);
2015-05-30 14:39:50 +00:00
define_as!(as_i64, i64, Integer);
2015-05-24 17:34:18 +00:00
define_as_ref!(as_str, &str, String);
define_as_ref!(as_hash, &Hash, Hash);
define_as_ref!(as_vec, &Array, Array);
pub fn is_null(&self) -> bool {
match *self {
Yaml::Null => true,
_ => false
}
}
2015-05-24 18:16:28 +00:00
pub fn is_badvalue(&self) -> bool {
match *self {
Yaml::BadValue => true,
_ => false
}
}
2015-05-30 14:39:50 +00:00
pub fn as_f64(&self) -> Option<f64> {
2015-05-24 17:34:18 +00:00
match *self {
2015-05-30 14:39:50 +00:00
Yaml::Real(ref v) => {
v.parse::<f64>().ok()
2015-05-24 17:34:18 +00:00
},
_ => None
}
}
2015-05-24 18:16:28 +00:00
2015-05-30 14:39:50 +00:00
pub fn from_str(v: &str) -> Yaml {
match v {
"~" | "null" => Yaml::Null,
"true" => Yaml::Boolean(true),
"false" => Yaml::Boolean(false),
_ if v.parse::<i64>().is_ok() => Yaml::Integer(v.parse::<i64>().unwrap()),
// try parsing as f64
_ if v.parse::<f64>().is_ok() => Yaml::Real(v.to_string()),
_ => Yaml::String(v.to_string())
}
2015-05-24 18:16:28 +00:00
}
2015-05-24 17:34:18 +00:00
}
2015-05-24 18:16:28 +00:00
static BAD_VALUE: Yaml = Yaml::BadValue;
impl<'a> Index<&'a str> for Yaml {
type Output = Yaml;
fn index(&self, idx: &'a str) -> &Yaml {
let key = Yaml::String(idx.to_string());
match self.as_hash() {
Some(h) => h.get(&key).unwrap_or(&BAD_VALUE),
None => &BAD_VALUE
}
}
}
impl Index<usize> for Yaml {
type Output = Yaml;
fn index(&self, idx: usize) -> &Yaml {
match self.as_vec() {
Some(v) => v.get(idx).unwrap_or(&BAD_VALUE),
None => &BAD_VALUE
}
}
}
2015-05-24 17:34:18 +00:00
#[cfg(test)]
mod test {
2015-05-26 18:50:51 +00:00
use yaml::*;
2015-05-24 18:16:28 +00:00
#[test]
2015-05-24 17:34:18 +00:00
fn test_coerce() {
let s = "---
a: 1
b: 2.2
c: [1, 2]
";
2015-05-26 18:50:51 +00:00
let out = YamlLoader::load_from_str(&s).unwrap();
let doc = &out[0];
2015-05-30 14:39:50 +00:00
assert_eq!(doc["a"].as_i64().unwrap(), 1i64);
assert_eq!(doc["b"].as_f64().unwrap(), 2.2f64);
assert_eq!(doc["c"][1].as_i64().unwrap(), 2i64);
2015-05-26 18:50:51 +00:00
assert!(doc["d"][0].is_badvalue());
}
#[test]
fn test_parser() {
let s: String = "
# comment
a0 bb: val
a1:
b1: 4
b2: d
a2: 4 # i'm comment
a3: [1, 2, 3]
a4:
- - a1
- a2
- 2
a5: 'single_quoted'
a6: \"double_quoted\"
a7:
".to_string();
let out = YamlLoader::load_from_str(&s).unwrap();
let doc = &out[0];
assert_eq!(doc["a7"].as_str().unwrap(), "你好");
}
#[test]
fn test_multi_doc() {
2015-06-29 16:31:22 +00:00
let s =
2015-05-26 18:50:51 +00:00
"
'a scalar'
---
'a scalar'
---
'a scalar'
";
let out = YamlLoader::load_from_str(&s).unwrap();
assert_eq!(out.len(), 3);
2015-05-24 17:34:18 +00:00
}
2015-05-26 18:50:51 +00:00
2015-12-16 07:10:02 +00:00
#[test]
fn test_anchor() {
let s =
"
a1: &DEFAULT
b1: 4
b2: d
a2: *DEFAULT
";
let out = YamlLoader::load_from_str(&s).unwrap();
let doc = &out[0];
assert_eq!(doc["a2"]["b1"].as_i64().unwrap(), 4);
}
#[test]
fn test_bad_anchor() {
let s =
"
a1: &DEFAULT
b1: 4
b2: *DEFAULT
";
let out = YamlLoader::load_from_str(&s).unwrap();
let doc = &out[0];
assert_eq!(doc["a1"]["b2"], Yaml::BadValue);
}
2015-05-30 14:39:50 +00:00
#[test]
fn test_plain_datatype() {
let s =
"
- 'string'
- \"string\"
- string
- 123
- -321
- 1.23
- -1e4
- ~
- null
- true
- false
- !!str 0
- !!int 100
- !!float 2
- !!null ~
- !!bool true
- !!bool false
# bad values
- !!int string
- !!float string
- !!bool null
- !!null val
";
let out = YamlLoader::load_from_str(&s).unwrap();
let doc = &out[0];
assert_eq!(doc[0].as_str().unwrap(), "string");
assert_eq!(doc[1].as_str().unwrap(), "string");
assert_eq!(doc[2].as_str().unwrap(), "string");
assert_eq!(doc[3].as_i64().unwrap(), 123);
assert_eq!(doc[4].as_i64().unwrap(), -321);
assert_eq!(doc[5].as_f64().unwrap(), 1.23);
assert_eq!(doc[6].as_f64().unwrap(), -1e4);
assert!(doc[7].is_null());
assert!(doc[8].is_null());
assert_eq!(doc[9].as_bool().unwrap(), true);
assert_eq!(doc[10].as_bool().unwrap(), false);
assert_eq!(doc[11].as_str().unwrap(), "0");
assert_eq!(doc[12].as_i64().unwrap(), 100);
assert_eq!(doc[13].as_f64().unwrap(), 2.0);
assert!(doc[14].is_null());
assert_eq!(doc[15].as_bool().unwrap(), true);
assert_eq!(doc[16].as_bool().unwrap(), false);
assert!(doc[17].is_badvalue());
assert!(doc[18].is_badvalue());
assert!(doc[19].is_badvalue());
assert!(doc[20].is_badvalue());
}
2015-05-24 17:34:18 +00:00
}