use std::collections::BTreeMap;
use std::str::FromStr;
use num::NumCast;
use super::{File, Value};
pub trait FromValue: Sized {
fn from_value(value: &Value) -> Option<Self>;
}
impl FromValue for Value {
fn from_value(value: &Value) -> Option<Self> {
Some(value.clone())
}
}
macro_rules! num_from_value {
($ty:ty) => {
impl FromValue for $ty {
fn from_value(value: &Value) -> Option<$ty> {
match *value {
Value::I64(value) => NumCast::from(value),
Value::U64(value) => NumCast::from(value),
Value::F64(value) => NumCast::from(value),
Value::String(ref value) => FromStr::from_str(value).ok(),
_ => None,
}
}
}
}
}
num_from_value!(u8);
num_from_value!(u16);
num_from_value!(u32);
num_from_value!(u64);
num_from_value!(usize);
num_from_value!(i8);
num_from_value!(i16);
num_from_value!(i32);
num_from_value!(i64);
num_from_value!(isize);
num_from_value!(f32);
num_from_value!(f64);
impl FromValue for String {
fn from_value(value: &Value) -> Option<String> {
match *value {
Value::Null => Some(String::new()),
Value::Boolean(value) => Some(value.to_string()),
Value::I64(value) => Some(value.to_string()),
Value::U64(value) => Some(value.to_string()),
Value::F64(value) => Some(value.to_string()),
Value::String(ref value) => Some(value.clone()),
_ => None,
}
}
}
static FALSE_STRINGS: &'static [&'static str] = &["0", "f", "F", "false", "FALSE", "off", "OFF"];
static TRUE_STRINGS: &'static [&'static str] = &["1", "t", "T", "true", "TRUE", "on", "ON"];
impl FromValue for bool {
fn from_value(value: &Value) -> Option<bool> {
match *value {
Value::Boolean(value) => Some(value),
Value::I64(value) if value == 0 => Some(false),
Value::I64(value) if value == 1 => Some(true),
Value::U64(value) if value == 0 => Some(false),
Value::U64(value) if value == 1 => Some(true),
Value::String(ref value) if FALSE_STRINGS.contains(&&value[..]) => Some(false),
Value::String(ref value) if TRUE_STRINGS.contains(&&value[..]) => Some(true),
_ => None,
}
}
}
impl FromValue for File {
fn from_value(value: &Value) -> Option<File> {
match *value {
Value::File(ref file) => Some(file.clone()),
_ => None,
}
}
}
impl<T: FromValue> FromValue for Option<T> {
fn from_value(value: &Value) -> Option<Option<T>> {
match *value {
Value::Null => Some(None),
_ => T::from_value(value).map(Some),
}
}
}
impl<T: FromValue> FromValue for Vec<T> {
fn from_value(value: &Value) -> Option<Vec<T>> {
match *value {
Value::Array(ref array) => {
let mut vec = Vec::with_capacity(array.len());
for value in array {
match T::from_value(value) {
Some(value) => vec.push(value),
None => return None,
}
}
Some(vec)
},
_ => None,
}
}
}
impl<T: FromValue> FromValue for BTreeMap<String, T> {
fn from_value(value: &Value) -> Option<BTreeMap<String, T>> {
match *value {
Value::Map(ref map) => map.to_strict_map(),
_ => None,
}
}
}
#[test]
fn cast_within_bounds() {
assert_eq!(<u8 as FromValue>::from_value(&Value::U64(100)), Some(100));
assert_eq!(<u8 as FromValue>::from_value(&Value::String("100".to_owned())), Some(100));
assert_eq!(<i8 as FromValue>::from_value(&Value::I64(-100)), Some(-100));
assert_eq!(<i8 as FromValue>::from_value(&Value::String("-100".to_owned())), Some(-100));
}
#[test]
fn cast_out_of_bounds() {
assert!(<u8 as FromValue>::from_value(&Value::U64(1000)).is_none());
assert!(<u8 as FromValue>::from_value(&Value::String("1000".to_owned())).is_none());
assert!(<i8 as FromValue>::from_value(&Value::I64(-1000)).is_none());
assert!(<i8 as FromValue>::from_value(&Value::String("-1000".to_owned())).is_none());
}