femstache 0.1.2

Embrace the feminine mustache to unlock your templating dreams! Or something, I don't know. :3
Documentation
use std::collections::BTreeMap;

/// A representation of a value in the templating language
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
    /// The billion dollar mistake
    Null,
    /// A boolean value
    Bool(bool),
    /// A string
    String(String),
    /// A list of values
    List(Vec<Value>),
    /// A mapping of strings to values
    Dict(BTreeMap<String, Value>),
}

impl Value {
    /// Is this a billion dollar mistake?
    pub fn is_null(&self) -> bool {
        matches!(self, Value::Null)
    }

    /// Is this a boolean value?
    pub fn is_bool(&self) -> bool {
        matches!(self, Value::Bool(_))
    }

    /// Is this a string value?
    pub fn is_string(&self) -> bool {
        matches!(self, Value::String(_))
    }

    /// Is this a list value?
    pub fn is_list(&self) -> bool {
        matches!(self, Value::List(_))
    }

    /// Is this a dict value?
    pub fn is_dict(&self) -> bool {
        matches!(self, Value::Dict(_))
    }

    /// Grab the inner [bool], if this is a bool
    pub fn as_bool(&self) -> Option<bool> {
        if let &Value::Bool(b) = self {
            Some(b)
        } else {
            None
        }
    }

    /// Borrow as a [str], if this is a string
    pub fn as_str(&self) -> Option<&str> {
        if let Value::String(s) = self {
            Some(s)
        } else {
            None
        }
    }

    /// Borrow as a slice, if this is a list
    pub fn as_list(&self) -> Option<&[Value]> {
        if let Value::List(l) = self {
            Some(l)
        } else {
            None
        }
    }

    /// Borrow as a [BTreeMap], if this is a dict
    pub fn as_dict(&self) -> Option<&BTreeMap<String, Value>> {
        if let Value::Dict(m) = self {
            Some(m)
        } else {
            None
        }
    }

    /// Turn this into its inner [String], if it is a list
    pub fn into_string(self) -> Option<String> {
        if let Value::String(s) = self {
            Some(s)
        } else {
            None
        }
    }

    /// Turn this into its inner [Vec], if it is a list
    pub fn into_list(self) -> Option<Vec<Value>> {
        if let Value::List(l) = self {
            Some(l)
        } else {
            None
        }
    }

    pub fn is_truthy(&self) -> bool {
        match self {
            Value::Null => false,
            Value::Bool(b) => *b,
            Value::String(s) => !s.is_empty(),
            Value::List(l) => !l.is_empty(),
            Value::Dict(m) => !m.is_empty(),
        }
    }
}

/// Trait for types which can be turned into [Value]
pub trait ToValue {
    /// Turn this into a [Value]
    fn to_value(&self) -> Value;
}

impl ToValue for Value {
    fn to_value(&self) -> Value {
        self.clone()
    }
}

impl<T: ToValue> ToValue for &T {
    fn to_value(&self) -> Value {
        <T as ToValue>::to_value(self)
    }
}

impl<T: ToValue> ToValue for &[T] {
    fn to_value(&self) -> Value {
        Value::List(self.iter().map(|e| e.to_value()).collect())
    }
}

impl ToValue for &str {
    fn to_value(&self) -> Value {
        Value::String((*self).to_owned())
    }
}

impl ToValue for String {
    fn to_value(&self) -> Value {
        Value::String(self.clone())
    }
}

macro_rules! impl_to_value_via_to_string {
    ($name:ident) => {
        impl ToValue for $name {
            fn to_value(&self) -> Value {
                Value::String(<$name as ::std::string::ToString>::to_string(self))
            }
        }
    };
    ($($name:ident,)*) => {
        $(impl_to_value_via_to_string! { $name })*
    }
}

impl_to_value_via_to_string! {
    u8, u16, u32, u64, u128,
    i8, i16, i32, i64, i128,
    bool,
}