saphyr-serde/parser/src/yaml.rs

155 lines
3.4 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 17:34:18 +00:00
use regex::Regex;
2015-05-24 06:27:42 +00:00
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 {
I64(i64),
2015-05-24 17:34:18 +00:00
//U64(u64),
//F64(f64),
2015-05-24 06:27:42 +00:00
String(string::String),
Boolean(bool),
Array(self::Array),
Hash(self::Hash),
Null,
2015-05-24 19:21:53 +00:00
/// Access non-exist node by Index trait will return BadValue.
/// This simplifies error handling of user.
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
/// The errors that can arise while parsing a YAML stream.
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ErrorCode {
InvalidSyntax,
InvalidNumber,
EOFWhileParsingObject,
EOFWhileParsingArray,
EOFWhileParsingValue,
EOFWhileParsingString,
KeyMustBeAString,
ExpectedColon,
TrailingCharacters,
TrailingComma,
InvalidEscape,
InvalidUnicodeCodePoint,
LoneLeadingSurrogateInHexEscape,
UnexpectedEndOfHexEscape,
UnrecognizedHex,
NotFourDigit,
NotUtf8,
}
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
}
}
);
);
// these regex are from libyaml-rust project
macro_rules! regex(
($s:expr) => (Regex::new($s).unwrap());
);
impl Yaml {
define_as!(as_i64, i64, I64);
define_as!(as_bool, bool, Boolean);
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-24 17:34:18 +00:00
pub fn as_f64(&self) -> Option<f64> {
// XXX(chenyh) precompile me
let float_pattern = regex!(r"^([-+]?)(\.[0-9]+|[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)?)$");
match *self {
Yaml::String(ref v) if float_pattern.is_match(v) => {
v.parse::<f64>().ok()
},
_ => None
}
}
2015-05-24 18:16:28 +00:00
pub fn from_str(s: &str) -> Yaml {
Yaml::String(s.to_string())
}
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 {
use parser::Parser;
use yaml::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]
";
let mut parser = Parser::new(s.chars());
let out = parser.load().unwrap();
2015-05-24 18:16:28 +00:00
assert_eq!(out["a"].as_str().unwrap(), "1");
assert_eq!(out["c"][1].as_str().unwrap(), "2");
assert!(out["d"][0].is_badvalue());
2015-05-24 17:34:18 +00:00
//assert_eq!(out.as_hash().unwrap()[&Yaml::String("a".to_string())].as_i64().unwrap(), 1i64);
}
}