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}