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,
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 let entry = self
268 .entries
269 .entry(name)
270 .or_insert_with(FunctionOverloads::new);
271 entry.add_decl(member, D::function_type())?;
272 Ok(self)
273 }
274
275 /// Declares a member function signature.
276 ///
277 /// Convenience method for declaring member function signatures for compile-time
278 /// type checking without providing implementations.
279 ///
280 /// # Type Parameters
281 ///
282 /// - `D`: Function declaration type, must implement [`FunctionDecl`]
283 ///
284 /// # Parameters
285 ///
286 /// - `name`: Function name
287 ///
288 /// # Returns
289 ///
290 /// Returns `&mut Self` to support method chaining
291 ///
292 /// # Examples
293 ///
294 /// ```rust,no_run
295 /// use cel_cxx::{FunctionRegistry, Error};
296 ///
297 /// let mut registry = FunctionRegistry::new();
298 ///
299 /// // Declare member function signature
300 /// registry.declare_member::<fn(String) -> i64>("hash")?;
301 /// # Ok::<(), cel_cxx::Error>(())
302 /// ```
303 pub fn declare_member<D>(&mut self, name: impl Into<String>) -> Result<&mut Self, Error>
304 where
305 D: FunctionDecl,
306 {
307 self.declare::<D>(name, true)
308 }
309
310 /// Declares a global function signature.
311 ///
312 /// Convenience method for declaring global function signatures for compile-time
313 /// type checking without providing implementations.
314 ///
315 /// # Type Parameters
316 ///
317 /// - `D`: Function declaration type, must implement [`FunctionDecl`]
318 ///
319 /// # Parameters
320 ///
321 /// - `name`: Function name
322 ///
323 /// # Returns
324 ///
325 /// Returns `&mut Self` to support method chaining
326 ///
327 /// # Examples
328 ///
329 /// ```rust,no_run
330 /// use cel_cxx::{FunctionRegistry, Error};
331 ///
332 /// let mut registry = FunctionRegistry::new();
333 ///
334 /// // Declare global function signature
335 /// registry.declare_global::<fn(String, String) -> Result<String, Error>>("concat")?;
336 /// # Ok::<(), cel_cxx::Error>(())
337 /// ```
338 pub fn declare_global<D>(&mut self, name: impl Into<String>) -> Result<&mut Self, Error>
339 where
340 D: FunctionDecl,
341 {
342 self.declare::<D>(name, false)
343 }
344
345 /// Finds function overloads by name.
346 ///
347 /// # Parameters
348 ///
349 /// - `name`: Function name to search for
350 ///
351 /// # Returns
352 ///
353 /// `Some(&FunctionOverloads)` if found, `None` if not found
354 pub fn find(&self, name: &str) -> Option<&FunctionOverloads<FunctionDeclOrImpl<'f>>> {
355 self.entries.get(name)
356 }
357
358 /// Finds function overloads by name (mutable).
359 ///
360 /// # Parameters
361 ///
362 /// - `name`: Function name to search for
363 ///
364 /// # Returns
365 ///
366 /// `Some(&mut FunctionOverloads)` if found, `None` if not found
367 pub fn find_mut(
368 &mut self,
369 name: &str,
370 ) -> Option<&mut FunctionOverloads<FunctionDeclOrImpl<'f>>> {
371 self.entries.get_mut(name)
372 }
373
374 /// Returns an iterator over all function entries.
375 ///
376 /// # Returns
377 ///
378 /// Iterator yielding `(&str, &FunctionOverloads)` pairs
379 pub fn entries(
380 &self,
381 ) -> impl Iterator<Item = (&str, &FunctionOverloads<FunctionDeclOrImpl<'f>>)> {
382 self.entries.iter().map(|(k, v)| (k.as_str(), v))
383 }
384
385 /// Returns a mutable iterator over all function entries.
386 ///
387 /// # Returns
388 ///
389 /// Iterator yielding `(&str, &mut FunctionOverloads)` pairs
390 pub fn entries_mut(
391 &mut self,
392 ) -> impl Iterator<Item = (&str, &mut FunctionOverloads<FunctionDeclOrImpl<'f>>)> {
393 self.entries.iter_mut().map(|(k, v)| (k.as_str(), v))
394 }
395
396 /// Removes all overloads for a function name.
397 ///
398 /// # Parameters
399 ///
400 /// - `name`: Function name to remove
401 ///
402 /// # Returns
403 ///
404 /// `Ok(())` if removal was successful, `Err(Error)` if function not found
405 pub fn remove(&mut self, name: &str) -> Result<(), Error> {
406 self.entries
407 .remove(name)
408 .ok_or_else(|| Error::not_found(format!("Function '{name}' not found")))?;
409 Ok(())
410 }
411
412 /// Clears all registered functions.
413 pub fn clear(&mut self) {
414 self.entries.clear();
415 }
416
417 /// Returns the number of registered function names.
418 ///
419 /// Note: This counts function names, not individual overloads.
420 ///
421 /// # Returns
422 ///
423 /// Number of registered function names
424 pub fn len(&self) -> usize {
425 self.entries.len()
426 }
427
428 /// Returns whether the registry is empty.
429 ///
430 /// # Returns
431 ///
432 /// `true` if no functions are registered, `false` otherwise
433 pub fn is_empty(&self) -> bool {
434 self.entries.is_empty()
435 }
436}
437
438#[allow(dead_code)]
439#[allow(unused_variables)]
440#[allow(unused_imports)]
441#[cfg(test)]
442mod test {
443 use crate::{
444 types::{OpaqueType, ValueType},
445 values::OpaqueValue,
446 values::Optional,
447 values::{TypedOpaque, Value},
448 Error, Opaque,
449 };
450 use std::{
451 collections::{BTreeMap, HashMap},
452 sync::Arc,
453 };
454
455 use super::*;
456
457 fn f1(v: i64) -> Result<i64, Error> {
458 Ok(5)
459 }
460 fn f11(a1: i64, a2: i64) -> Result<i64, Error> {
461 Ok(5)
462 }
463
464 fn f2(a1: HashMap<i64, i64>) -> Result<i64, Error> {
465 Ok(5)
466 }
467 fn f3(a1: String) -> Result<i64, Error> {
468 Ok(5)
469 }
470 fn f4(a1: String, a2: String) -> Result<i64, Error> {
471 Ok(5)
472 }
473 fn f5(a1: String, a2: String, a3: HashMap<u64, Vec<Vec<i64>>>) -> Result<i64, Error> {
474 Ok(5)
475 }
476
477 #[cfg(feature = "derive")]
478 #[derive(Opaque, Debug, Clone, PartialEq)]
479 #[cel_cxx(type = "MyOpaque", crate = crate)]
480 #[cel_cxx(display)]
481 struct MyOpaque(pub i64);
482
483 #[cfg(feature = "derive")]
484 fn f6(a1: String, a2: MyOpaque, a3: HashMap<u64, Vec<Vec<i64>>>) -> Result<i64, Error> {
485 Ok(5)
486 }
487
488 fn f7(a1: &str) -> Result<(), Error> {
489 Ok(())
490 }
491
492 #[cfg(feature = "derive")]
493 fn f8(a1: &MyOpaque) -> &MyOpaque {
494 a1
495 }
496
497 #[cfg(feature = "derive")]
498 fn f9(a1: Optional<&MyOpaque>) -> Result<Option<&MyOpaque>, Error> {
499 Ok(a1.into_option())
500 }
501
502 async fn async_f1(a1: String, a2: i64, a3: Option<String>) -> Result<i64, Error> {
503 Ok(5)
504 }
505
506 fn f_map1(a1: HashMap<String, Vec<u8>>) -> Result<(), Error> {
507 Ok(())
508 }
509 fn f_map2(a1: HashMap<&str, &[u8]>) -> Result<(), Error> {
510 Ok(())
511 }
512 fn f_map3<'a>(a1: HashMap<&'a str, &'a str>) -> Result<BTreeMap<&'a str, &'a str>, Error> {
513 Ok(BTreeMap::from_iter(a1))
514 }
515
516 #[test]
517 fn test_register() -> Result<(), Error> {
518 let n = 100;
519 let lifetime_closure = |a: i64, b: i64| Ok::<_, Error>(a + b + n);
520 let mut registry = FunctionRegistry::new();
521 registry
522 .register_member("f1", f1)?
523 .register_member("f11", f11)?
524 .register_member("f2", f2)?
525 .register_member("f3", f3)?
526 .register_member("f4", f4)?
527 .register_member("f5", f5)?
528 .register_global("lifetime_closure", lifetime_closure)?
529 .register_global("f7", f7)?
530 .register_global("f_map1", f_map1)?
531 .register_global("f_map2", f_map2)?
532 .register_global("f_map3", f_map3)?;
533
534 #[cfg(feature = "derive")]
535 registry
536 .register_global("f6", f6)?
537 .register_global("f8", f8)?
538 .register_global("f9", f9)?;
539
540 #[cfg(feature = "async")]
541 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
542 let registry = registry.register_global("async_f1", async_f1)?;
543
544 //let variable = VariableDecl::builder()
545 // .add::<i64, _>("v1")
546 // .build(None);
547 //let env = Env::new(function, variable);
548
549 #[cfg(feature = "derive")]
550 {
551 let x = MyOpaque(1);
552 let y: Box<dyn Opaque> = Box::new(x);
553
554 let value: OpaqueValue = Box::new(MyOpaque(1));
555 assert!(value.is::<MyOpaque>());
556 assert!(value.downcast_ref::<MyOpaque>().is_some());
557 let value2 = value.clone().downcast::<MyOpaque>();
558 assert!(value2.is_ok());
559
560 let value3 = value.clone();
561 assert!(value3.is::<MyOpaque>());
562
563 let value4: OpaqueValue = Box::new(MyOpaque(1));
564 assert!(value4.is::<MyOpaque>());
565 assert!(value4.clone().downcast::<MyOpaque>().is_ok());
566 let value5 = value4.downcast::<MyOpaque>();
567 assert!(value5.is_ok());
568 }
569
570 Ok(())
571 }
572}