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}