diff --git a/Cargo.toml b/Cargo.toml index c0d91d1c4..1c3f59718 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,8 @@ lazy_static = "0.2" chrono = "0.3" unicode-segmentation = "1.1" itertools = "0.6.0" +serde = "1.0" +serde_derive = "1.0" [build-dependencies] skeptic = "0.9" @@ -26,6 +28,7 @@ skeptic = "0.9" [dev-dependencies] difference = "1.0" skeptic = "0.9" +serde_yaml = "0.7" [features] default = ["extra-filters"] diff --git a/src/lib.rs b/src/lib.rs index 8466811d6..86fe6ba16 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,13 +31,18 @@ #![allow(unknown_lints)] #![allow(zero_ptr)] -#[macro_use] -extern crate lazy_static; extern crate regex; extern crate chrono; extern crate unicode_segmentation; extern crate itertools; +#[macro_use] +extern crate lazy_static; + +extern crate serde; +#[macro_use] +extern crate serde_derive; + use std::collections::HashMap; use lexer::Element; use tags::{assign_tag, cycle_tag, include_tag, break_tag, continue_tag, comment_block, raw_block, diff --git a/src/value.rs b/src/value.rs index 5cb7c41af..9261e6e1e 100644 --- a/src/value.rs +++ b/src/value.rs @@ -8,10 +8,12 @@ use token::Token::*; /// An enum to represent different value types #[derive(Clone, Debug)] +#[derive(Serialize, Deserialize)] +#[serde(untagged)] pub enum Value { - Str(String), Num(f32), Bool(bool), + Str(String), Array(Array), Object(Object), } @@ -40,19 +42,6 @@ impl<'a> Value { } } - /// Extracts the str value if it is a str. - pub fn as_str(&'a self) -> Option<&'a str> { - match *self { - Value::Str(ref v) => Some(v), - _ => None, - } - } - - /// Tests whether this value is a str - pub fn is_str(&self) -> bool { - self.as_str().is_some() - } - /// Extracts the float value if it is a float. pub fn as_float(&self) -> Option { match *self { @@ -79,6 +68,19 @@ impl<'a> Value { self.as_bool().is_some() } + /// Extracts the str value if it is a str. + pub fn as_str(&'a self) -> Option<&'a str> { + match *self { + Value::Str(ref v) => Some(v), + _ => None, + } + } + + /// Tests whether this value is a str + pub fn is_str(&self) -> bool { + self.as_str().is_some() + } + /// Extracts the array value if it is an array. pub fn as_array(&self) -> Option<&Vec> { match *self { @@ -125,9 +127,9 @@ impl<'a> Value { impl PartialEq for Value { fn eq(&self, other: &Value) -> bool { match (self, other) { - (&Value::Str(ref x), &Value::Str(ref y)) => x == y, (&Value::Num(x), &Value::Num(y)) => x == y, (&Value::Bool(x), &Value::Bool(y)) => x == y, + (&Value::Str(ref x), &Value::Str(ref y)) => x == y, (&Value::Array(ref x), &Value::Array(ref y)) => x == y, (&Value::Object(ref x), &Value::Object(ref y)) => x == y, @@ -146,9 +148,9 @@ impl Eq for Value {} impl PartialOrd for Value { fn partial_cmp(&self, other: &Value) -> Option { match (self, other) { - (&Value::Str(ref x), &Value::Str(ref y)) => x.partial_cmp(y), (&Value::Num(x), &Value::Num(y)) => x.partial_cmp(&y), (&Value::Bool(x), &Value::Bool(y)) => x.partial_cmp(&y), + (&Value::Str(ref x), &Value::Str(ref y)) => x.partial_cmp(y), (&Value::Array(ref x), &Value::Array(ref y)) => x.iter().partial_cmp(y.iter()), (&Value::Object(ref x), &Value::Object(ref y)) => x.iter().partial_cmp(y.iter()), _ => None, @@ -159,9 +161,9 @@ impl PartialOrd for Value { impl ToString for Value { fn to_string(&self) -> String { match *self { - Value::Str(ref x) => x.to_owned(), Value::Num(ref x) => x.to_string(), Value::Bool(ref x) => x.to_string(), + Value::Str(ref x) => x.to_owned(), Value::Array(ref x) => { let arr: Vec = x.iter().map(|v| v.to_string()).collect(); arr.join(", ") @@ -190,21 +192,21 @@ mod test { static FALSE: Value = Value::Bool(false); #[test] - fn test_as_str() { + fn test_num_to_string() { let val = Value::Num(42f32); - assert_eq!(val.as_str(), None); + assert_eq!(&val.to_string(), "42"); - let val = Value::str("test"); - assert_eq!(val.as_str(), Some("test")); + let val = Value::Num(42.34); + assert_eq!(&val.to_string(), "42.34"); } #[test] - fn test_num_to_string() { + fn test_as_str() { let val = Value::Num(42f32); - assert_eq!(&val.to_string(), "42"); + assert_eq!(val.as_str(), None); - let val = Value::Num(42.34); - assert_eq!(&val.to_string(), "42.34"); + let val = Value::str("test"); + assert_eq!(val.as_str(), Some("test")); } #[test] diff --git a/tests/value.rs b/tests/value.rs new file mode 100644 index 000000000..130a34c7d --- /dev/null +++ b/tests/value.rs @@ -0,0 +1,131 @@ +extern crate liquid; +extern crate serde_yaml; + +#[macro_use] +extern crate difference; + +use std::f32; + +#[test] +pub fn serialize_num() { + let actual = liquid::Value::Num(1f32); + let actual = serde_yaml::to_string(&actual).unwrap(); + assert_diff!(&actual, "---\n1", "", 0); + + let actual = liquid::Value::Num(-100f32); + let actual = serde_yaml::to_string(&actual).unwrap(); + assert_diff!(&actual, "---\n-100", "", 0); + + let actual = liquid::Value::Num(3.14e10f32); + let actual = serde_yaml::to_string(&actual).unwrap(); + assert_diff!(&actual, "---\n31399999488", "", 0); + + let actual = liquid::Value::Num(f32::NAN); + let actual = serde_yaml::to_string(&actual).unwrap(); + assert_diff!(&actual, "---\nNaN", "", 0); + + let actual = liquid::Value::Num(f32::INFINITY); + let actual = serde_yaml::to_string(&actual).unwrap(); + assert_diff!(&actual, "---\ninf", "", 0); +} + +#[test] +pub fn deserialize_num() { + let actual: liquid::Value = serde_yaml::from_str("---\n1").unwrap(); + assert_eq!(actual, liquid::Value::Num(1f32)); + + let actual: liquid::Value = serde_yaml::from_str("---\n-100").unwrap(); + assert_eq!(actual, liquid::Value::Num(-100f32)); + + let actual: liquid::Value = serde_yaml::from_str("---\n31399999488").unwrap(); + assert_eq!(actual, liquid::Value::Num(3.14e10f32)); + + // Skipping NaN since equality fails + + let actual: liquid::Value = serde_yaml::from_str("---\ninf").unwrap(); + assert_eq!(actual, liquid::Value::Num(f32::INFINITY)); +} + +#[test] +pub fn serialize_bool() { + let actual = liquid::Value::Bool(true); + let actual = serde_yaml::to_string(&actual).unwrap(); + assert_diff!(&actual, "---\ntrue", "", 0); + + let actual = liquid::Value::Bool(false); + let actual = serde_yaml::to_string(&actual).unwrap(); + assert_diff!(&actual, "---\nfalse", "", 0); +} + +#[test] +pub fn deserialize_bool() { + let actual: liquid::Value = serde_yaml::from_str("---\ntrue").unwrap(); + assert_eq!(actual, liquid::Value::Bool(true)); + + let actual: liquid::Value = serde_yaml::from_str("---\nfalse").unwrap(); + assert_eq!(actual, liquid::Value::Bool(false)); +} + +#[test] +pub fn serialize_str() { + let actual = liquid::Value::str("Hello"); + let actual = serde_yaml::to_string(&actual).unwrap(); + assert_diff!(&actual, "---\nHello", "", 0); + + let actual = liquid::Value::str("10"); + let actual = serde_yaml::to_string(&actual).unwrap(); + assert_diff!(&actual, "---\n\"10\"", "", 0); + + let actual = liquid::Value::str("false"); + let actual = serde_yaml::to_string(&actual).unwrap(); + assert_diff!(&actual, "---\n\"false\"", "", 0); +} + +#[test] +pub fn deserialize_str() { + let actual: liquid::Value = serde_yaml::from_str("---\nHello").unwrap(); + assert_eq!(actual, liquid::Value::str("Hello")); + + let actual: liquid::Value = serde_yaml::from_str("\"10\"\n").unwrap(); + assert_eq!(actual, liquid::Value::str("10")); + + let actual: liquid::Value = serde_yaml::from_str("---\n\"false\"").unwrap(); + assert_eq!(actual, liquid::Value::str("false")); +} + +#[test] +pub fn serialize_array() { + let actual = + vec![liquid::Value::Num(1f32), liquid::Value::Bool(true), liquid::Value::str("true")]; + let actual = liquid::Value::Array(actual); + let actual = serde_yaml::to_string(&actual).unwrap(); + assert_diff!(&actual, "---\n- 1\n- true\n- \"true\"", "", 0); +} + +#[test] +pub fn deserialize_array() { + let actual: liquid::Value = serde_yaml::from_str("---\n- 1\n- true\n- \"true\"").unwrap(); + let expected = + vec![liquid::Value::Num(1f32), liquid::Value::Bool(true), liquid::Value::str("true")]; + let expected = liquid::Value::Array(expected); + assert_eq!(actual, expected); +} + +#[test] +pub fn serialize_object() { + // Skipping due to HashMap ordering issues +} + +#[test] +pub fn deserialize_object() { + let actual: liquid::Value = serde_yaml::from_str("---\nNum: 1\nBool: true\nStr: \"true\"") + .unwrap(); + let expected: liquid::Object = [("Num".to_owned(), liquid::Value::Num(1f32)), + ("Bool".to_owned(), liquid::Value::Bool(true)), + ("Str".to_owned(), liquid::Value::str("true"))] + .iter() + .cloned() + .collect(); + let expected = liquid::Value::Object(expected); + assert_eq!(actual, expected); +}