Skip to main content

ferroui_base/rendering/composition/expressions/
delegate_expression_ffi.rs

1//! Foreign function interface for composition animations based on calling closures.
2
3use std::collections::HashMap;
4use std::sync::Arc;
5
6use super::expression_evaluation_context::IExpressionForeignFunctionInterface;
7use super::expression_variant::{ExpressionVariant, ExpressionVariantValue, VariantType};
8
9/// A registered function taking its arguments as variants.
10type FfiDelegate = Arc<dyn Fn(&[ExpressionVariant]) -> ExpressionVariant + Send + Sync>;
11
12struct FfiRecord {
13    types: Vec<VariantType>,
14    delegate: FfiDelegate,
15}
16
17/// Foreign function interface for composition animations based on calling closures.
18///
19/// Functions are overloaded by name, argument count and argument types. A
20/// call first looks for an overload whose parameter types are exactly the
21/// argument types, in registration order; then for one reachable by widening
22/// single-precision vector arguments to the double-precision vector types;
23/// then for one reachable by converting between the two vector families in
24/// either direction.
25#[derive(Default)]
26pub struct DelegateExpressionFfi {
27    registry: HashMap<String, HashMap<usize, Vec<FfiRecord>>>,
28}
29
30/// The variant type a parameter of type `T` is matched against.
31///
32/// # Panics
33/// Panics when `T` cannot be a foreign function parameter (a programmer error).
34fn type_of<T: ExpressionVariantValue>() -> VariantType {
35    match T::FFI_VARIANT_TYPE {
36        Some(t) => t,
37        None => panic!(
38            "{} cannot be a parameter of an expression foreign function",
39            std::any::type_name::<T>()
40        ),
41    }
42}
43
44/// Generates a typed `add` overload. Each parameter is given with the index
45/// of the argument it is read from.
46macro_rules! add_overload {
47    ($(#[$meta:meta])* $name:ident; $($t:ident => $index:expr),+) => {
48        $(#[$meta])*
49        pub fn $name<$($t,)+ R, F>(&mut self, name: &str, cb: F)
50        where
51            $($t: ExpressionVariantValue,)+
52            R: Into<ExpressionVariant>,
53            F: Fn($($t),+) -> R + Send + Sync + 'static,
54        {
55            self.add(
56                name,
57                Arc::new(move |args: &[ExpressionVariant]| {
58                    cb($(args[$index].cast_or_default::<$t>()),+).into()
59                }),
60                vec![$(type_of::<$t>()),+],
61            );
62        }
63    };
64}
65
66impl DelegateExpressionFfi {
67    /// Creates an empty registry.
68    pub fn new() -> Self {
69        Self::default()
70    }
71
72    fn call_with_cast(
73        count_group: &[FfiRecord],
74        arguments: &[ExpressionVariant],
75        any_cast: bool,
76    ) -> Option<ExpressionVariant> {
77        for record in count_group {
78            let mut matches = true;
79            for (c, argument) in arguments.iter().enumerate() {
80                let parameter = record.types[c];
81                let arg = argument.variant_type();
82                if parameter != arg {
83                    let can_cast = (parameter == VariantType::Vector3D && arg == VariantType::Vector3)
84                        || (parameter == VariantType::Vector && arg == VariantType::Vector2)
85                        || (any_cast
86                            && ((arg == VariantType::Vector3D && parameter == VariantType::Vector3)
87                                || (arg == VariantType::Vector && parameter == VariantType::Vector2)));
88                    if !can_cast {
89                        matches = false;
90                        break;
91                    }
92                }
93            }
94
95            if matches {
96                return Some((record.delegate)(arguments));
97            }
98        }
99
100        if !any_cast {
101            return Self::call_with_cast(count_group, arguments, true);
102        }
103        None
104    }
105
106    fn add(&mut self, name: &str, cb: FfiDelegate, types: Vec<VariantType>) {
107        self.registry
108            .entry(name.to_owned())
109            .or_default()
110            .entry(types.len())
111            .or_default()
112            .push(FfiRecord { types, delegate: cb });
113    }
114
115    /// Registers a function without parameters.
116    pub fn add0<R, F>(&mut self, name: &str, cb: F)
117    where
118        R: Into<ExpressionVariant>,
119        F: Fn() -> R + Send + Sync + 'static,
120    {
121        self.add(name, Arc::new(move |_: &[ExpressionVariant]| cb().into()), Vec::new());
122    }
123
124    add_overload!(
125        /// Registers a function with one parameter.
126        add1; T1 => 0
127    );
128    add_overload!(
129        /// Registers a function with two parameters.
130        add2; T1 => 0, T2 => 1
131    );
132    add_overload!(
133        /// Registers a function with three parameters.
134        add3; T1 => 0, T2 => 1, T3 => 2
135    );
136    add_overload!(
137        /// Registers a function with four parameters.
138        add4; T1 => 0, T2 => 1, T3 => 2, T4 => 3
139    );
140    add_overload!(
141        /// Registers a function with five parameters.
142        add5; T1 => 0, T2 => 1, T3 => 2, T4 => 3, T5 => 4
143    );
144    add_overload!(
145        /// Registers a function with six parameters.
146        ///
147        /// As in the reference implementation, the sixth parameter is read
148        /// from the fifth argument.
149        add6; T1 => 0, T2 => 1, T3 => 2, T4 => 3, T5 => 4, T6 => 4
150    );
151    add_overload!(
152        /// Registers a function with sixteen parameters.
153        ///
154        /// As in the reference implementation, the sixth to sixteenth
155        /// parameters are read from the fifth argument.
156        add16;
157        T1 => 0, T2 => 1, T3 => 2, T4 => 3,
158        T5 => 4, T6 => 4, T7 => 4, T8 => 4,
159        T9 => 4, T10 => 4, T11 => 4, T12 => 4,
160        T13 => 4, T14 => 4, T15 => 4, T16 => 4
161    );
162}
163
164impl IExpressionForeignFunctionInterface for DelegateExpressionFfi {
165    fn call(&self, name: &str, arguments: &[ExpressionVariant]) -> Option<ExpressionVariant> {
166        let name_group = self.registry.get(name)?;
167        let count_group = name_group.get(&arguments.len())?;
168        for record in count_group {
169            let mut matches = true;
170            for (c, argument) in arguments.iter().enumerate() {
171                if record.types[c] != argument.variant_type() {
172                    matches = false;
173                    break;
174                }
175            }
176
177            if matches {
178                return Some((record.delegate)(arguments));
179            }
180        }
181
182        Self::call_with_cast(count_group, arguments, false)
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189    use crate::numerics::{Vector2, Vector3};
190    use crate::{Matrix, Vector, Vector3D};
191
192    fn call(ffi: &DelegateExpressionFfi, name: &str, args: &[ExpressionVariant]) -> Option<ExpressionVariant> {
193        ffi.call(name, args)
194    }
195
196    #[test]
197    fn selects_the_overload_by_name_count_and_types() {
198        let mut ffi = DelegateExpressionFfi::new();
199        ffi.add0("F", || 0.0f32);
200        ffi.add1("F", |a: f32| a + 1.0);
201        ffi.add1("F", |a: Vector2| a.x + a.y);
202        ffi.add2("F", |a: f32, b: bool| if b { a } else { -a });
203
204        assert_eq!(call(&ffi, "F", &[]), Some(0.0.into()));
205        assert_eq!(call(&ffi, "F", &[2.0.into()]), Some(3.0.into()));
206        assert_eq!(call(&ffi, "F", &[Vector2::new(1.0, 2.0).into()]), Some(3.0.into()));
207        assert_eq!(call(&ffi, "F", &[2.0.into(), false.into()]), Some((-2.0).into()));
208        assert_eq!(call(&ffi, "F", &[true.into()]), None);
209        assert_eq!(call(&ffi, "F", &[2.0.into(), 2.0.into()]), None);
210        assert_eq!(call(&ffi, "F", &[2.0.into(), false.into(), 1.0.into()]), None);
211        assert_eq!(call(&ffi, "G", &[]), None);
212        assert_eq!(call(&ffi, "f", &[]), None);
213    }
214
215    #[test]
216    fn the_first_registered_of_equal_signatures_wins() {
217        let mut ffi = DelegateExpressionFfi::new();
218        ffi.add1("F", |a: f32| a);
219        ffi.add1("F", |a: f64| a * 2.0);
220        // Both are matched against a Double argument; the single-precision
221        // overload narrows the argument.
222        assert_eq!(call(&ffi, "F", &[0.1.into()]), Some((0.1f32 as f64).into()));
223    }
224
225    #[test]
226    fn widens_single_precision_vectors_before_narrowing() {
227        let mut ffi = DelegateExpressionFfi::new();
228        ffi.add1("Wide", |a: Vector| a.x);
229        ffi.add1("Wide3", |a: Vector3D| a.z);
230        ffi.add1("Narrow", |a: Vector2| a.x);
231        ffi.add1("Narrow3", |a: Vector3| a.z);
232        ffi.add2("Both", |a: Vector2, _b: Vector2| a.x);
233        ffi.add2("Both", |a: Vector, _b: Vector| a.x + 100.0);
234
235        assert_eq!(call(&ffi, "Wide", &[Vector2::new(1.5, 0.0).into()]), Some(1.5.into()));
236        assert_eq!(
237            call(&ffi, "Wide3", &[Vector3::new(0.0, 0.0, 2.5).into()]),
238            Some(2.5.into())
239        );
240        assert_eq!(call(&ffi, "Narrow", &[Vector::new(1.5, 0.0).into()]), Some(1.5.into()));
241        assert_eq!(
242            call(&ffi, "Narrow3", &[Vector3D::new(0.0, 0.0, 2.5).into()]),
243            Some(2.5.into())
244        );
245        // Mixed arguments: only the widening overload is reachable in the first pass.
246        assert_eq!(
247            call(
248                &ffi,
249                "Both",
250                &[Vector2::new(1.0, 0.0).into(), Vector::new(0.0, 0.0).into()]
251            ),
252            Some(101.0.into())
253        );
254        assert_eq!(call(&ffi, "Wide", &[Vector3::new(1.0, 2.0, 3.0).into()]), None);
255    }
256
257    #[test]
258    fn parameters_past_the_fifth_read_the_fifth_argument() {
259        let mut ffi = DelegateExpressionFfi::new();
260        ffi.add5("F", |a: f32, b: f32, c: f32, d: f32, e: f32| {
261            a + 10.0 * b + 100.0 * c + 1000.0 * d + 10000.0 * e
262        });
263        ffi.add6("F", |_a: f32, _b: f32, _c: f32, _d: f32, e: f32, f: f32| e * 10.0 + f);
264        let args: Vec<ExpressionVariant> = (1..=6).map(|v| (v as f64).into()).collect();
265        assert_eq!(call(&ffi, "F", &args[..5]), Some(54321.0.into()));
266        assert_eq!(call(&ffi, "F", &args), Some(55.0.into()));
267    }
268
269    #[test]
270    #[should_panic(expected = "cannot be a parameter")]
271    fn the_3x3_matrix_cannot_be_a_parameter() {
272        let mut ffi = DelegateExpressionFfi::new();
273        ffi.add1("F", |m: Matrix| m);
274    }
275
276    #[test]
277    fn is_shareable_between_threads() {
278        fn assert_send_sync<T: Send + Sync>() {}
279        assert_send_sync::<DelegateExpressionFfi>();
280    }
281}