cel_cxx/function/
registry.rs

1//! Function registry for managing registered functions.
2//!
3//! This module provides the [`FunctionRegistry`] type which serves as the
4//! central repository for all functions available in a CEL environment.
5//! It handles function registration, lookup, and overload management.
6//!
7//! # Features
8//!
9//! - **Function registration**: Add new function implementations
10//! - **Declaration support**: Register type signatures without implementations
11//! - **Overload management**: Handle multiple signatures for the same function name
12//! - **Efficient lookup**: Fast function resolution during expression evaluation
13//!
14//! # Thread Safety
15//!
16//! The registry is designed to be thread-safe and can be shared across
17//! multiple evaluation contexts.
18
19use super::*;
20use std::collections::HashMap;
21
22/// Compile-time function registry.
23///
24/// `FunctionRegistry` manages function declarations and implementations during the CEL environment
25/// compilation phase. It maintains a mapping from function names to function overloads, where each
26/// overload set can contain multiple function implementations with different signatures.
27///
28/// The registry supports two types of functions:
29///
30/// - **Function declarations**: Type signatures only for compile-time checking
31/// - **Function implementations**: Callable code for runtime execution
32///
33/// # Function Types
34///
35/// Functions can be registered as either:
36///
37/// - **Global functions**: Callable from any context
38/// - **Member functions**: Called as methods on values (e.g., `value.method()`)
39///
40/// # Lifetime Parameters
41///
42/// - `'f`: Lifetime of function implementations, allowing closures valid within specific scopes
43///
44/// # Examples
45///
46/// ```rust,no_run
47/// use cel_cxx::{FunctionRegistry, Error};
48///
49/// let mut registry = FunctionRegistry::new();
50///
51/// // Register function implementations
52/// fn add(a: i64, b: i64) -> Result<i64, Error> {
53///     Ok(a + b)
54/// }
55/// registry.register_global("add", add)?;
56///
57/// // Register member functions
58/// fn string_length(s: String) -> Result<i64, Error> {
59///     Ok(s.len() as i64)
60/// }
61/// registry.register_member("length", string_length)?;
62///
63/// // Register function declarations (type signatures only)
64/// registry.declare_global::<fn(String) -> Result<bool, Error>>("is_valid")?;
65///
66/// assert_eq!(registry.len(), 3);
67/// # Ok::<(), cel_cxx::Error>(())
68/// ```
69#[derive(Debug, Default)]
70pub struct FunctionRegistry<'f> {
71    entries: HashMap<String, FunctionOverloads<FunctionDeclOrImpl<'f>>>,
72}
73
74impl<'f> FunctionRegistry<'f> {
75    /// Creates a new empty function registry.
76    pub fn new() -> Self {
77        Self {
78            entries: HashMap::new(),
79        }
80    }
81}
82
83impl<'f> FunctionRegistry<'f> {
84    /// Registers a function implementation with zero-annotation type inference.
85    ///
86    /// Registers a callable function that can be invoked during CEL expression evaluation.
87    /// The function signature is automatically inferred from the Rust function type, eliminating
88    /// the need for manual type annotations.
89    ///
90    /// # Zero-Annotation Features
91    ///
92    /// - **Automatic type inference**: Function signature extracted from Rust function type
93    /// - **Lifetime handling**: Safe conversion of borrowed arguments like `&str`
94    /// - **Error conversion**: Automatic conversion of `Result<T, E>` return types
95    /// - **Async support**: Seamless handling of both sync and async functions
96    ///
97    /// # Type Parameters
98    ///
99    /// - `F`: Function type, must implement [`IntoFunction`]
100    /// - `Fm`: Function marker (sync or async), automatically inferred
101    /// - `Args`: Argument tuple type, automatically inferred from function signature
102    ///
103    /// # Parameters
104    ///
105    /// - `name`: Function name
106    /// - `member`: Whether this is a member function (true) or global function (false)
107    /// - `f`: Function implementation
108    ///
109    /// # Returns
110    ///
111    /// Returns `&mut Self` to support method chaining
112    ///
113    /// # Examples
114    ///
115    /// ## Basic functions with automatic type inference
116    ///
117    /// ```rust,no_run
118    /// use cel_cxx::FunctionRegistry;
119    ///
120    /// let mut registry = FunctionRegistry::new();
121    ///
122    /// // Zero-annotation function registration
123    /// registry.register("add", false, |a: i64, b: i64| a + b)?;
124    /// registry.register("greet", false, |name: &str| format!("Hello, {}!", name))?;
125    /// registry.register("is_empty", true, |s: String| s.is_empty())?;
126    /// # Ok::<(), cel_cxx::Error>(())
127    /// ```
128    ///
129    /// ## Error handling with automatic conversion
130    ///
131    /// ```rust,no_run
132    /// use cel_cxx::{FunctionRegistry, Error};
133    ///
134    /// let mut registry = FunctionRegistry::new();
135    ///
136    /// // Functions returning Result are automatically handled
137    /// registry.register("divide", false, |a: i64, b: i64| -> Result<i64, Error> {
138    ///     if b == 0 {
139    ///         Err(Error::invalid_argument("Division by zero"))
140    ///     } else {
141    ///         Ok(a / b)
142    ///     }
143    /// })?;
144    /// # Ok::<(), cel_cxx::Error>(())
145    /// ```
146    pub fn register<F, Fm, Args>(
147        &mut self,
148        name: impl Into<String>,
149        member: bool,
150        f: F,
151    ) -> Result<&mut Self, Error>
152    where
153        F: IntoFunction<'f, Fm, Args>,
154        Fm: FnMarker,
155        Args: Arguments,
156    {
157        let name = name.into();
158        let entry = self
159            .entries
160            .entry(name)
161            .or_insert_with(FunctionOverloads::new);
162        entry.add_impl(member, f.into_function())?;
163        Ok(self)
164    }
165
166    /// Registers a member function implementation.
167    ///
168    /// Convenience method for registering member functions that can be called as methods
169    /// on values (e.g., `"hello".length()`).
170    ///
171    /// # Examples
172    ///
173    /// ```rust,no_run
174    /// use cel_cxx::{FunctionRegistry, Error};
175    ///
176    /// let mut registry = FunctionRegistry::new();
177    ///
178    /// fn to_upper(s: String) -> Result<String, Error> {
179    ///     Ok(s.to_uppercase())
180    /// }
181    ///
182    /// registry.register_member("upper", to_upper)?;
183    /// # Ok::<(), cel_cxx::Error>(())
184    /// ```
185    pub fn register_member<F, Fm, Args>(
186        &mut self,
187        name: impl Into<String>,
188        f: F,
189    ) -> Result<&mut Self, Error>
190    where
191        F: IntoFunction<'f, Fm, Args>,
192        Fm: FnMarker,
193        Args: Arguments + NonEmptyArguments,
194    {
195        self.register(name, true, f)
196    }
197
198    /// Registers a global function implementation.
199    ///
200    /// Convenience method for registering global functions that can be called from any context.
201    ///
202    /// # Examples
203    ///
204    /// ```rust,no_run
205    /// use cel_cxx::{FunctionRegistry, Error};
206    ///
207    /// let mut registry = FunctionRegistry::new();
208    ///
209    /// fn max(a: i64, b: i64) -> Result<i64, Error> {
210    ///     Ok(a.max(b))
211    /// }
212    ///
213    /// registry.register_global("max", max)?;
214    /// # Ok::<(), cel_cxx::Error>(())
215    /// ```
216    pub fn register_global<F, Fm, Args>(
217        &mut self,
218        name: impl Into<String>,
219        f: F,
220    ) -> Result<&mut Self, Error>
221    where
222        F: IntoFunction<'f, Fm, Args>,
223        Fm: FnMarker,
224        Args: Arguments,
225    {
226        self.register(name, false, f)
227    }
228
229    /// Declares a function signature.
230    ///
231    /// Declares a function type signature for compile-time type checking without providing
232    /// an implementation. The function signature is determined by the generic parameter `D`
233    /// which must implement [`FunctionDecl`].
234    ///
235    /// # Type Parameters
236    ///
237    /// - `D`: Function declaration type, must implement [`FunctionDecl`]
238    ///
239    /// # Parameters
240    ///
241    /// - `name`: Function name
242    /// - `member`: Whether this is a member function (true) or global function (false)
243    ///
244    /// # Returns
245    ///
246    /// Returns `&mut Self` to support method chaining
247    ///
248    /// # Examples
249    ///
250    /// ```rust,no_run
251    /// use cel_cxx::{FunctionRegistry, Error};
252    ///
253    /// let mut registry = FunctionRegistry::new();
254    ///
255    /// // Declare global function signature
256    /// registry.declare::<fn(String) -> Result<bool, Error>>("validate", false)?;
257    ///
258    /// // Declare member function signature  
259    /// registry.declare::<fn(String) -> Result<i64, Error>>("size", true)?;
260    /// # Ok::<(), cel_cxx::Error>(())
261    /// ```
262    pub fn declare<D>(&mut self, name: impl Into<String>, member: bool) -> Result<&mut Self, Error>
263    where
264        D: FunctionDecl,
265    {
266        let name = name.into();
267        if member && D::ARGUMENTS_LEN == 0 {
268            return Err(Error::invalid_argument("Member functions cannot have zero arguments"));
269        }
270        let entry = self
271            .entries
272            .entry(name)
273            .or_insert_with(FunctionOverloads::new);
274        entry.add_decl(member, D::function_type())?;
275        Ok(self)
276    }
277
278    /// Declares a member function signature.
279    ///
280    /// Convenience method for declaring member function signatures for compile-time
281    /// type checking without providing implementations.
282    ///
283    /// # Type Parameters
284    ///
285    /// - `D`: Function declaration type, must implement [`FunctionDecl`] and [`FunctionDeclWithNonEmptyArguments`]
286    ///
287    /// # Parameters
288    ///
289    /// - `name`: Function name
290    ///
291    /// # Returns
292    ///
293    /// Returns `&mut Self` to support method chaining
294    ///
295    /// # Examples
296    ///
297    /// ```rust,no_run
298    /// use cel_cxx::{FunctionRegistry, Error};
299    ///
300    /// let mut registry = FunctionRegistry::new();
301    ///
302    /// // Declare member function signature
303    /// registry.declare_member::<fn(String) -> i64>("hash")?;
304    /// # Ok::<(), cel_cxx::Error>(())
305    /// ```
306    pub fn declare_member<D>(&mut self, name: impl Into<String>) -> Result<&mut Self, Error>
307    where
308        D: FunctionDecl + FunctionDeclWithNonEmptyArguments,
309    {
310        self.declare::<D>(name, true)
311    }
312
313    /// Declares a global function signature.
314    ///
315    /// Convenience method for declaring global function signatures for compile-time
316    /// type checking without providing implementations.
317    ///
318    /// # Type Parameters
319    ///
320    /// - `D`: Function declaration type, must implement [`FunctionDecl`]
321    ///
322    /// # Parameters
323    ///
324    /// - `name`: Function name
325    ///
326    /// # Returns
327    ///
328    /// Returns `&mut Self` to support method chaining
329    ///
330    /// # Examples
331    ///
332    /// ```rust,no_run
333    /// use cel_cxx::{FunctionRegistry, Error};
334    ///
335    /// let mut registry = FunctionRegistry::new();
336    ///
337    /// // Declare global function signature
338    /// registry.declare_global::<fn(String, String) -> Result<String, Error>>("concat")?;
339    /// # Ok::<(), cel_cxx::Error>(())
340    /// ```
341    pub fn declare_global<D>(&mut self, name: impl Into<String>) -> Result<&mut Self, Error>
342    where
343        D: FunctionDecl,
344    {
345        self.declare::<D>(name, false)
346    }
347
348    /// Finds function overloads by name.
349    ///
350    /// # Parameters
351    ///
352    /// - `name`: Function name to search for
353    ///
354    /// # Returns
355    ///
356    /// `Some(&FunctionOverloads)` if found, `None` if not found
357    pub fn find(&self, name: &str) -> Option<&FunctionOverloads<FunctionDeclOrImpl<'f>>> {
358        self.entries.get(name)
359    }
360
361    /// Finds function overloads by name (mutable).
362    ///
363    /// # Parameters
364    ///
365    /// - `name`: Function name to search for
366    ///
367    /// # Returns
368    ///
369    /// `Some(&mut FunctionOverloads)` if found, `None` if not found
370    pub fn find_mut(
371        &mut self,
372        name: &str,
373    ) -> Option<&mut FunctionOverloads<FunctionDeclOrImpl<'f>>> {
374        self.entries.get_mut(name)
375    }
376
377    /// Returns an iterator over all function entries.
378    ///
379    /// # Returns
380    ///
381    /// Iterator yielding `(&str, &FunctionOverloads)` pairs
382    pub fn entries(
383        &self,
384    ) -> impl Iterator<Item = (&str, &FunctionOverloads<FunctionDeclOrImpl<'f>>)> {
385        self.entries.iter().map(|(k, v)| (k.as_str(), v))
386    }
387
388    /// Returns a mutable iterator over all function entries.
389    ///
390    /// # Returns
391    ///
392    /// Iterator yielding `(&str, &mut FunctionOverloads)` pairs
393    pub fn entries_mut(
394        &mut self,
395    ) -> impl Iterator<Item = (&str, &mut FunctionOverloads<FunctionDeclOrImpl<'f>>)> {
396        self.entries.iter_mut().map(|(k, v)| (k.as_str(), v))
397    }
398
399    /// Removes all overloads for a function name.
400    ///
401    /// # Parameters
402    ///
403    /// - `name`: Function name to remove
404    ///
405    /// # Returns
406    ///
407    /// `Ok(())` if removal was successful, `Err(Error)` if function not found
408    pub fn remove(&mut self, name: &str) -> Result<(), Error> {
409        self.entries
410            .remove(name)
411            .ok_or_else(|| Error::not_found(format!("Function '{name}' not found")))?;
412        Ok(())
413    }
414
415    /// Clears all registered functions.
416    pub fn clear(&mut self) {
417        self.entries.clear();
418    }
419
420    /// Returns the number of registered function names.
421    ///
422    /// Note: This counts function names, not individual overloads.
423    ///
424    /// # Returns
425    ///
426    /// Number of registered function names
427    pub fn len(&self) -> usize {
428        self.entries.len()
429    }
430
431    /// Returns whether the registry is empty.
432    ///
433    /// # Returns
434    ///
435    /// `true` if no functions are registered, `false` otherwise
436    pub fn is_empty(&self) -> bool {
437        self.entries.is_empty()
438    }
439}
440
441#[allow(dead_code)]
442#[allow(unused_variables)]
443#[allow(unused_imports)]
444#[cfg(test)]
445mod test {
446    use crate::{
447        types::{OpaqueType, ValueType},
448        values::OpaqueValue,
449        values::Optional,
450        values::{TypedOpaque, Value},
451        Error, Opaque,
452    };
453    use std::{
454        collections::{BTreeMap, HashMap},
455        sync::Arc,
456    };
457
458    use super::*;
459
460    fn f1(v: i64) -> Result<i64, Error> {
461        Ok(5)
462    }
463    fn f11(a1: i64, a2: i64) -> Result<i64, Error> {
464        Ok(5)
465    }
466
467    fn f2(a1: HashMap<i64, i64>) -> Result<i64, Error> {
468        Ok(5)
469    }
470    fn f3(a1: String) -> Result<i64, Error> {
471        Ok(5)
472    }
473    fn f4(a1: String, a2: String) -> Result<i64, Error> {
474        Ok(5)
475    }
476    fn f5(a1: String, a2: String, a3: HashMap<u64, Vec<Vec<i64>>>) -> Result<i64, Error> {
477        Ok(5)
478    }
479
480    #[cfg(feature = "derive")]
481    #[derive(Opaque, Debug, Clone, PartialEq)]
482    #[cel_cxx(type = "MyOpaque", crate = crate)]
483    #[cel_cxx(display)]
484    struct MyOpaque(pub i64);
485
486    #[cfg(feature = "derive")]
487    fn f6(a1: String, a2: MyOpaque, a3: HashMap<u64, Vec<Vec<i64>>>) -> Result<i64, Error> {
488        Ok(5)
489    }
490
491    fn f7(a1: &str) -> Result<(), Error> {
492        Ok(())
493    }
494
495    #[cfg(feature = "derive")]
496    fn f8(a1: &MyOpaque) -> &MyOpaque {
497        a1
498    }
499
500    #[cfg(feature = "derive")]
501    fn f9(a1: Optional<&MyOpaque>) -> Result<Option<&MyOpaque>, Error> {
502        Ok(a1.into_option())
503    }
504
505    async fn async_f1(a1: String, a2: i64, a3: Option<String>) -> Result<i64, Error> {
506        Ok(5)
507    }
508
509    fn f_map1(a1: HashMap<String, Vec<u8>>) -> Result<(), Error> {
510        Ok(())
511    }
512    fn f_map2(a1: HashMap<&str, &[u8]>) -> Result<(), Error> {
513        Ok(())
514    }
515    fn f_map3<'a>(a1: HashMap<&'a str, &'a str>) -> Result<BTreeMap<&'a str, &'a str>, Error> {
516        Ok(BTreeMap::from_iter(a1))
517    }
518
519    #[test]
520    fn test_register() -> Result<(), Error> {
521        let n = 100;
522        let lifetime_closure = |a: i64, b: i64| Ok::<_, Error>(a + b + n);
523        let mut registry = FunctionRegistry::new();
524        registry
525            .register_member("f1", f1)?
526            .register_member("f11", f11)?
527            .register_member("f2", f2)?
528            .register_member("f3", f3)?
529            .register_member("f4", f4)?
530            .register_member("f5", f5)?
531            .register_global("lifetime_closure", lifetime_closure)?
532            .register_global("f7", f7)?
533            .register_global("f_map1", f_map1)?
534            .register_global("f_map2", f_map2)?
535            .register_global("f_map3", f_map3)?;
536
537        #[cfg(feature = "derive")]
538        registry
539            .register_global("f6", f6)?
540            .register_global("f8", f8)?
541            .register_global("f9", f9)?;
542
543        #[cfg(feature = "async")]
544        #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
545        let registry = registry.register_global("async_f1", async_f1)?;
546
547        //let variable = VariableDecl::builder()
548        //    .add::<i64, _>("v1")
549        //    .build(None);
550        //let env = Env::new(function, variable);
551
552        #[cfg(feature = "derive")]
553        {
554            let x = MyOpaque(1);
555            let y: Box<dyn Opaque> = Box::new(x);
556
557            let value: OpaqueValue = Box::new(MyOpaque(1));
558            assert!(value.is::<MyOpaque>());
559            assert!(value.downcast_ref::<MyOpaque>().is_some());
560            let value2 = value.clone().downcast::<MyOpaque>();
561            assert!(value2.is_ok());
562
563            let value3 = value.clone();
564            assert!(value3.is::<MyOpaque>());
565
566            let value4: OpaqueValue = Box::new(MyOpaque(1));
567            assert!(value4.is::<MyOpaque>());
568            assert!(value4.clone().downcast::<MyOpaque>().is_ok());
569            let value5 = value4.downcast::<MyOpaque>();
570            assert!(value5.is_ok());
571        }
572
573        Ok(())
574    }
575}