gtmpl_value 0.5.1

Internal value format for gtmpl-rust
Documentation
use std::borrow::Cow;
use std::collections::HashMap;

use crate::value::{Func, Function, Value};

macro_rules! from_num {
    ($($ty:ident)*) => {
        $(
            impl From<$ty> for Value {
                fn from(n: $ty) -> Self {
                    Value::Number(n.into())
                }
            }
        )*
    };
}

from_num! {
    i8 i16 i32 i64 isize
    u8 u16 u32 u64 usize
    f32 f64
}

impl From<bool> for Value {
    /// Convert boolean to `Value`
    ///
    /// # Examples
    ///
    /// ```rust
    /// use gtmpl_value::Value;
    ///
    /// let b = false;
    /// let x: Value = b.into();
    /// ```
    fn from(b: bool) -> Self {
        Value::Bool(b)
    }
}

impl<'a> From<&'a String> for Value {
    /// Convert &String to `Value`
    ///
    /// # Examples
    ///
    /// ```rust
    /// use gtmpl_value::Value;
    ///
    /// let s: &String = &"foobar".to_owned();
    /// let x: Value = s.into();
    /// ```
    fn from(s: &'a String) -> Self {
        Value::String(s.clone())
    }
}

impl From<String> for Value {
    /// Convert String to `Value`
    ///
    /// # Examples
    ///
    /// ```rust
    /// use gtmpl_value::Value;
    ///
    /// let s: String = "foobar".to_owned();
    /// let x: Value = s.into();
    /// ```
    fn from(s: String) -> Self {
        Value::String(s)
    }
}

impl<'a> From<&'a str> for Value {
    /// Convert &str to `Value`
    ///
    /// # Examples
    ///
    /// ```rust
    /// use gtmpl_value::Value;
    ///
    /// let s = "foobar";
    /// let x: Value = s.into();
    /// ```
    fn from(f: &str) -> Self {
        Value::String(f.to_string())
    }
}

impl<'a> From<Cow<'a, str>> for Value {
    /// Convert Cow<str> to `Value`
    ///
    /// # Examples
    ///
    /// ```rust
    /// use gtmpl_value::Value;
    /// use std::borrow::Cow;
    ///
    /// let s: Cow<str> = Cow::Borrowed("foobar");
    /// let x: Value = s.into();
    /// ```
    fn from(f: Cow<'a, str>) -> Self {
        Value::String(f.to_string())
    }
}

impl From<Func> for Value {
    /// Convert Func to `Value`
    ///
    /// # Examples
    ///
    /// ```rust
    /// use gtmpl_value::{Func, FuncError, Value};
    ///
    /// fn f(a: &[Value]) -> Result<Value, FuncError> {
    ///     Ok(a[0].clone())
    /// };
    /// let x: Value = (f as Func).into();
    /// ```
    fn from(f: Func) -> Self {
        Value::Function(Function { f })
    }
}

impl<T> From<Vec<T>> for Value
where
    T: Into<Value> + Clone,
{
    /// Convert Vec to `Value`
    ///
    /// # Examples
    ///
    /// ```rust
    /// use gtmpl_value::Value;
    ///
    /// let v = vec!(1, 2, 3);
    /// let x: Value = v.into();
    /// ```
    fn from(f: Vec<T>) -> Self {
        Value::Array(f.iter().cloned().map(|x| x.into()).collect())
    }
}

impl<'a, T> From<&'a [T]> for Value
where
    T: Into<Value> + Clone,
{
    /// Convert Slice to `Value`
    ///
    /// # Examples
    ///
    /// ```rust
    /// use gtmpl_value::Value;
    ///
    /// let v: &[i32] = &[1, 2, 3];
    /// let x: Value = v.into();
    /// ```
    fn from(f: &'a [T]) -> Self {
        Value::Array(f.iter().cloned().map(|x| x.into()).collect())
    }
}

impl<T> From<HashMap<String, T>> for Value
where
    T: Into<Value> + Clone,
{
    /// Convert HashMap<String, T> to `Value`
    ///
    /// # Examples
    ///
    /// ```rust
    /// use gtmpl_value::Value;
    /// use std::collections::HashMap;
    ///
    /// let mut m = HashMap::new();
    /// m.insert("hello".to_owned(), 123);
    /// let x: Value = m.into();
    /// ```
    fn from(f: HashMap<String, T>) -> Self {
        Value::Map(
            f.iter()
                .map(|(s, x)| (s.clone(), x.clone().into()))
                .collect(),
        )
    }
}

impl<T> From<Option<T>> for Value
where
    T: Into<Value> + Clone,
{
    /// Convert Option<T> to `Value`
    ///
    /// # Examples
    ///
    /// ```rust
    /// use gtmpl_value::Value;
    ///
    /// let i = Some(1);
    /// let x: Value = i.into();
    /// ```
    fn from(f: Option<T>) -> Self {
        match f {
            Some(x) => x.into(),
            _ => Value::NoValue,
        }
    }
}

/// Convert Value into something.
pub trait FromValue<T> {
    /// Tries to retrieve `T` from `Value.`
    fn from_value(val: &Value) -> Option<T>;
}

impl FromValue<i64> for i64 {
    /// Tries to retrieve `i64` from `Value.`
    ///
    /// # Examples:
    ///
    /// ```rust
    /// use gtmpl_value::{FromValue, Value};
    ///
    /// let v: Value = 23i64.into();
    /// let i = i64::from_value(&v);
    /// assert_eq!(i, Some(23i64));
    /// ```
    fn from_value(val: &Value) -> Option<i64> {
        if let Value::Number(ref n) = *val {
            n.as_i64()
        } else {
            None
        }
    }
}

impl FromValue<u64> for u64 {
    /// Tries to retrieve `u64` from `Value.`
    ///
    /// # Examples:
    ///
    /// ```rust
    /// use gtmpl_value::{FromValue, Value};
    ///
    /// let v: Value = 23u64.into();
    /// let i = u64::from_value(&v);
    /// assert_eq!(i, Some(23u64));
    /// ```
    fn from_value(val: &Value) -> Option<u64> {
        if let Value::Number(ref n) = *val {
            n.as_u64()
        } else {
            None
        }
    }
}

impl FromValue<f64> for f64 {
    /// Tries to retrieve `f64` from `Value.`
    ///
    /// # Examples:
    ///
    /// ```rust
    /// use gtmpl_value::{FromValue, Value};
    ///
    /// let v: Value = 23.1f64.into();
    /// let i = f64::from_value(&v);
    /// assert_eq!(i, Some(23.1f64));
    /// ```
    fn from_value(val: &Value) -> Option<f64> {
        if let Value::Number(ref n) = *val {
            n.as_f64()
        } else {
            None
        }
    }
}

impl FromValue<String> for String {
    /// Tries to retrieve `String` from `Value.`
    ///
    /// # Examples:
    ///
    /// ```rust
    /// use gtmpl_value::{FromValue, Value};
    ///
    /// let v: Value = "foobar".into();
    /// let s = String::from_value(&v);
    /// assert_eq!(s, Some("foobar".to_owned()));
    /// ```
    fn from_value(val: &Value) -> Option<String> {
        if let Value::String(ref s) = *val {
            Some(s.clone())
        } else {
            None
        }
    }
}

impl<T> FromValue<Vec<T>> for Vec<T>
where
    T: FromValue<T>,
{
    /// Tries to retrieve `Vec<T>` from `Value.`
    ///
    /// # Examples:
    ///
    /// ```rust
    /// use gtmpl_value::{FromValue, Value};
    ///
    /// let v: Value = vec!(1, 2, 3).into();
    /// let v: Option<Vec<i64>> = Vec::from_value(&v);
    /// assert_eq!(v, Some(vec!(1, 2, 3)));
    /// ```
    fn from_value(val: &Value) -> Option<Vec<T>> {
        if let Value::Array(ref a) = *val {
            let v: Vec<T> = a.iter().flat_map(|v| T::from_value(v)).collect();
            if v.len() == a.len() {
                return Some(v);
            }
        }
        None
    }
}

#[allow(clippy::implicit_hasher)]
impl<T> FromValue<HashMap<String, T>> for HashMap<String, T>
where
    T: FromValue<T>,
{
    /// Tries to retrieve `HashMap<String, T>` from `Value.`
    ///
    /// # Examples:
    ///
    /// ```rust
    /// use gtmpl_value::{FromValue, Value};
    /// use std::collections::HashMap;
    ///
    /// let mut m = HashMap::new();
    /// m.insert("a".to_owned(), 1);
    /// let v: Value = m.into();
    /// let m: Option<HashMap<String, i64>> = HashMap::from_value(&v);
    /// assert!(m.is_some());
    /// if let Some(m) = m {
    ///   assert_eq!(m.get("a"), Some(&1));
    /// }
    /// ```
    fn from_value(val: &Value) -> Option<HashMap<String, T>> {
        match *val {
            Value::Object(ref o) | Value::Map(ref o) => {
                let m: HashMap<String, T> = o
                    .iter()
                    .map(|(s, v)| (s.clone(), T::from_value(v)))
                    .flat_map(|(s, t)| t.map(|t| (s, t)))
                    .collect();
                if m.len() == o.len() {
                    Some(m)
                } else {
                    None
                }
            }
            _ => None,
        }
    }
}

/// `FromValue` wrapped in a macro (required for `gtmpl_fn!` macro).
///
/// # Examples:
///
/// ```rust
/// use gtmpl_value::{from_value, Value};
///
/// let v: Value = 1.into();
/// let s: Option<i64> = from_value(&v);
/// assert_eq!(s, Some(1));
/// ```
pub fn from_value<T>(val: &Value) -> Option<T>
where
    T: FromValue<T>,
{
    T::from_value(val)
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_vec() {
        let val: Value = vec![1, 2, 3].into();
        if let Value::Array(array) = val {
            assert_eq!(array[0], 1.into());
            assert_eq!(array[1], 2.into());
            assert_eq!(array[2], 3.into());
        } else {
            panic!();
        }

        let val: Value = vec!["foo", "bar"].into();
        if let Value::Array(array) = val {
            assert_eq!(array[0], "foo".into());
            assert_eq!(array[1], "bar".into());
        } else {
            panic!();
        }
    }

    #[test]
    fn test_slice() {
        let slice: &[u8] = &[1, 2, 3];
        let val: Value = slice.into();
        if let Value::Array(array) = val {
            assert_eq!(array[0], 1.into());
            assert_eq!(array[1], 2.into());
            assert_eq!(array[2], 3.into());
        } else {
            panic!();
        }
    }

    #[test]
    fn test_map() {
        let mut m = HashMap::new();
        m.insert("a".to_owned(), 1);
        m.insert("b".to_owned(), 2);
        let val: Value = m.into();
        if let Value::Map(obj) = val {
            assert_eq!(obj.get("a"), Some(&(1.into())));
            assert_eq!(obj.get("b"), Some(&(2.into())));
        } else {
            panic!();
        }
    }
}