tytanic_filter/eval/
func.rs

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