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;
#[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 {
Bool(bool),
Integer(i64),
Float(f32),
Symbol(Symbol),
#[cfg(glam)]
Vec2(glam::Vec2),
#[cfg(glam)]
Vec3(glam::Vec3),
#[cfg(glam)]
Vec4(glam::Vec4),
Callable(Callable),
}
impl Value {
pub fn symbol(s: impl Into<String>) -> Self {
Self::Symbol(Symbol::new(s))
}
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 {
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);
}
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);
}
#[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()
}
}
#[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 {
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()
},
}
}
}
pub trait TryFromValue: Sized {
fn try_from_value(value: Value) -> Result<Self, TryFromValueError<Self>>;
}
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);
#[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>())
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum TryFromValueErrorKind {
InvalidType,
Overflow,
}