From d116fd7bff18342d9ee58eecc05108301e4af933 Mon Sep 17 00:00:00 2001 From: Jordan Santell Date: Thu, 26 Jan 2017 15:16:49 -0800 Subject: [PATCH] Add is_$type and as_$type methods to edn::Value types and add tests. Fixes #197 --- edn/src/types.rs | 59 ++++++++++++++++++++++++++++--- edn/tests/tests.rs | 86 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 5 deletions(-) diff --git a/edn/src/types.rs b/edn/src/types.rs index 24a82815..33bd998d 100644 --- a/edn/src/types.rs +++ b/edn/src/types.rs @@ -87,15 +87,64 @@ fn test_print_edn() { Value::Boolean(true))).to_string()); } -impl Value { - pub fn is_keyword(&self) -> bool { - match *self { - Keyword(_) => true, - _ => false, +/// Creates `is_$TYPE` helper functions for Value, like +/// `is_big_integer()` or `is_text()`. +macro_rules! def_is { + ($name: ident, $pat: pat) => { + pub fn $name(&self) -> bool { + match *self { $pat => true, _ => false } } } } +/// Creates `as_$TYPE` helper functions for Value, like `as_big_integer()`, +/// which returns the underlying value representing this Value wrapped +/// in an Option, like ``. +macro_rules! def_as { + ($name: ident, $kind: path, $t: ty) => { + pub fn $name(&self) -> Option<&$t> { + match *self { $kind(ref v) => Some(v), _ => None } + } + } +} + +impl Value { + def_is!(is_nil, Nil); + def_is!(is_boolean, Boolean(_)); + def_is!(is_integer, Integer(_)); + def_is!(is_big_integer, BigInteger(_)); + def_is!(is_float, Float(_)); + def_is!(is_text, Text(_)); + def_is!(is_symbol, PlainSymbol(_)); + def_is!(is_namespaced_symbol, NamespacedSymbol(_)); + def_is!(is_keyword, Keyword(_)); + def_is!(is_namespaced_keyword, NamespacedKeyword(_)); + def_is!(is_vector, Vector(_)); + def_is!(is_list, List(_)); + def_is!(is_set, Set(_)); + def_is!(is_map, Map(_)); + + /// `as_nil` does not use the macro as it does not have an underlying + /// value, and returns `Option<()>`. + pub fn as_nil(&self) -> Option<()> { + match *self { Nil => Some(()), _ => None } + } + + def_as!(as_boolean, Boolean, bool); + def_as!(as_integer, Integer, i64); + def_as!(as_big_integer, BigInteger, BigInt); + def_as!(as_float, Float, OrderedFloat); + def_as!(as_text, Text, String); + def_as!(as_symbol, PlainSymbol, symbols::PlainSymbol); + def_as!(as_namespaced_symbol, NamespacedSymbol, symbols::NamespacedSymbol); + def_as!(as_keyword, Keyword, symbols::Keyword); + def_as!(as_namespaced_keyword, NamespacedKeyword, symbols::NamespacedKeyword); + def_as!(as_vector, Vector, Vec); + def_as!(as_list, List, LinkedList); + def_as!(as_set, Set, BTreeSet); + def_as!(as_map, Map, BTreeMap); +} + impl PartialOrd for Value { fn partial_cmp(&self, other: &Value) -> Option { Some(self.cmp(other)) diff --git a/edn/tests/tests.rs b/edn/tests/tests.rs index 1707cd2b..46a053c5 100644 --- a/edn/tests/tests.rs +++ b/edn/tests/tests.rs @@ -33,6 +33,12 @@ fn k_plain(name: &str) -> Value { return Keyword(symbols::Keyword::new(name)); } +// Helper for making wrapped symbols with a namespace +fn s_ns(ns: &str, name: &str) -> Value { + return NamespacedSymbol(symbols::NamespacedSymbol::new(ns, name)); +} + +// Helper for making wrapped symbols without a namespace fn s_plain(name: &str) -> Value { return PlainSymbol(symbols::PlainSymbol::new(name)); } @@ -887,6 +893,86 @@ fn test_utils_merge() { test(&right, &left, &expected); } +macro_rules! def_test_as_type { + ($value: ident, $method: ident, $is_some: expr, $expected: expr) => { + if $is_some { + assert_eq!(*$value.$method().unwrap(), $expected) + } + assert_eq!($value.$method().is_some(), $is_some); + } +} + +#[test] +fn test_is_and_as_type_helper_functions() { + let max_i64 = i64::max_value().to_bigint().unwrap(); + let bigger = &max_i64 * &max_i64; + + let values = [ + Value::Nil, + Value::Boolean(false), + Value::Integer(1i64), + Value::BigInteger(bigger), + Value::Float(OrderedFloat(22.22f64)), + Value::Text("hello world".to_string()), + s_plain("$symbol"), + s_ns("$ns", "$symbol"), + k_plain("hello"), + k_ns("hello", "world"), + Value::Vector(vec![Integer(1)]), + Value::List(LinkedList::from_iter(vec![])), + Value::Set(BTreeSet::from_iter(vec![])), + Value::Map(BTreeMap::from_iter(vec![ + (Value::Text("a".to_string()), Value::Integer(1)), + ])), + ]; + + for i in 0..values.len() { + let value = &values[i]; + + let is_result = [ + value.is_nil(), + value.is_boolean(), + value.is_integer(), + value.is_big_integer(), + value.is_float(), + value.is_text(), + value.is_symbol(), + value.is_namespaced_symbol(), + value.is_keyword(), + value.is_namespaced_keyword(), + value.is_vector(), + value.is_list(), + value.is_set(), + value.is_map(), + ]; + + for j in 0..values.len() { + assert_eq!(j == i, is_result[j]); + } + + if i == 0 { assert_eq!(value.as_nil().unwrap(), ()) } + else { assert!(!value.as_nil().is_some()) } + + def_test_as_type!(value, as_boolean, i == 1, false); + def_test_as_type!(value, as_integer, i == 2, 1i64); + def_test_as_type!(value, as_big_integer, i == 3, &max_i64 * &max_i64); + def_test_as_type!(value, as_float, i == 4, OrderedFloat(22.22f64)); + def_test_as_type!(value, as_text, i == 5, "hello world".to_string()); + def_test_as_type!(value, as_symbol, i == 6, symbols::PlainSymbol::new("$symbol")); + def_test_as_type!(value, as_namespaced_symbol, i == 7, + symbols::NamespacedSymbol::new("$ns", "$symbol")); + def_test_as_type!(value, as_keyword, i == 8, symbols::Keyword::new("hello")); + def_test_as_type!(value, as_namespaced_keyword, i == 9, + symbols::NamespacedKeyword::new("hello", "world")); + def_test_as_type!(value, as_vector, i == 10, vec![Integer(1)]); + def_test_as_type!(value, as_list, i == 11, LinkedList::from_iter(vec![])); + def_test_as_type!(value, as_set, i == 12, BTreeSet::from_iter(vec![])); + def_test_as_type!(value, as_map, i == 13, BTreeMap::from_iter(vec![ + (Value::Text("a".to_string()), Value::Integer(1)), + ])); + } +} + /* // Handy templates for creating test cases follow: