expy 0.0.2

Embeddable & extensible expression evaluator
Documentation
//! Value type.

use std::any::type_name;
use std::error::Error;
use std::fmt::{self, Debug, Display};
use std::marker::PhantomData;
use std::ptr;
use std::sync::Arc;

use derive_more::{Deref, From, TryInto, Unwrap};
use derive_new::new;
use enum_as_inner::EnumAsInner;
use strum::{Display as EnumDisplay, EnumDiscriminants, EnumIter, EnumString, IntoStaticStr};

use crate::eval::{Context, Error as EvalError};

use super::function::Function;


/// Value that the expression evaluator can operate on.
///
/// Both inputs to, and the outputs of, the expy's expressions are represented as [`Value`]s.
#[derive(Clone, Debug, EnumAsInner, EnumDiscriminants, From, PartialEq)]
#[cfg_attr(serde, derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(serde, serde(rename_all = "snake_case"))]
#[strum(serialize_all = "snake_case")]
#[strum_discriminants(
    derive(EnumDisplay, EnumIter, EnumString, Hash, IntoStaticStr),
    cfg_attr(serde, derive(serde::Deserialize, serde::Serialize)),
    cfg_attr(serde, serde(rename_all = "snake_case")),
    strum(serialize_all = "snake_case"),
    vis(pub), name(Type)
)]
pub enum Value {
    /// Boolean value.
    Bool(bool),

    /// Integer value (signed).
    Integer(i64),

    /// Floating point value.
    Float(f32),

    /// Symbol value.
    ///
    /// Symbols are opaque, unmodifiable "tokens" that can be thought of as type-less enum
    /// variants. They can be used to represent particular, distinct concepts that are handled
    /// by user-defined function in some specific way.
    Symbol(Symbol),

    /// 2D vector of floats.
    #[cfg(glam)]
    Vec2(glam::Vec2),

    /// 3D vector of floats.
    #[cfg(glam)]
    Vec3(glam::Vec3),

    /// 4D vector of floats.
    #[cfg(glam)]
    Vec4(glam::Vec4),

    /// Callable value.
    Callable(Callable),
}

impl Value {
    /// Create a new symbol value.
    pub fn symbol(s: impl Into<String>) -> Self {
        Self::Symbol(Symbol::new(s))
    }

    /// Create a new callable value.
    pub fn callable(c: impl Into<Callable>) -> Self {
        Self::Callable(c.into())
    }
}

impl From<Function> for Value {
    fn from(func: Function) -> Self {
        Self::callable(func)
    }
}

impl<F> From<F> for Value
    where F: Fn(&mut Context, &[Value]) -> Result<Value, EvalError> + Send + Sync + 'static
{
    fn from(func: F) -> Self {
        Self::callable(Callable::Custom(Arc::new(func)))
    }
}

impl Value {
    /// Type of this value.
    pub fn ty(&self) -> Type {
        self.into()
    }
}

macro_rules! try_as {
    ($func:ident: $value_ty:ident => $ty:ty) => {
        doc_comment! {
            concat!(
                "Try to cast the value as a borrowed `", stringify!($ty),
                "`, returning a reference on success or a type error on failure."),
            pub fn $func(&self) -> Result<&$ty, EvalError> {
                match self {
                    Self::$value_ty(v) => Ok(v),
                    val => Err(EvalError::type_([[Type::$value_ty]], [val.ty()])),
                }
            }
        }
    };
}

macro_rules! try_to {
    ($func:ident: $value_ty:ident => $ty:ty) => {
        doc_comment! {
            concat!(
                "Try to cast the value as owned `", stringify!($ty),
                "`, returning a type error on failure."),
            pub fn $func(&self) -> Result<$ty, EvalError> {
                match self {
                    Self::$value_ty(v) => Ok(v.clone()),
                    val => Err(EvalError::type_([[Type::$value_ty]], [val.ty()])),
                }
            }
        }
    };
}

impl Value {
    try_as!(try_as_bool: Bool => bool);
    try_as!(try_as_integer: Integer => i64);
    try_as!(try_as_float: Float => f32);
    try_as!(try_as_symbol: Symbol => str);
    #[cfg(glam)] try_as!(try_as_vec2: Vec2 => glam::Vec2);
    #[cfg(glam)] try_as!(try_as_vec3: Vec3 => glam::Vec3);
    #[cfg(glam)] try_as!(try_as_vec4: Vec4 => glam::Vec4);
    try_as!(try_as_callable: Callable => Callable);

    try_to!(try_to_bool: Bool => bool);
    try_to!(try_to_integer: Integer => i64);
    try_to!(try_to_float: Float => f32);
    try_as!(try_to_symbol: Symbol => String);
    #[cfg(glam)] try_to!(try_to_vec2: Vec2 => glam::Vec2);
    #[cfg(glam)] try_to!(try_to_vec3: Vec3 => glam::Vec3);
    #[cfg(glam)] try_to!(try_to_vec4: Vec4 => glam::Vec4);
    try_to!(try_to_callable: Callable => Callable);
}

// The unwrap_*() methods need to be implemented manually because derive_more::Unwrap
// doesn't handle #[attributes] on enum variant fields.
macro_rules! unwrap {
    ($func:ident: $value_ty:ident => $ty:ty) => {
        doc_comment! {
            concat!(
                "Returns the contained `", stringify!($value_ty), "` as `", stringify!(),
                "`, consuming the Value or panicking if it's not `", stringify!($value_ty), "`."),
            pub fn $func(self) -> $ty {
                match self {
                    Self::$value_ty(v) => v,
                    _ => panic!("expected a Value::{}", stringify!($value_ty)),
                }
            }
        }
    };
}

impl Value {
    unwrap!(unwrap_bool: Bool => bool);
    unwrap!(unwrap_integer: Integer => i64);
    unwrap!(unwrap_float: Float => f32);
    unwrap!(unwrap_symbol: Symbol => Symbol);
    #[cfg(glam)] unwrap!(unwrap_vec2: Vec2 => glam::Vec2);
    #[cfg(glam)] unwrap!(unwrap_vec3: Vec3 => glam::Vec3);
    #[cfg(glam)] unwrap!(unwrap_vec4: Vec4 => glam::Vec4);
    unwrap!(unwrap_callable: Callable => Callable);
}


/// Symbol type.
#[derive(Clone, Debug, Deref, Eq, PartialEq)]
#[cfg_attr(serde, derive(serde::Serialize))]
#[cfg_attr(serde, serde(into = "String"))]
pub struct Symbol(Arc<String>);

impl Symbol {
    pub fn new(name: impl Into<String>) -> Self {
        Self(Arc::new(name.into()))
    }
}

#[cfg(serde)]
impl<'de> serde::Deserialize<'de> for Symbol {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: serde::Deserializer<'de>
    {
        let name: String = serde::Deserialize::deserialize(deserializer)?;
        Ok(Self::new(name))
    }
}

impl From<Symbol> for String {
    fn from(symbol: Symbol) -> Self {
        (*symbol.0).clone()
    }
}


/// Callable value.
#[derive(Clone, EnumAsInner, From, TryInto, Unwrap)]
#[cfg_attr(serde, derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(serde, serde(untagged))]
pub enum Callable {
    Native(Function),

    #[cfg_attr(serde, serde(skip))]
    Custom(Arc<dyn Fn(&mut Context, &[Value]) -> Result<Value, EvalError> + Send + Sync>),
}

impl Callable {
    /// Return the address of the callable's implementation, if available.
    pub fn address(&self) -> Option<usize> {
        self.as_custom().map(|func| ptr::addr_of!(**func) as *const () as _)
    }
}

impl PartialEq for Callable {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Self::Native(lhs), Self::Native(rhs)) => lhs == rhs,
            (lhs@Self::Custom(_), rhs@Self::Custom(_)) => lhs.address() == rhs.address(),
            _ => false,
        }
    }
}

impl Debug for Callable {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::Native(func) => fmt.debug_tuple("Native").field(func).finish(),
            Self::Custom(_) => {
                fmt.debug_tuple("Custom")
                    .field(&format_args!("{:x}", self.address().unwrap()))
                    .finish()
            },
        }
    }
}


//
// Conversions from `Value`
//

/// Trait for Rust types that a [`Value`] can potentially be converted to.
pub trait TryFromValue: Sized {
    /// Try to convert from a [`Value`] into this type.
    fn try_from_value(value: Value) -> Result<Self, TryFromValueError<Self>>;
}

// ^ The main reason this trait exists is that the standard TryFrom trait defines its ::Error
// associated type with no constraints whatsoever -- particularly no :Error and no :Display.
// This means it's impossible to convert TryFrom::Error to some more universal error types,
// most notably the Serde deserialization errors.
//
// Additionally, this trait makes it possible to implement the Calc<T> type in a generic way,
// as opposed to repeating its Deserialize impl for each of the supported types.

macro_rules! impl_TryFromValue {
    ($value_ty:ident => $ty:ty) => {
        impl TryFromValue for $ty {
            fn try_from_value(value: Value) -> Result<Self, TryFromValueError<Self>> {
                match value {
                    Value::$value_ty(val) => Ok(val as $ty),
                    _ => {
                        Err(TryFromValueError::<$ty>::new(
                            TryFromValueErrorKind::InvalidType, value))
                    },
                }
            }
        }
    };
}

macro_rules! impl_TryFromValue_with_overflow {
    ($value_ty:ident => $ty:ty) => {
        impl TryFromValue for $ty {
            fn try_from_value(value: Value) -> Result<Self, TryFromValueError<Self>> {
                match value {
                    Value::$value_ty(val) => {
                        val.try_into().map_err(|_| {
                            TryFromValueError::<$ty>::new(TryFromValueErrorKind::Overflow, value)
                        })
                    },
                    _ => {
                        Err(TryFromValueError::<$ty>::new(
                            TryFromValueErrorKind::InvalidType, value))
                    },
                }
            }
        }
    };
}

impl_TryFromValue!(Bool => bool);
impl_TryFromValue_with_overflow!(Integer => i8);
impl_TryFromValue_with_overflow!(Integer => i16);
impl_TryFromValue_with_overflow!(Integer => i32);
impl_TryFromValue!(Integer => i64);
impl_TryFromValue!(Integer => i128);
impl_TryFromValue_with_overflow!(Integer => isize);
impl_TryFromValue_with_overflow!(Integer => u8);
impl_TryFromValue_with_overflow!(Integer => u16);
impl_TryFromValue_with_overflow!(Integer => u32);
impl_TryFromValue!(Integer => u64);
impl_TryFromValue!(Integer => u128);
impl_TryFromValue_with_overflow!(Integer => usize);
impl_TryFromValue!(Float => f32);
impl_TryFromValue!(Float => f64);
#[cfg(glam)] impl_TryFromValue!(Vec2 => glam::Vec2);
#[cfg(glam)] impl_TryFromValue!(Vec3 => glam::Vec3);
#[cfg(glam)] impl_TryFromValue!(Vec4 => glam::Vec4);

/// Error when trying to convert from [`Value`] into a Rust type.
#[derive(new)]
pub struct TryFromValueError<T> {
    kind: TryFromValueErrorKind,
    input: Value,

    #[new(default)]
    _marker: PhantomData<fn() -> T>,
}

impl<T> Error for TryFromValueError<T> {}

impl<T> TryFromValueError<T> {
    pub fn kind(&self) -> TryFromValueErrorKind {
        self.kind
    }
}

impl<T> Debug for TryFromValueError<T> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt
            .debug_struct(&format!("FromValueError<{}>", type_name::<T>()))
            .field("input", &self.input)
            .finish_non_exhaustive()
    }
}

impl<T> Display for TryFromValueError<T> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{:?} cannot be converted to {}", self.input, type_name::<T>())
    }
}

/// [`TryFromValueError`] error kind.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum TryFromValueErrorKind {
    /// [`Value`] had an invalid value type for the requested target type.
    InvalidType,
    /// Value would overflow the target type.
    Overflow,
}