cel_interpreter/
magic.rs

1use crate::macros::{impl_conversions, impl_handler};
2use crate::resolvers::{AllArguments, Argument};
3use crate::{ExecutionError, FunctionContext, ResolveResult, Value};
4use cel_parser::Expression;
5use std::collections::HashMap;
6use std::marker::PhantomData;
7use std::sync::Arc;
8
9impl_conversions!(
10    i64 => Value::Int,
11    u64 => Value::UInt,
12    f64 => Value::Float,
13    Arc<String> => Value::String,
14    Arc<Vec<u8>> => Value::Bytes,
15    bool => Value::Bool,
16    Arc<Vec<Value>> => Value::List
17);
18
19#[cfg(feature = "chrono")]
20impl_conversions!(
21    chrono::Duration => Value::Duration,
22    chrono::DateTime<chrono::FixedOffset> => Value::Timestamp,
23);
24
25impl From<i32> for Value {
26    fn from(value: i32) -> Self {
27        Value::Int(value as i64)
28    }
29}
30
31/// Describes any type that can be converted from a [`Value`] into itself.
32/// This is commonly used to convert from [`Value`] into primitive types,
33/// e.g. from `Value::Bool(true) -> true`. This trait is auto-implemented
34/// for many CEL-primitive types.
35trait FromValue {
36    fn from_value(value: &Value) -> Result<Self, ExecutionError>
37    where
38        Self: Sized;
39}
40
41impl FromValue for Value {
42    fn from_value(value: &Value) -> Result<Self, ExecutionError>
43    where
44        Self: Sized,
45    {
46        Ok(value.clone())
47    }
48}
49
50/// A trait for types that can be converted into a [`ResolveResult`]. Every function that can
51/// be registered to the CEL context must return a value that implements this trait.
52pub trait IntoResolveResult {
53    fn into_resolve_result(self) -> ResolveResult;
54}
55
56impl IntoResolveResult for String {
57    fn into_resolve_result(self) -> ResolveResult {
58        Ok(Value::String(Arc::new(self)))
59    }
60}
61
62impl IntoResolveResult for Result<Value, ExecutionError> {
63    fn into_resolve_result(self) -> ResolveResult {
64        self
65    }
66}
67
68/// Describes any type that can be converted from a [`FunctionContext`] into
69/// itself, for example CEL primitives implement this trait to allow them to
70/// be used as arguments to functions. This trait is core to the 'magic function
71/// parameter' system. Every argument to a function that can be registered to
72/// the CEL context must implement this type.
73pub(crate) trait FromContext<'a, 'context> {
74    fn from_context(ctx: &'a mut FunctionContext<'context>) -> Result<Self, ExecutionError>
75    where
76        Self: Sized;
77}
78
79/// A function argument abstraction enabling dynamic method invocation on a
80/// target instance or on the first argument if the function is not called
81/// as a method.
82///
83/// This is similar to how methods can be called as functions using the
84/// [fully-qualified syntax](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name).
85///
86/// # Using `This`
87/// ```
88/// # use std::sync::Arc;
89/// # use cel_interpreter::{Program, Context};
90/// use cel_interpreter::extractors::This;
91/// # let mut context = Context::default();
92/// # context.add_function("startsWith", starts_with);
93///
94/// /// Notice how `This` refers to the target value when called as a method,
95/// /// but the first argument when called as a function.
96/// let program1 = "'foobar'.startsWith('foo') == true";
97/// let program2 = "startsWith('foobar', 'foo') == true";
98/// # let program1 = Program::compile(program1).unwrap();
99/// # let program2 = Program::compile(program2).unwrap();
100/// # let value = program1.execute(&context).unwrap();
101/// # assert_eq!(value, true.into());
102/// # let value = program2.execute(&context).unwrap();
103/// # assert_eq!(value, true.into());
104///
105/// fn starts_with(This(this): This<Arc<String>>, prefix: Arc<String>) -> bool {
106///     this.starts_with(prefix.as_str())
107/// }
108/// ```
109///
110/// # Type of `This`
111/// This also accepts a type `T` which determines the specific type
112/// that's extracted. Any type that supports [`FromValue`] can be used.
113/// In the previous example, the method `startsWith` is only ever called
114/// on a string, so we can use `This<Rc<String>>` to extract the string
115/// automatically prior to our method actually being called.
116///
117/// In some cases, you may want access to the raw [`Value`] instead, for
118/// example, the `contains` method works for several different types. In these
119/// cases, you can use `This<Value>` to extract the raw value.
120///
121/// ```skip
122/// pub fn contains(This(this): This<Value>, arg: Value) -> Result<Value> {
123///     Ok(match this {
124///         Value::List(v) => v.contains(&arg),
125///         ...
126///     }
127/// }
128/// ```
129pub struct This<T>(pub T);
130
131impl<'a, 'context, T> FromContext<'a, 'context> for This<T>
132where
133    T: FromValue,
134{
135    fn from_context(ctx: &'a mut FunctionContext<'context>) -> Result<Self, ExecutionError>
136    where
137        Self: Sized,
138    {
139        if let Some(ref this) = ctx.this {
140            Ok(This(T::from_value(this)?))
141        } else {
142            let arg = arg_value_from_context(ctx)
143                .map_err(|_| ExecutionError::missing_argument_or_target())?;
144            Ok(This(T::from_value(&arg)?))
145        }
146    }
147}
148
149/// Identifier is an argument extractor that attempts to extract an identifier
150/// from an argument's expression.
151///
152/// It fails if the argument is not available, or if the argument cannot be
153/// converted into an expression.
154///
155/// # Examples
156/// Identifiers are useful for functions like `.map` or `.filter` where one
157/// of the arguments is the declaration of a variable. In this case, as noted
158/// below, the x is an identifier, and we want to be able to parse it
159/// automatically.
160///
161/// ```javascript
162/// //        Identifier
163/// //            ↓
164/// [1, 2, 3].map(x, x * 2) == [2, 4, 6]
165/// ```
166///
167/// The function signature for the Rust implementation of `map` looks like this
168///
169/// ```skip
170/// pub fn map(
171///     ftx: &FunctionContext,
172///     This(this): This<Value>, // <- [1, 2, 3]
173///     ident: Identifier,       // <- x
174///     expr: Expression,        // <- x * 2
175/// ) -> Result<Value>;
176/// ```
177#[derive(Clone)]
178pub struct Identifier(pub Arc<String>);
179
180impl<'a, 'context> FromContext<'a, 'context> for Identifier {
181    fn from_context(ctx: &'a mut FunctionContext<'context>) -> Result<Self, ExecutionError>
182    where
183        Self: Sized,
184    {
185        match arg_expr_from_context(ctx) {
186            Expression::Ident(ident) => Ok(Identifier(ident.clone())),
187            expr => Err(ExecutionError::UnexpectedType {
188                got: format!("{:?}", expr),
189                want: "identifier".to_string(),
190            }),
191        }
192    }
193}
194
195impl From<&Identifier> for String {
196    fn from(value: &Identifier) -> Self {
197        value.0.to_string()
198    }
199}
200
201impl From<Identifier> for String {
202    fn from(value: Identifier) -> Self {
203        value.0.as_ref().clone()
204    }
205}
206
207/// An argument extractor that extracts all the arguments passed to a function, resolves their
208/// expressions and returns a vector of [`Value`].
209///
210/// This is useful for functions that accept a variable number of arguments rather than known
211/// arguments and types (for example a `sum` function).
212///
213/// # Example
214/// ```javascript
215/// sum(1, 2.0, uint(3)) == 5.0
216/// ```
217///
218/// ```rust
219/// # use cel_interpreter::{Value};
220/// use cel_interpreter::extractors::Arguments;
221/// pub fn sum(Arguments(args): Arguments) -> Value {
222///     args.iter().fold(0.0, |acc, val| match val {
223///         Value::Int(x) => *x as f64 + acc,
224///         Value::UInt(x) => *x as f64 + acc,
225///         Value::Float(x) => *x + acc,
226///         _ => acc,
227///     }).into()
228/// }
229/// ```
230#[derive(Clone)]
231pub struct Arguments(pub Arc<Vec<Value>>);
232
233impl<'a> FromContext<'a, '_> for Arguments {
234    fn from_context(ctx: &'a mut FunctionContext) -> Result<Self, ExecutionError>
235    where
236        Self: Sized,
237    {
238        match ctx.resolve(AllArguments)? {
239            Value::List(list) => Ok(Arguments(list.clone())),
240            _ => todo!(),
241        }
242    }
243}
244
245impl<'a, 'context> FromContext<'a, 'context> for Value {
246    fn from_context(ctx: &'a mut FunctionContext<'context>) -> Result<Self, ExecutionError>
247    where
248        Self: Sized,
249    {
250        arg_value_from_context(ctx)
251    }
252}
253
254impl<'a, 'context> FromContext<'a, 'context> for Expression {
255    fn from_context(ctx: &'a mut FunctionContext<'context>) -> Result<Self, ExecutionError>
256    where
257        Self: Sized,
258    {
259        Ok(arg_expr_from_context(ctx))
260    }
261}
262
263/// Returns the next argument specified by the context's `arg_idx` field as an expression
264/// (i.e. not resolved). Calling this multiple times will increment the `arg_idx` which will
265/// return subsequent arguments every time.
266///
267/// Calling this function when there are no more arguments will result in a panic. Since this
268/// function is only ever called within the context of a controlled macro that calls it once
269/// for each argument, this should never happen.
270fn arg_expr_from_context(ctx: &mut FunctionContext) -> Expression {
271    let idx = ctx.arg_idx;
272    ctx.arg_idx += 1;
273    ctx.args[idx].clone()
274}
275
276/// Returns the next argument specified by the context's `arg_idx` field as after resolving
277/// it. Calling this multiple times will increment the `arg_idx` which will return subsequent
278/// arguments every time.
279///
280/// Calling this function when there are no more arguments will result in a panic. Since this
281/// function is only ever called within the context of a controlled macro that calls it once
282/// for each argument, this should never happen.
283fn arg_value_from_context(ctx: &mut FunctionContext) -> Result<Value, ExecutionError> {
284    let idx = ctx.arg_idx;
285    ctx.arg_idx += 1;
286    ctx.resolve(Argument(idx))
287}
288
289pub struct WithFunctionContext;
290
291impl_handler!();
292impl_handler!(C1);
293impl_handler!(C1, C2);
294impl_handler!(C1, C2, C3);
295impl_handler!(C1, C2, C3, C4);
296impl_handler!(C1, C2, C3, C4, C5);
297impl_handler!(C1, C2, C3, C4, C5, C6);
298impl_handler!(C1, C2, C3, C4, C5, C6, C7);
299impl_handler!(C1, C2, C3, C4, C5, C6, C7, C8);
300impl_handler!(C1, C2, C3, C4, C5, C6, C7, C8, C9);
301
302// Heavily inspired by https://users.rust-lang.org/t/common-data-type-for-functions-with-different-parameters-e-g-axum-route-handlers/90207/6
303// and https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=c6744c27c2358ec1d1196033a0ec11e4
304
305#[derive(Default)]
306pub struct FunctionRegistry {
307    functions: HashMap<String, Box<dyn Function>>,
308}
309
310impl FunctionRegistry {
311    pub(crate) fn add<H, T>(&mut self, name: &str, handler: H)
312    where
313        H: Handler<T> + 'static + Send + Sync,
314        T: 'static,
315    {
316        self.functions.insert(
317            name.to_string(),
318            Box::new(HandlerFunction {
319                handler,
320                into_callable: |h, ctx| Box::new(HandlerCallable::new(h, ctx)),
321            }),
322        );
323    }
324
325    pub(crate) fn get(&self, name: &str) -> Option<Box<dyn Function>> {
326        self.functions.get(name).map(|f| f.clone_box())
327    }
328
329    pub(crate) fn has(&self, name: &str) -> bool {
330        self.functions.contains_key(name)
331    }
332}
333
334pub trait Function: Send + Sync {
335    fn clone_box(&self) -> Box<dyn Function>;
336    fn into_callable<'a>(self: Box<Self>, ctx: &'a mut FunctionContext) -> Box<dyn Callable + 'a>;
337    fn call_with_context(self: Box<Self>, ctx: &mut FunctionContext) -> ResolveResult;
338}
339
340pub struct HandlerFunction<H: Clone + Send + Sync> {
341    pub handler: H,
342    pub into_callable: for<'a> fn(H, &'a mut FunctionContext) -> Box<dyn Callable + 'a>,
343}
344
345impl<H: Clone + Send + Sync> Clone for HandlerFunction<H> {
346    fn clone(&self) -> Self {
347        Self {
348            handler: self.handler.clone(),
349            into_callable: self.into_callable,
350        }
351    }
352}
353
354impl<H> Function for HandlerFunction<H>
355where
356    H: Clone + Send + Sync + 'static,
357{
358    fn clone_box(&self) -> Box<dyn Function> {
359        Box::new(self.clone())
360    }
361
362    fn into_callable<'a>(self: Box<Self>, ctx: &'a mut FunctionContext) -> Box<dyn Callable + 'a> {
363        (self.into_callable)(self.handler, ctx)
364    }
365
366    fn call_with_context(self: Box<Self>, ctx: &mut FunctionContext) -> ResolveResult {
367        self.into_callable(ctx).call()
368    }
369}
370
371// Callable and HandlerCallable
372pub trait Callable {
373    fn call(&mut self) -> ResolveResult;
374}
375
376pub struct HandlerCallable<'a, 'context, H, T> {
377    handler: H,
378    context: &'a mut FunctionContext<'context>,
379    _marker: PhantomData<fn() -> T>,
380}
381
382impl<'a, 'context, H, T> HandlerCallable<'a, 'context, H, T> {
383    pub fn new(handler: H, ctx: &'a mut FunctionContext<'context>) -> Self {
384        Self {
385            handler,
386            context: ctx,
387            _marker: PhantomData,
388        }
389    }
390}
391
392impl<H, T> Callable for HandlerCallable<'_, '_, H, T>
393where
394    H: Handler<T> + Clone + 'static,
395{
396    fn call(&mut self) -> ResolveResult {
397        self.handler.clone().call(self.context)
398    }
399}
400
401pub trait Handler<T>: Clone {
402    fn call(self, ctx: &mut FunctionContext) -> ResolveResult;
403}