expy 0.0.2

Embeddable & extensible expression evaluator
Documentation
//! Deserialization utilities.

use std::fmt::{self, Display};

use derivative::Derivative;
use derive_more::{Deref, DerefMut};
use derive_new::new;
use serde::{Deserialize, Deserializer, Serialize};
use serde::de::{self, Visitor};

use crate::eval::Context;
use crate::interface::eval_in;
use crate::model::{TryFromValue, Value};


/// Deserialization wrapper around any Rust type that can be converted from a [`Value`].
///
/// When embedded inside a larger type that implements [`Deserialize`]
/// it enables a value to be computed from a deserialized expression string (in addition to the
/// regular serialized form of the type parameter).
#[derive(Clone, Derivative, Deref, DerefMut, Eq, Serialize)]
#[derivative(Debug, PartialEq, PartialOrd, Ord)]
#[serde(transparent)]
pub struct Calc<T> {
    #[deref]
    #[deref_mut]
    value: T,

    #[derivative(Debug = "ignore", PartialEq = "ignore", PartialOrd = "ignore", Ord = "ignore")]
    #[serde(skip)]
    expr: Option<String>,
}

impl<'de, T: TryFromValue> Deserialize<'de> for Calc<T> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: Deserializer<'de>
    {
        Calc::deserialize_ctx(deserializer, Context::new())
    }
}

impl<T: TryFromValue> Calc<T> {
    /// Deserialize [`Calc<T>`] using provided [`Context`].
    ///
    /// This method is typically invoked from a custom `#[serde(deserialize_with = ...)]` routine,
    /// or from a Deserialize impl for a (new)type wrapping [`Calc`].
    pub fn deserialize_ctx<'ctx, 'de, D>(
        deserializer: D, context: Context<'ctx>,
    ) -> Result<Calc<T>, D::Error>
        where D: Deserializer<'de>
    {
        let Calc { value, expr } = deserializer.deserialize_any(CalcValueVisitor::new(context))?;
        let value = T::try_from_value(value).map_err(de::Error::custom)?;
        Ok(Calc { value, expr })
    }
}

impl<T> Calc<T> {
    /// The original expression string that the value was deserialized from, if any.
    pub fn expr(&self) -> Option<&str> {
        self.expr.as_deref()
    }

    /// The calculated value.
    pub fn value(&self) -> &T {
        &self.value
    }
}

impl<T: Display> Display for Calc<T> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}", self.value)
    }
}

impl<T> Calc<T> {
    /// Consume this [`Calc`] wrapper, returning the contained value.
    pub fn into(self) -> T {
        self.value
    }
}


/// Implementation of serde [`Visitor`] that produces expression [`Value`]s by either consuming
/// raw input values, or evaluating string expressions that it encounters.
///
/// This type can be used to implement custom deserialization of [`Calc`] types, e.g. to provide
/// a custom [`Context`] to evaluate the expressions in.
///
/// Note that it is typically more convenient to use [`Calc::deserialize_ctx`].
#[derive(Debug, Default, new)]
struct CalcValueVisitor<'ctx> {  // TODO: do we want to expose this?
    context: Context<'ctx>,
}

macro_rules! impl_CalcValueVisitor_visit {
    ($visit_func:ident($arg_ty:ty) => $value_ty:ident) => {
        fn $visit_func<E: de::Error>(self, v: $arg_ty) -> Result<Self::Value, E> {
            Ok(Calc { value: Value::$value_ty(v as _), expr: None })
        }
    };
}

impl<'ctx, 'de> Visitor<'de> for CalcValueVisitor<'ctx> {
    type Value = Calc<Value>;

    fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(
            "representation of an expression value, \
            or a string with a valid expression to evaluate")
    }

    impl_CalcValueVisitor_visit!(visit_bool(bool) => Bool);
    impl_CalcValueVisitor_visit!(visit_i64(i64) => Integer);
    impl_CalcValueVisitor_visit!(visit_f64(f64) => Float);
    impl_CalcValueVisitor_visit!(visit_u64(u64) => Integer);  // TODO: handle overflow

    #[cfg(glam)]
    fn visit_seq<A: de::SeqAccess<'de>>(mut self, seq: A) -> Result<Self::Value, A::Error> {
        <Vec<f32> as Deserialize>::deserialize(de::value::SeqAccessDeserializer::new(seq))
            .map(|items| items.into_iter().map(Value::Float).collect::<Vec<_>>())
            .and_then(|items| self.context.eval_vector(&items).map_err(de::Error::custom))
            .map(|value| Calc { value, expr: None })
    }

    fn visit_str<E: de::Error>(mut self, s: &str) -> Result<Self::Value, E> {
        let value = eval_in(&mut self.context, s).map_err(de::Error::custom)?;
        Ok(Calc { value, expr: Some(s.to_owned()) })
    }
}