1use crate::{for_all_tuples, value::LoadStore, TypeSet, TypedValue};
4
5pub enum FunctionCallError {
7 FunctionNotFound,
9
10 IncorrectArgumentCount {
12 expected: usize,
14 },
15
16 IncorrectArgumentType {
18 idx: usize,
20 expected: &'static str,
22 },
23
24 Other(Box<str>),
26}
27
28pub trait DynFunction<A, T>
30where
31 T: TypeSet,
32{
33 fn call(&self, ctx: &mut T, args: &[TypedValue<T>])
41 -> Result<TypedValue<T>, FunctionCallError>;
42}
43
44macro_rules! substitute {
45 ($arg:tt, $replacement:tt) => {
46 $replacement
47 };
48}
49
50for_all_tuples! {
51 ($($arg:ident),*) => {
52 impl<$($arg,)* R, F, T> DynFunction<($($arg,)*), T> for F
53 where
54 $($arg: LoadStore<T>,)*
55 F: Fn($($arg,)*) -> R,
59 F: for<'t> Fn($($arg::Output<'t>,)*) -> R,
60 R: LoadStore<T>,
61 T: TypeSet,
62 {
63
64 #[allow(non_snake_case, unused)]
65 fn call(&self, ctx: &mut T, args: &[TypedValue<T>]) -> Result<TypedValue<T>, FunctionCallError> {
66 const ARG_COUNT: usize = 0 $( + substitute!($arg, 1) )* ;
67
68 if args.len() != ARG_COUNT {
69 return Err(FunctionCallError::IncorrectArgumentCount { expected: ARG_COUNT });
70 }
71
72 let idx = 0;
73 $(
74 let Some($arg) = <$arg>::load(ctx, &args[idx]) else {
75 return Err(FunctionCallError::IncorrectArgumentType { idx, expected: std::any::type_name::<$arg>() });
76 };
77 let idx = idx + 1;
78 )*
79
80
81 Ok(self($($arg),*).store(ctx))
82 }
83 }
84 };
85}
86
87pub(crate) struct ExprFn<'ctx, T: TypeSet> {
88 #[allow(clippy::type_complexity)]
89 func: Box<dyn Fn(&mut T, &[TypedValue<T>]) -> Result<TypedValue<T>, FunctionCallError> + 'ctx>,
90}
91
92impl<'ctx, T: TypeSet> ExprFn<'ctx, T> {
93 pub fn new<A, F>(func: F) -> Self
94 where
95 F: DynFunction<A, T> + 'ctx,
96 {
97 Self {
98 func: Box::new(move |ctx, args| func.call(ctx, args)),
99 }
100 }
101
102 pub fn call(
103 &self,
104 ctx: &mut T,
105 args: &[TypedValue<T>],
106 ) -> Result<TypedValue<T>, FunctionCallError> {
107 (self.func)(ctx, args)
108 }
109}