Add is_$type and as_$type methods to edn::Value types and add tests. Fixes #197

This commit is contained in:
Jordan Santell 2017-01-26 15:16:49 -08:00
parent c6fa14c0c8
commit d116fd7bff
2 changed files with 140 additions and 5 deletions

View file

@ -87,13 +87,62 @@ fn test_print_edn() {
Value::Boolean(true))).to_string()); Value::Boolean(true))).to_string());
} }
/// 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 `<Option<&BigInt>`.
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 { impl Value {
pub fn is_keyword(&self) -> bool { def_is!(is_nil, Nil);
match *self { def_is!(is_boolean, Boolean(_));
Keyword(_) => true, def_is!(is_integer, Integer(_));
_ => false, 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<f64>);
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<Value>);
def_as!(as_list, List, LinkedList<Value>);
def_as!(as_set, Set, BTreeSet<Value>);
def_as!(as_map, Map, BTreeMap<Value, Value>);
} }
impl PartialOrd for Value { impl PartialOrd for Value {

View file

@ -33,6 +33,12 @@ fn k_plain(name: &str) -> Value {
return Keyword(symbols::Keyword::new(name)); 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 { fn s_plain(name: &str) -> Value {
return PlainSymbol(symbols::PlainSymbol::new(name)); return PlainSymbol(symbols::PlainSymbol::new(name));
} }
@ -887,6 +893,86 @@ fn test_utils_merge() {
test(&right, &left, &expected); 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: // Handy templates for creating test cases follow: