cel_cxx/function/
bindings.rs

1//! Function binding utilities for runtime variable capture.
2//!
3//! This module provides utilities for binding functions with captured
4//! environment variables, enabling closures to access external state
5//! during CEL expression evaluation.
6//!
7//! # Use Cases
8//!
9//! - **Configuration binding**: Capture configuration values in closures
10//! - **Database connections**: Bind database handles to query functions
11//! - **External services**: Capture service clients for API calls
12//! - **State management**: Access mutable state from function implementations
13
14use super::*;
15use std::collections::HashMap;
16
17/// Runtime function bindings.
18///
19/// `FunctionBindings` manages function implementations during CEL expression evaluation.
20/// Unlike [`FunctionRegistry`] which is used at compile time, `FunctionBindings` provides
21/// actual callable function implementations for runtime execution.
22///
23/// Each function name can have multiple overloads with different signatures, and each overload
24/// can be either a global function or a member function.
25///
26/// # Lifetime Parameters
27///
28/// - `'f`: Lifetime of function implementations, allowing closures valid within specific scopes
29///
30/// # Examples
31///
32/// ```rust,no_run
33/// use cel_cxx::{FunctionBindings, Error};
34///
35/// let mut bindings = FunctionBindings::new();
36///
37/// // Bind global functions
38/// fn add(a: i64, b: i64) -> Result<i64, Error> {
39///     Ok(a + b)
40/// }
41/// bindings.bind_global("add", add)?;
42///
43/// // Bind member functions
44/// fn string_length(s: String) -> Result<i64, Error> {
45///     Ok(s.len() as i64)
46/// }
47/// bindings.bind_member("length", string_length)?;
48///
49/// // Bind closures
50/// let multiplier = 10;
51/// let closure = move |x: i64| -> Result<i64, Error> {
52///     Ok(x * multiplier)
53/// };
54/// bindings.bind_global("multiply_by_ten", closure)?;
55/// # Ok::<(), cel_cxx::Error>(())
56/// ```
57///
58/// [`FunctionRegistry`]: crate::function::FunctionRegistry
59#[derive(Debug, Default)]
60pub struct FunctionBindings<'f> {
61    entries: HashMap<String, FunctionOverloads<Function<'f>>>,
62}
63
64impl<'f> FunctionBindings<'f> {
65    /// Creates a new empty function bindings.
66    pub fn new() -> Self {
67        Self {
68            entries: HashMap::new(),
69        }
70    }
71}
72
73impl<'f> FunctionBindings<'f> {
74    /// Binds a function implementation with zero-annotation type inference.
75    ///
76    /// Binds a callable function that can be invoked during CEL expression evaluation.
77    /// The function signature is automatically inferred from the Rust function type, enabling
78    /// seamless runtime function binding without manual type annotations.
79    ///
80    /// # Zero-Annotation Features
81    ///
82    /// - **Automatic type inference**: Function signature extracted from Rust function type
83    /// - **Runtime binding**: Functions are immediately available for CEL evaluation
84    /// - **Lifetime handling**: Safe conversion of borrowed arguments like `&str`
85    /// - **Error conversion**: Automatic conversion of `Result<T, E>` return types
86    /// - **Async support**: Seamless handling of both sync and async functions
87    ///
88    /// # Type Parameters
89    ///
90    /// - `F`: Function type, must implement [`IntoFunction`]
91    /// - `Fm`: Function marker (sync or async), automatically inferred
92    /// - `Args`: Argument tuple type, automatically inferred from function signature
93    ///
94    /// # Parameters
95    ///
96    /// - `name`: Function name
97    /// - `member`: Whether this is a member function (true) or global function (false)
98    /// - `f`: Function implementation
99    ///
100    /// # Returns
101    ///
102    /// Returns `&mut Self` to support method chaining
103    ///
104    /// # Examples
105    ///
106    /// ## Basic function binding with automatic type inference
107    ///
108    /// ```rust,no_run
109    /// use cel_cxx::FunctionBindings;
110    ///
111    /// let mut bindings = FunctionBindings::new();
112    ///
113    /// // Zero-annotation function binding
114    /// bindings.bind("multiply", false, |a: i64, b: i64| a * b)?;
115    /// bindings.bind("upper", true, |s: String| s.to_uppercase())?;
116    /// bindings.bind("contains", true, |s: &str, substr: &str| s.contains(substr))?;
117    /// # Ok::<(), cel_cxx::Error>(())
118    /// ```
119    ///
120    /// ## Runtime function binding with closures
121    ///
122    /// ```rust,no_run
123    /// use cel_cxx::FunctionBindings;
124    ///
125    /// let mut bindings = FunctionBindings::new();
126    /// let multiplier = 10;
127    ///
128    /// // Bind closure that captures environment
129    /// bindings.bind("scale", false, move |x: i64| x * multiplier)?;
130    /// # Ok::<(), cel_cxx::Error>(())
131    /// ```
132    pub fn bind<F, Fm, Args>(
133        &mut self,
134        name: impl Into<String>,
135        member: bool,
136        f: F,
137    ) -> Result<&mut Self, Error>
138    where
139        F: IntoFunction<'f, Fm, Args>,
140        Fm: FnMarker,
141        Args: Arguments,
142    {
143        let name = name.into();
144        let entry = self
145            .entries
146            .entry(name)
147            .or_insert_with(FunctionOverloads::new);
148        entry.add(member, f.into_function())?;
149        Ok(self)
150    }
151
152    /// Binds a member function implementation.
153    ///
154    /// Convenience method for binding member functions that can be called as methods
155    /// on values (e.g., `"hello".upper()`).
156    ///
157    /// # Examples
158    ///
159    /// ```rust,no_run
160    /// use cel_cxx::{FunctionBindings, Error};
161    ///
162    /// let mut bindings = FunctionBindings::new();
163    ///
164    /// fn reverse(s: String) -> Result<String, Error> {
165    ///     Ok(s.chars().rev().collect())
166    /// }
167    ///
168    /// bindings.bind_member("reverse", reverse)?;
169    /// # Ok::<(), cel_cxx::Error>(())
170    /// ```
171    pub fn bind_member<F, Fm, Args>(
172        &mut self,
173        name: impl Into<String>,
174        f: F,
175    ) -> Result<&mut Self, Error>
176    where
177        F: IntoFunction<'f, Fm, Args>,
178        Fm: FnMarker,
179        Args: Arguments,
180    {
181        self.bind(name, true, f)
182    }
183
184    /// Binds a global function implementation.
185    ///
186    /// Convenience method for binding global functions that can be called from any context.
187    ///
188    /// # Examples
189    ///
190    /// ```rust,no_run
191    /// use cel_cxx::{FunctionBindings, Error};
192    ///
193    /// let mut bindings = FunctionBindings::new();
194    ///
195    /// fn min(a: i64, b: i64) -> Result<i64, Error> {
196    ///     Ok(a.min(b))
197    /// }
198    ///
199    /// bindings.bind_global("min", min)?;
200    /// # Ok::<(), cel_cxx::Error>(())
201    /// ```
202    pub fn bind_global<F, Fm, Args>(
203        &mut self,
204        name: impl Into<String>,
205        f: F,
206    ) -> Result<&mut Self, Error>
207    where
208        F: IntoFunction<'f, Fm, Args>,
209        Fm: FnMarker,
210        Args: Arguments,
211    {
212        self.bind(name, false, f)
213    }
214
215    /// Finds a function overload set by name.
216    ///
217    /// # Parameters
218    ///
219    /// - `name`: Function name to search for
220    ///
221    /// # Returns
222    ///
223    /// Returns `Some(&FunctionOverloads)` if found, `None` otherwise
224    pub fn find(&self, name: &str) -> Option<&FunctionOverloads<Function<'f>>> {
225        self.entries.get(name)
226    }
227
228    /// Finds a mutable function overload set by name.
229    ///
230    /// # Parameters
231    ///
232    /// - `name`: Function name to search for
233    ///
234    /// # Returns
235    ///
236    /// Returns `Some(&mut FunctionOverloads)` if found, `None` otherwise
237    pub fn find_mut(&mut self, name: &str) -> Option<&mut FunctionOverloads<Function<'f>>> {
238        self.entries.get_mut(name)
239    }
240
241    /// Returns an iterator over all function entries.
242    ///
243    /// The iterator yields `(name, overloads)` pairs for all bound functions.
244    pub fn entries(&self) -> impl Iterator<Item = (&str, &FunctionOverloads<Function<'f>>)> {
245        self.entries
246            .iter()
247            .map(|(name, entry)| (name.as_str(), entry))
248    }
249
250    /// Returns a mutable iterator over all function entries.
251    ///
252    /// The iterator yields `(name, overloads)` pairs and allows modifying the overloads.
253    pub fn entries_mut(
254        &mut self,
255    ) -> impl Iterator<Item = (&str, &mut FunctionOverloads<Function<'f>>)> {
256        self.entries
257            .iter_mut()
258            .map(|(name, entry)| (name.as_str(), entry))
259    }
260
261    /// Removes a function by name.
262    ///
263    /// # Parameters
264    ///
265    /// - `name`: Function name to remove
266    ///
267    /// # Returns
268    ///
269    /// Returns `Ok(())` if successfully removed, or an error if the function doesn't exist
270    pub fn remove(&mut self, name: &str) -> Result<(), Error> {
271        if self.entries.remove(name).is_none() {
272            return Err(Error::not_found(format!("Function {name} not found")));
273        }
274        Ok(())
275    }
276
277    /// Clears all function bindings.
278    pub fn clear(&mut self) {
279        self.entries.clear();
280    }
281}