tytanic_filter/eval/
func.rs

1use std::fmt::{self, Debug};
2use std::sync::Arc;
3
4use ecow::eco_vec;
5
6use super::{Context, Error, TryFromValue, Type, Value};
7
8/// The backing implementation for a [`Func`].
9type FuncImpl<T> =
10    Arc<dyn Fn(&Context<T>, &[Value<T>]) -> Result<Value<T>, Error> + Send + Sync + 'static>;
11
12/// A function value, this can be called with a set of positional arguments to
13/// produce a value. This is most commonly used as a constructor for tests sets.
14#[derive(Clone)]
15pub struct Func<T>(FuncImpl<T>);
16
17impl<T> Func<T> {
18    /// Create a new function with the given implementation.
19    pub fn new<F>(f: F) -> Self
20    where
21        F: Fn(&Context<T>, &[Value<T>]) -> Result<Value<T>, Error> + Send + Sync + 'static,
22    {
23        Self(Arc::new(f) as _)
24    }
25
26    /// Call the given function with the given context and arguments.
27    pub fn call(&self, ctx: &Context<T>, args: &[Value<T>]) -> Result<Value<T>, Error> {
28        (self.0)(ctx, args)
29    }
30}
31
32impl<T> Debug for Func<T> {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        f.debug_tuple("Func").field(&..).finish()
35    }
36}
37
38impl<T> Func<T> {
39    /// Ensure there are no args.
40    pub fn expect_no_args(id: &str, _ctx: &Context<T>, args: &[Value<T>]) -> Result<(), Error> {
41        if args.is_empty() {
42            Ok(())
43        } else {
44            Err(Error::InvalidArgumentCount {
45                func: id.into(),
46                expected: 0,
47                is_min: false,
48                found: args.len(),
49            })
50        }
51    }
52
53    /// Extract an exact number of values from the given arguments. Validates the
54    /// types of all arguments.
55    pub fn expect_args_exact<V: TryFromValue<T> + Debug, const N: usize>(
56        func: &str,
57        _ctx: &Context<T>,
58        args: &[Value<T>],
59    ) -> Result<[V; N], Error>
60    where
61        T: Clone,
62    {
63        if args.len() < N {
64            return Err(Error::InvalidArgumentCount {
65                func: func.into(),
66                expected: N,
67                is_min: false,
68                found: args.len(),
69            });
70        }
71
72        Ok(args
73            .iter()
74            .take(N)
75            .cloned()
76            .map(V::try_from_value)
77            .collect::<Result<Vec<_>, _>>()?
78            .try_into()
79            .expect("we checked both min and max of the args"))
80    }
81
82    /// Extract a variadic number of values with a minimum amount given arguments.
83    /// Validates the types of all arguments.
84    pub fn expect_args_min<V: TryFromValue<T> + Debug, const N: usize>(
85        func: &str,
86        _ctx: &Context<T>,
87        args: &[Value<T>],
88    ) -> Result<([V; N], Vec<V>), Error>
89    where
90        T: Clone,
91    {
92        if args.len() < N {
93            return Err(Error::InvalidArgumentCount {
94                func: func.into(),
95                expected: N,
96                is_min: true,
97                found: args.len(),
98            });
99        }
100
101        let min = args
102            .iter()
103            .take(N)
104            .cloned()
105            .map(V::try_from_value)
106            .collect::<Result<Vec<_>, _>>()?
107            .try_into()
108            .expect("we checked both min and max of the args");
109
110        Ok((
111            min,
112            args[N..]
113                .iter()
114                .cloned()
115                .map(V::try_from_value)
116                .collect::<Result<_, _>>()?,
117        ))
118    }
119}
120
121impl<T> TryFromValue<T> for Func<T> {
122    fn try_from_value(value: Value<T>) -> Result<Self, Error> {
123        Ok(match value {
124            Value::Func(set) => set,
125            _ => {
126                return Err(Error::TypeMismatch {
127                    expected: eco_vec![Type::Func],
128                    found: value.as_type(),
129                })
130            }
131        })
132    }
133}
134
135/// Ensure Func<T> is thread safe if T is.
136#[allow(dead_code)]
137fn assert_traits() {
138    tytanic_utils::assert::send::<Func<()>>();
139    tytanic_utils::assert::sync::<Func<()>>();
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145    use crate::ast::Num;
146
147    const NUM: Num = Num(0);
148    const VAL: Value<()> = Value::Num(NUM);
149
150    #[test]
151    fn test_expect_args_variadic_min_length() {
152        let ctx = Context::new();
153
154        assert_eq!(
155            Func::expect_args_min::<Num, 0>("f", &ctx, &[]).unwrap(),
156            ([], vec![]),
157        );
158        assert_eq!(
159            Func::expect_args_min("f", &ctx, &[VAL]).unwrap(),
160            ([], vec![NUM]),
161        );
162        assert_eq!(
163            Func::expect_args_min("f", &ctx, &[VAL, VAL]).unwrap(),
164            ([], vec![NUM, NUM]),
165        );
166
167        assert!(Func::expect_args_min::<Num, 1>("f", &ctx, &[]).is_err());
168        assert_eq!(
169            Func::expect_args_min("f", &ctx, &[VAL]).unwrap(),
170            ([NUM], vec![]),
171        );
172        assert_eq!(
173            Func::expect_args_min("f", &ctx, &[VAL, VAL]).unwrap(),
174            ([NUM], vec![NUM]),
175        );
176
177        assert!(Func::expect_args_min::<Num, 2>("f", &ctx, &[]).is_err());
178        assert!(Func::expect_args_min::<Num, 2>("f", &ctx, &[VAL]).is_err(),);
179        assert_eq!(
180            Func::expect_args_min("f", &ctx, &[VAL, VAL]).unwrap(),
181            ([NUM, NUM], vec![]),
182        );
183    }
184}