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        if member && Args::LEN == 0 {
145            return Err(Error::invalid_argument("Member functions cannot have zero arguments"));
146        }
147        let entry = self
148            .entries
149            .entry(name)
150            .or_insert_with(FunctionOverloads::new);
151        entry.add(member, f.into_function())?;
152        Ok(self)
153    }
154
155    /// Binds a member function implementation.
156    ///
157    /// Convenience method for binding member functions that can be called as methods
158    /// on values (e.g., `"hello".upper()`).
159    ///
160    /// # Examples
161    ///
162    /// ```rust,no_run
163    /// use cel_cxx::{FunctionBindings, Error};
164    ///
165    /// let mut bindings = FunctionBindings::new();
166    ///
167    /// fn reverse(s: String) -> Result<String, Error> {
168    ///     Ok(s.chars().rev().collect())
169    /// }
170    ///
171    /// bindings.bind_member("reverse", reverse)?;
172    /// # Ok::<(), cel_cxx::Error>(())
173    /// ```
174    pub fn bind_member<F, Fm, Args>(
175        &mut self,
176        name: impl Into<String>,
177        f: F,
178    ) -> Result<&mut Self, Error>
179    where
180        F: IntoFunction<'f, Fm, Args>,
181        Fm: FnMarker,
182        Args: Arguments + NonEmptyArguments,
183    {
184        self.bind(name, true, f)
185    }
186
187    /// Binds a global function implementation.
188    ///
189    /// Convenience method for binding global functions that can be called from any context.
190    ///
191    /// # Examples
192    ///
193    /// ```rust,no_run
194    /// use cel_cxx::{FunctionBindings, Error};
195    ///
196    /// let mut bindings = FunctionBindings::new();
197    ///
198    /// fn min(a: i64, b: i64) -> Result<i64, Error> {
199    ///     Ok(a.min(b))
200    /// }
201    ///
202    /// bindings.bind_global("min", min)?;
203    /// # Ok::<(), cel_cxx::Error>(())
204    /// ```
205    pub fn bind_global<F, Fm, Args>(
206        &mut self,
207        name: impl Into<String>,
208        f: F,
209    ) -> Result<&mut Self, Error>
210    where
211        F: IntoFunction<'f, Fm, Args>,
212        Fm: FnMarker,
213        Args: Arguments,
214    {
215        self.bind(name, false, f)
216    }
217
218    /// Finds a function overload set by name.
219    ///
220    /// # Parameters
221    ///
222    /// - `name`: Function name to search for
223    ///
224    /// # Returns
225    ///
226    /// Returns `Some(&FunctionOverloads)` if found, `None` otherwise
227    pub fn find(&self, name: &str) -> Option<&FunctionOverloads<Function<'f>>> {
228        self.entries.get(name)
229    }
230
231    /// Finds a mutable function overload set by name.
232    ///
233    /// # Parameters
234    ///
235    /// - `name`: Function name to search for
236    ///
237    /// # Returns
238    ///
239    /// Returns `Some(&mut FunctionOverloads)` if found, `None` otherwise
240    pub fn find_mut(&mut self, name: &str) -> Option<&mut FunctionOverloads<Function<'f>>> {
241        self.entries.get_mut(name)
242    }
243
244    /// Returns an iterator over all function entries.
245    ///
246    /// The iterator yields `(name, overloads)` pairs for all bound functions.
247    pub fn entries(&self) -> impl Iterator<Item = (&str, &FunctionOverloads<Function<'f>>)> {
248        self.entries
249            .iter()
250            .map(|(name, entry)| (name.as_str(), entry))
251    }
252
253    /// Returns a mutable iterator over all function entries.
254    ///
255    /// The iterator yields `(name, overloads)` pairs and allows modifying the overloads.
256    pub fn entries_mut(
257        &mut self,
258    ) -> impl Iterator<Item = (&str, &mut FunctionOverloads<Function<'f>>)> {
259        self.entries
260            .iter_mut()
261            .map(|(name, entry)| (name.as_str(), entry))
262    }
263
264    /// Removes a function by name.
265    ///
266    /// # Parameters
267    ///
268    /// - `name`: Function name to remove
269    ///
270    /// # Returns
271    ///
272    /// Returns `Ok(())` if successfully removed, or an error if the function doesn't exist
273    pub fn remove(&mut self, name: &str) -> Result<(), Error> {
274        if self.entries.remove(name).is_none() {
275            return Err(Error::not_found(format!("Function {name} not found")));
276        }
277        Ok(())
278    }
279
280    /// Clears all function bindings.
281    pub fn clear(&mut self) {
282        self.entries.clear();
283    }
284}