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}