1use std::borrow::Cow;
4use std::collections::HashMap;
5use std::sync::Arc;
6
7use super::expression_evaluation_context::IExpressionForeignFunctionInterface;
8use super::expression_variant::{ExpressionVariant, ExpressionVariantValue, VariantType};
9
10type FfiDelegate = Arc<dyn Fn(&[ExpressionVariant]) -> ExpressionVariant + Send + Sync>;
12
13struct FfiRecord {
14 types: Vec<VariantType>,
15 delegate: FfiDelegate,
16}
17
18#[derive(Default)]
30pub struct DelegateExpressionFfi {
31 registry: HashMap<String, HashMap<usize, Vec<FfiRecord>>>,
32 ignore_case: bool,
33}
34
35fn type_of<T: ExpressionVariantValue>() -> VariantType {
40 match T::FFI_VARIANT_TYPE {
41 Some(t) => t,
42 None => panic!(
43 "{} cannot be a parameter of an expression foreign function",
44 std::any::type_name::<T>()
45 ),
46 }
47}
48
49macro_rules! add_overload {
52 ($(#[$meta:meta])* $name:ident; $($t:ident => $index:expr),+) => {
53 $(#[$meta])*
54 pub fn $name<$($t,)+ R, F>(&mut self, name: &str, cb: F)
55 where
56 $($t: ExpressionVariantValue,)+
57 R: Into<ExpressionVariant>,
58 F: Fn($($t),+) -> R + Send + Sync + 'static,
59 {
60 self.add(
61 name,
62 Arc::new(move |args: &[ExpressionVariant]| {
63 cb($(args[$index].cast_or_default::<$t>()),+).into()
64 }),
65 vec![$(type_of::<$t>()),+],
66 );
67 }
68 };
69}
70
71impl DelegateExpressionFfi {
72 pub fn new() -> Self {
74 Self::default()
75 }
76
77 pub fn new_ignoring_case() -> Self {
81 Self {
82 ignore_case: true,
83 ..Self::default()
84 }
85 }
86
87 fn normalized_name<'n>(&self, name: &'n str) -> Cow<'n, str> {
89 if self.ignore_case && name.bytes().any(|b| b.is_ascii_uppercase()) {
90 Cow::Owned(name.to_ascii_lowercase())
91 } else {
92 Cow::Borrowed(name)
93 }
94 }
95
96 fn call_with_cast(
97 count_group: &[FfiRecord],
98 arguments: &[ExpressionVariant],
99 any_cast: bool,
100 ) -> Option<ExpressionVariant> {
101 for record in count_group {
102 let mut matches = true;
103 for (c, argument) in arguments.iter().enumerate() {
104 let parameter = record.types[c];
105 let arg = argument.variant_type();
106 if parameter != arg {
107 let can_cast = (parameter == VariantType::Vector3D && arg == VariantType::Vector3)
108 || (parameter == VariantType::Vector && arg == VariantType::Vector2)
109 || (any_cast
110 && ((arg == VariantType::Vector3D && parameter == VariantType::Vector3)
111 || (arg == VariantType::Vector && parameter == VariantType::Vector2)));
112 if !can_cast {
113 matches = false;
114 break;
115 }
116 }
117 }
118
119 if matches {
120 return Some((record.delegate)(arguments));
121 }
122 }
123
124 if !any_cast {
125 return Self::call_with_cast(count_group, arguments, true);
126 }
127 None
128 }
129
130 fn add(&mut self, name: &str, cb: FfiDelegate, types: Vec<VariantType>) {
131 let key = self.normalized_name(name).into_owned();
132 self.registry
133 .entry(key)
134 .or_default()
135 .entry(types.len())
136 .or_default()
137 .push(FfiRecord { types, delegate: cb });
138 }
139
140 pub fn add0<R, F>(&mut self, name: &str, cb: F)
142 where
143 R: Into<ExpressionVariant>,
144 F: Fn() -> R + Send + Sync + 'static,
145 {
146 self.add(name, Arc::new(move |_: &[ExpressionVariant]| cb().into()), Vec::new());
147 }
148
149 add_overload!(
150 add1; T1 => 0
152 );
153 add_overload!(
154 add2; T1 => 0, T2 => 1
156 );
157 add_overload!(
158 add3; T1 => 0, T2 => 1, T3 => 2
160 );
161 add_overload!(
162 add4; T1 => 0, T2 => 1, T3 => 2, T4 => 3
164 );
165 add_overload!(
166 add5; T1 => 0, T2 => 1, T3 => 2, T4 => 3, T5 => 4
168 );
169 add_overload!(
170 add6; T1 => 0, T2 => 1, T3 => 2, T4 => 3, T5 => 4, T6 => 5
177 );
178 add_overload!(
179 add16;
185 T1 => 0, T2 => 1, T3 => 2, T4 => 3,
186 T5 => 4, T6 => 5, T7 => 6, T8 => 7,
187 T9 => 8, T10 => 9, T11 => 10, T12 => 11,
188 T13 => 12, T14 => 13, T15 => 14, T16 => 15
189 );
190}
191
192impl IExpressionForeignFunctionInterface for DelegateExpressionFfi {
193 fn call(&self, name: &str, arguments: &[ExpressionVariant]) -> Option<ExpressionVariant> {
194 let key = self.normalized_name(name);
195 let name_group = self.registry.get(key.as_ref())?;
196 let count_group = name_group.get(&arguments.len())?;
197 for record in count_group {
198 let mut matches = true;
199 for (c, argument) in arguments.iter().enumerate() {
200 if record.types[c] != argument.variant_type() {
201 matches = false;
202 break;
203 }
204 }
205
206 if matches {
207 return Some((record.delegate)(arguments));
208 }
209 }
210
211 Self::call_with_cast(count_group, arguments, false)
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218 use crate::numerics::{Vector2, Vector3};
219 use crate::{Matrix, Vector, Vector3D};
220
221 fn call(ffi: &DelegateExpressionFfi, name: &str, args: &[ExpressionVariant]) -> Option<ExpressionVariant> {
222 ffi.call(name, args)
223 }
224
225 #[test]
226 fn selects_the_overload_by_name_count_and_types() {
227 let mut ffi = DelegateExpressionFfi::new();
228 ffi.add0("F", || 0.0f32);
229 ffi.add1("F", |a: f32| a + 1.0);
230 ffi.add1("F", |a: Vector2| a.x + a.y);
231 ffi.add2("F", |a: f32, b: bool| if b { a } else { -a });
232
233 assert_eq!(call(&ffi, "F", &[]), Some(0.0.into()));
234 assert_eq!(call(&ffi, "F", &[2.0.into()]), Some(3.0.into()));
235 assert_eq!(call(&ffi, "F", &[Vector2::new(1.0, 2.0).into()]), Some(3.0.into()));
236 assert_eq!(call(&ffi, "F", &[2.0.into(), false.into()]), Some((-2.0).into()));
237 assert_eq!(call(&ffi, "F", &[true.into()]), None);
238 assert_eq!(call(&ffi, "F", &[2.0.into(), 2.0.into()]), None);
239 assert_eq!(call(&ffi, "F", &[2.0.into(), false.into(), 1.0.into()]), None);
240 assert_eq!(call(&ffi, "G", &[]), None);
241 assert_eq!(call(&ffi, "f", &[]), None);
242 }
243
244 #[test]
245 fn the_first_registered_of_equal_signatures_wins() {
246 let mut ffi = DelegateExpressionFfi::new();
247 ffi.add1("F", |a: f32| a);
248 ffi.add1("F", |a: f64| a * 2.0);
249 assert_eq!(call(&ffi, "F", &[0.1.into()]), Some((0.1f32 as f64).into()));
252 }
253
254 #[test]
255 fn widens_single_precision_vectors_before_narrowing() {
256 let mut ffi = DelegateExpressionFfi::new();
257 ffi.add1("Wide", |a: Vector| a.x);
258 ffi.add1("Wide3", |a: Vector3D| a.z);
259 ffi.add1("Narrow", |a: Vector2| a.x);
260 ffi.add1("Narrow3", |a: Vector3| a.z);
261 ffi.add2("Both", |a: Vector2, _b: Vector2| a.x);
262 ffi.add2("Both", |a: Vector, _b: Vector| a.x + 100.0);
263
264 assert_eq!(call(&ffi, "Wide", &[Vector2::new(1.5, 0.0).into()]), Some(1.5.into()));
265 assert_eq!(
266 call(&ffi, "Wide3", &[Vector3::new(0.0, 0.0, 2.5).into()]),
267 Some(2.5.into())
268 );
269 assert_eq!(call(&ffi, "Narrow", &[Vector::new(1.5, 0.0).into()]), Some(1.5.into()));
270 assert_eq!(
271 call(&ffi, "Narrow3", &[Vector3D::new(0.0, 0.0, 2.5).into()]),
272 Some(2.5.into())
273 );
274 assert_eq!(
276 call(
277 &ffi,
278 "Both",
279 &[Vector2::new(1.0, 0.0).into(), Vector::new(0.0, 0.0).into()]
280 ),
281 Some(101.0.into())
282 );
283 assert_eq!(call(&ffi, "Wide", &[Vector3::new(1.0, 2.0, 3.0).into()]), None);
284 }
285
286 #[test]
287 fn every_parameter_reads_its_own_argument() {
288 let mut ffi = DelegateExpressionFfi::new();
289 ffi.add5("F", |a: f32, b: f32, c: f32, d: f32, e: f32| {
290 a + 10.0 * b + 100.0 * c + 1000.0 * d + 10000.0 * e
291 });
292 ffi.add6("F", |_a: f32, _b: f32, _c: f32, _d: f32, e: f32, f: f32| e * 10.0 + f);
293 #[allow(clippy::too_many_arguments)]
294 ffi.add16(
295 "F",
296 |a: f32,
297 _b: f32,
298 _c: f32,
299 _d: f32,
300 e: f32,
301 f: f32,
302 _g: f32,
303 _h: f32,
304 _i: f32,
305 j: f32,
306 _k: f32,
307 _l: f32,
308 _m: f32,
309 _n: f32,
310 _o: f32,
311 p: f32| a + e * 10.0 + f * 100.0 + j * 1000.0 + p * 10000.0,
312 );
313 let args: Vec<ExpressionVariant> = (1..=16).map(|v| (v as f64).into()).collect();
314 assert_eq!(call(&ffi, "F", &args[..5]), Some(54321.0.into()));
315 assert_eq!(call(&ffi, "F", &args[..6]), Some(56.0.into()));
316 assert_eq!(call(&ffi, "F", &args), Some(170651.0.into()));
317 }
318
319 #[test]
320 fn a_registry_ignoring_case_matches_names_in_any_case() {
321 let mut ffi = DelegateExpressionFfi::new_ignoring_case();
322 ffi.add1("Lerp", |a: f32| a);
323 ffi.add1("Matrix3x2.CreateScale", |a: f32| a * 2.0);
324 assert_eq!(call(&ffi, "Lerp", &[1.0.into()]), Some(1.0.into()));
325 assert_eq!(call(&ffi, "lerp", &[1.0.into()]), Some(1.0.into()));
326 assert_eq!(call(&ffi, "LERP", &[1.0.into()]), Some(1.0.into()));
327 assert_eq!(call(&ffi, "matrix3x2.createscale", &[1.0.into()]), Some(2.0.into()));
328 assert_eq!(call(&ffi, "Lerp2", &[1.0.into()]), None);
329 ffi.add1("LERP", |a: Vector2| a.x);
331 assert_eq!(call(&ffi, "lerp", &[Vector2::new(3.0, 0.0).into()]), Some(3.0.into()));
332 }
333
334 #[test]
335 #[should_panic(expected = "cannot be a parameter")]
336 fn the_3x3_matrix_cannot_be_a_parameter() {
337 let mut ffi = DelegateExpressionFfi::new();
338 ffi.add1("F", |m: Matrix| m);
339 }
340
341 #[test]
342 fn is_shareable_between_threads() {
343 fn assert_send_sync<T: Send + Sync>() {}
344 assert_send_sync::<DelegateExpressionFfi>();
345 }
346}