cel/magic.rs
1use crate::macros::{impl_conversions, impl_handler};
2use crate::objects::Opaque;
3use crate::resolvers::{AllArguments, Argument};
4use crate::{ExecutionError, FunctionContext, ResolveResult, Value};
5use std::collections::BTreeMap;
6use std::sync::Arc;
7
8impl_conversions!(
9 i64 => Value::Int,
10 u64 => Value::UInt,
11 f64 => Value::Float,
12 Arc<String> => Value::String,
13 Arc<Vec<u8>> => Value::Bytes,
14 bool => Value::Bool,
15 Arc<Vec<Value>> => Value::List,
16 Arc<dyn Opaque> => Value::Opaque
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
31impl From<u32> for Value {
32 fn from(value: u32) -> Self {
33 Value::UInt(value as u64)
34 }
35}
36
37impl From<f32> for Value {
38 fn from(value: f32) -> Self {
39 Value::Float(value as f64)
40 }
41}
42
43/// Describes any type that can be converted from a [`Value`] into itself.
44/// This is commonly used to convert from [`Value`] into primitive types,
45/// e.g. from `Value::Bool(true) -> true`. This trait is auto-implemented
46/// for many CEL-primitive types.
47trait FromValue {
48 fn from_value(value: &Value) -> Result<Self, ExecutionError>
49 where
50 Self: Sized;
51}
52
53impl FromValue for Value {
54 fn from_value(value: &Value) -> Result<Self, ExecutionError>
55 where
56 Self: Sized,
57 {
58 Ok(value.clone())
59 }
60}
61
62/// A trait for types that can be converted into a [`ResolveResult`]. Every function that can
63/// be registered to the CEL context must return a value that implements this trait.
64pub trait IntoResolveResult {
65 fn into_resolve_result(self) -> ResolveResult;
66}
67
68impl IntoResolveResult for String {
69 fn into_resolve_result(self) -> ResolveResult {
70 Ok(Value::String(Arc::new(self)))
71 }
72}
73
74impl IntoResolveResult for Result<Value, ExecutionError> {
75 fn into_resolve_result(self) -> ResolveResult {
76 self
77 }
78}
79
80/// Describes any type that can be converted from a [`FunctionContext`] into
81/// itself, for example CEL primitives implement this trait to allow them to
82/// be used as arguments to functions. This trait is core to the 'magic function
83/// parameter' system. Every argument to a function that can be registered to
84/// the CEL context must implement this type.
85pub(crate) trait FromContext<'a, 'context, 'call> {
86 fn from_context(ctx: &'a mut FunctionContext<'context, 'call>) -> Result<Self, ExecutionError>
87 where
88 Self: Sized;
89}
90
91/// A function argument abstraction enabling dynamic method invocation on a
92/// target instance or on the first argument if the function is not called
93/// as a method.
94///
95/// This is similar to how methods can be called as functions using the
96/// [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).
97///
98/// # Using `This`
99/// ```
100/// # use std::sync::Arc;
101/// # use cel::{Program, Context};
102/// use cel::extractors::This;
103/// # let mut context = Context::default();
104/// # context.add_function("startsWith", starts_with);
105///
106/// /// Notice how `This` refers to the target value when called as a method,
107/// /// but the first argument when called as a function.
108/// let program1 = "'foobar'.startsWith('foo') == true";
109/// let program2 = "startsWith('foobar', 'foo') == true";
110/// # let program1 = Program::compile(program1).unwrap();
111/// # let program2 = Program::compile(program2).unwrap();
112/// # let value = program1.execute(&context).unwrap();
113/// # assert_eq!(value, true.into());
114/// # let value = program2.execute(&context).unwrap();
115/// # assert_eq!(value, true.into());
116///
117/// fn starts_with(This(this): This<Arc<String>>, prefix: Arc<String>) -> bool {
118/// this.starts_with(prefix.as_str())
119/// }
120/// ```
121///
122/// # Type of `This`
123/// This also accepts a type `T` which determines the specific type
124/// that's extracted. Any type that supports [`FromValue`] can be used.
125/// In the previous example, the method `startsWith` is only ever called
126/// on a string, so we can use `This<Rc<String>>` to extract the string
127/// automatically prior to our method actually being called.
128///
129/// In some cases, you may want access to the raw [`Value`] instead, for
130/// example, the `contains` method works for several different types. In these
131/// cases, you can use `This<Value>` to extract the raw value.
132///
133/// ```skip
134/// pub fn contains(This(this): This<Value>, arg: Value) -> Result<Value> {
135/// Ok(match this {
136/// Value::List(v) => v.contains(&arg),
137/// ...
138/// }
139/// }
140/// ```
141pub struct This<T>(pub T);
142
143impl<'a, 'context, 'call, T> FromContext<'a, 'context, 'call> for This<T>
144where
145 T: FromValue,
146{
147 fn from_context(ctx: &'a mut FunctionContext<'context, 'call>) -> Result<Self, ExecutionError>
148 where
149 Self: Sized,
150 {
151 if let Some(ref this) = ctx.this {
152 Ok(This(T::from_value(&this.as_ref().try_into()?)?))
153 } else {
154 let arg = arg_value_from_context(ctx)
155 .map_err(|_| ExecutionError::missing_argument_or_target())?;
156 Ok(This(T::from_value(&arg)?))
157 }
158 }
159}
160
161/// Identifier is an argument extractor that attempts to extract an identifier
162/// from an argument's expression.
163///
164/// It fails if the argument is not available, or if the argument cannot be
165/// converted into an expression.
166///
167/// # Examples
168/// Identifiers are useful for functions like `.map` or `.filter` where one
169/// of the arguments is the declaration of a variable. In this case, as noted
170/// below, the x is an identifier, and we want to be able to parse it
171/// automatically.
172///
173/// ```javascript
174/// // Identifier
175/// // ↓
176/// [1, 2, 3].map(x, x * 2) == [2, 4, 6]
177/// ```
178///
179/// The function signature for the Rust implementation of `map` looks like this
180///
181/// ```skip
182/// pub fn map(
183/// ftx: &FunctionContext,
184/// This(this): This<Value>, // <- [1, 2, 3]
185/// ident: Identifier, // <- x
186/// expr: Expression, // <- x * 2
187/// ) -> Result<Value>;
188/// ```
189#[derive(Clone)]
190pub struct Identifier(pub Arc<String>);
191
192impl From<&Identifier> for String {
193 fn from(value: &Identifier) -> Self {
194 value.0.to_string()
195 }
196}
197
198impl From<Identifier> for String {
199 fn from(value: Identifier) -> Self {
200 value.0.as_ref().clone()
201 }
202}
203
204/// An argument extractor that extracts all the arguments passed to a function, resolves their
205/// expressions and returns a vector of [`Value`].
206///
207/// This is useful for functions that accept a variable number of arguments rather than known
208/// arguments and types (for example a `sum` function).
209///
210/// # Example
211/// ```javascript
212/// sum(1, 2.0, uint(3)) == 5.0
213/// ```
214///
215/// ```rust
216/// # use cel::{Value};
217/// use cel::extractors::Arguments;
218/// pub fn sum(Arguments(args): Arguments) -> Value {
219/// args.iter().fold(0.0, |acc, val| match val {
220/// Value::Int(x) => *x as f64 + acc,
221/// Value::UInt(x) => *x as f64 + acc,
222/// Value::Float(x) => *x + acc,
223/// _ => acc,
224/// }).into()
225/// }
226/// ```
227#[derive(Clone)]
228pub struct Arguments(pub Arc<Vec<Value>>);
229
230impl<'a> FromContext<'a, '_, '_> for Arguments {
231 fn from_context(ctx: &'a mut FunctionContext) -> Result<Self, ExecutionError>
232 where
233 Self: Sized,
234 {
235 match ctx.resolve(AllArguments)? {
236 Value::List(list) => Ok(Arguments(list.clone())),
237 _ => todo!(),
238 }
239 }
240}
241
242impl<'a, 'context, 'call> FromContext<'a, 'context, 'call> for Value {
243 fn from_context(ctx: &'a mut FunctionContext<'context, 'call>) -> Result<Self, ExecutionError>
244 where
245 Self: Sized,
246 {
247 arg_value_from_context(ctx)
248 }
249}
250
251/// Returns the next argument specified by the context's `arg_idx` field as after resolving
252/// it. Calling this multiple times will increment the `arg_idx` which will return subsequent
253/// arguments every time.
254///
255/// Calling this function when there are no more arguments will result in a panic. Since this
256/// function is only ever called within the context of a controlled macro that calls it once
257/// for each argument, this should never happen.
258fn arg_value_from_context(ctx: &mut FunctionContext) -> Result<Value, ExecutionError> {
259 let idx = ctx.arg_idx;
260 ctx.arg_idx += 1;
261 ctx.resolve(Argument(idx))
262}
263
264pub struct WithFunctionContext;
265
266impl_handler!();
267impl_handler!(C1);
268impl_handler!(C1, C2);
269impl_handler!(C1, C2, C3);
270impl_handler!(C1, C2, C3, C4);
271impl_handler!(C1, C2, C3, C4, C5);
272impl_handler!(C1, C2, C3, C4, C5, C6);
273impl_handler!(C1, C2, C3, C4, C5, C6, C7);
274impl_handler!(C1, C2, C3, C4, C5, C6, C7, C8);
275impl_handler!(C1, C2, C3, C4, C5, C6, C7, C8, C9);
276
277// Heavily inspired by https://users.rust-lang.org/t/common-data-type-for-functions-with-different-parameters-e-g-axum-route-handlers/90207/6
278// and https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=c6744c27c2358ec1d1196033a0ec11e4
279
280#[derive(Default)]
281pub struct FunctionRegistry {
282 functions: BTreeMap<String, Function>,
283}
284
285impl FunctionRegistry {
286 pub(crate) fn add<F, T>(&mut self, name: &str, function: F)
287 where
288 F: IntoFunction<T> + 'static + Send + Sync,
289 T: 'static,
290 {
291 self.functions
292 .insert(name.to_string(), function.into_function());
293 }
294
295 #[allow(dead_code)]
296 pub(crate) fn get(&self, name: &str) -> Option<&Function> {
297 self.functions.get(name)
298 }
299}
300
301pub type Function = Box<dyn Fn(&mut FunctionContext) -> ResolveResult + Send + Sync>;
302
303pub trait IntoFunction<T> {
304 fn into_function(self) -> Function;
305}
306
307impl IntoFunction<Function> for Function {
308 fn into_function(self) -> Function {
309 self
310 }
311}