typed_eval/
eval_type.rs

1use crate::{BinOp, DynFn, RegistryAccess, Result, TypeInfo, UnOp};
2use std::borrow::Cow;
3
4pub trait EvalType: EvalTypeMethods + 'static {
5    type RefType<'a>;
6
7    fn type_info() -> TypeInfo;
8
9    fn to_ref_type<'a>(&'a self) -> Self::RefType<'a>;
10
11    fn register<Ctx: EvalType>(
12        registry: RegistryAccess<Ctx, Self>,
13    ) -> Result<()>;
14
15    fn make_dyn_fn<Ctx: EvalType>(
16        f: impl for<'a> Fn(&'a Ctx) -> Self::RefType<'a> + Clone + 'static,
17    ) -> DynFn {
18        DynFn::new::<Ctx, Self>(f)
19    }
20}
21
22// separate trait for methods
23// because Derive macro do not have access to impl blocks
24// there is a separate procedural macro to implement this trait
25pub trait EvalTypeMethods: Sized {
26    fn register_methods<Ctx: EvalType>(
27        registry: RegistryAccess<Ctx, Self>,
28    ) -> Result<()> {
29        _ = registry;
30        Ok(())
31    }
32}
33
34#[cfg(feature = "nightly")]
35// Blanket implementation with no methods
36impl<T: EvalType> EvalTypeMethods for T {
37    default fn register_methods<Ctx: EvalType>(
38        _registry: RegistryAccess<Ctx, Self>,
39    ) -> Result<()> {
40        Ok(())
41    }
42}
43
44#[cfg(not(feature = "nightly"))]
45impl EvalTypeMethods for () {}
46impl EvalType for () {
47    type RefType<'a> = ();
48
49    fn type_info() -> TypeInfo {
50        TypeInfo::new::<Self>("void")
51    }
52
53    fn to_ref_type<'a>(&'a self) -> Self::RefType<'a> {
54        *self
55    }
56
57    fn register<Ctx: EvalType>(
58        _registry: RegistryAccess<Ctx, Self>,
59    ) -> Result<()> {
60        Ok(())
61    }
62}
63
64#[cfg(not(feature = "nightly"))]
65impl EvalTypeMethods for i64 {}
66impl EvalType for i64 {
67    type RefType<'a> = i64;
68
69    fn type_info() -> TypeInfo {
70        TypeInfo::new::<Self>("i64")
71    }
72
73    fn to_ref_type<'a>(&'a self) -> Self::RefType<'a> {
74        *self
75    }
76
77    fn register<Ctx: EvalType>(
78        mut registry: RegistryAccess<Ctx, Self>,
79    ) -> Result<()> {
80        registry.register_cast::<f64>(|_: &Ctx, value: i64| value as f64)?;
81        registry.register_cast::<String>(|_: &Ctx, value: i64| {
82            format!("{value}").into()
83        })?;
84
85        registry
86            .register_bin_op(BinOp::Add, |_: &Ctx, lhs: i64, rhs: i64| {
87                lhs + rhs
88            })?;
89        registry
90            .register_bin_op(BinOp::Sub, |_: &Ctx, lhs: i64, rhs: i64| {
91                lhs - rhs
92            })?;
93        registry
94            .register_bin_op(BinOp::Mul, |_: &Ctx, lhs: i64, rhs: i64| {
95                lhs * rhs
96            })?;
97        registry
98            .register_bin_op(BinOp::Div, |_: &Ctx, lhs: i64, rhs: i64| {
99                lhs / rhs
100            })?;
101        registry.register_un_op(UnOp::Neg, |_: &Ctx, rhs: i64| -rhs)?;
102        registry.register_un_op(UnOp::Plus, |_: &Ctx, rhs: i64| rhs)?;
103        Ok(())
104    }
105}
106
107#[cfg(not(feature = "nightly"))]
108impl EvalTypeMethods for f64 {}
109impl EvalType for f64 {
110    type RefType<'a> = f64;
111
112    fn type_info() -> TypeInfo {
113        TypeInfo::new::<Self>("f64")
114    }
115
116    fn to_ref_type<'a>(&'a self) -> Self::RefType<'a> {
117        *self
118    }
119
120    fn register<Ctx: EvalType>(
121        mut registry: RegistryAccess<Ctx, Self>,
122    ) -> Result<()> {
123        registry.register_cast::<String>(|_: &Ctx, value: f64| {
124            format!("{value}").into()
125        })?;
126
127        registry
128            .register_bin_op(BinOp::Add, |_: &Ctx, lhs: f64, rhs: f64| {
129                lhs + rhs
130            })?;
131        registry
132            .register_bin_op(BinOp::Sub, |_: &Ctx, lhs: f64, rhs: f64| {
133                lhs - rhs
134            })?;
135        registry
136            .register_bin_op(BinOp::Mul, |_: &Ctx, lhs: f64, rhs: f64| {
137                lhs * rhs
138            })?;
139        registry
140            .register_bin_op(BinOp::Div, |_: &Ctx, lhs: f64, rhs: f64| {
141                lhs / rhs
142            })?;
143        registry.register_un_op(UnOp::Neg, |_: &Ctx, rhs: f64| -rhs)?;
144        registry.register_un_op(UnOp::Plus, |_: &Ctx, rhs: f64| rhs)?;
145        Ok(())
146    }
147}
148
149#[cfg(not(feature = "nightly"))]
150impl EvalTypeMethods for String {}
151impl EvalType for String {
152    type RefType<'a> = Cow<'a, str>;
153
154    fn type_info() -> TypeInfo {
155        TypeInfo::new::<Self>("String")
156    }
157
158    fn to_ref_type<'a>(&'a self) -> Self::RefType<'a> {
159        self.into()
160    }
161
162    fn register<Ctx: EvalType>(
163        mut registry: RegistryAccess<Ctx, Self>,
164    ) -> Result<()> {
165        registry.register_bin_op(
166            BinOp::Add,
167            |_: &Ctx, lhs: Self::RefType<'_>, rhs: Self::RefType<'_>| lhs + rhs,
168        )?;
169        Ok(())
170    }
171}
172
173macro_rules! impl_eval_type_for_tuples {
174    // Match one or more types in a tuple
175    ( $( $name:ident ),+ ) => {
176        #[cfg(not(feature = "nightly"))]
177        impl< $( $name ),+ > EvalTypeMethods for ( $( $name ),+ ) {}
178
179        impl< $( $name ),+ > EvalType for ( $( $name ),+ )
180        where
181            $( $name: EvalType ),+
182        {
183            type RefType<'a> = &'a Self;
184
185            fn type_info() -> TypeInfo {
186                TypeInfo::new::<Self>(concat!("Tuple(", $(stringify!($name), ","),*, ")"))
187            }
188
189            fn to_ref_type<'a>(&'a self) -> Self::RefType<'a> {
190                self
191            }
192
193            fn register<Ctx: EvalType>(
194                _registry: RegistryAccess<Ctx, Self>,
195            ) -> Result<()> {
196                Ok(())
197            }
198        }
199    };
200}
201
202impl_eval_type_for_tuples!(T1, T2);
203impl_eval_type_for_tuples!(T1, T2, T3);
204impl_eval_type_for_tuples!(T1, T2, T3, T4);
205impl_eval_type_for_tuples!(T1, T2, T3, T4, T5);