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};
#[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> {
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> {
pub fn expr(&self) -> Option<&str> {
self.expr.as_deref()
}
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> {
pub fn into(self) -> T {
self.value
}
}
#[derive(Debug, Default, new)]
struct CalcValueVisitor<'ctx> { 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);
#[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()) })
}
}