cel_cxx/variable/bindings.rs
1//! Variable bindings for runtime value storage.
2//!
3//! This module provides the [`VariableBindings`] type for binding actual
4//! values to declared variables during CEL expression evaluation.
5//!
6//! # Features
7//!
8//! - **Value binding**: Associate variable names with runtime values
9//! - **Type checking**: Validate that bound values match declared types
10//! - **Efficient lookup**: Fast variable resolution during evaluation
11//! - **Lifetime management**: Proper handling of borrowed values
12
13use crate::function::*;
14use crate::marker::*;
15use crate::values::*;
16use crate::Error;
17use crate::ValueType;
18use std::collections::HashMap;
19
20/// Runtime variable bindings.
21///
22/// `VariableBindings` manages variable bindings during CEL expression evaluation.
23/// It maps variable names to concrete values or value providers.
24///
25/// Unlike compile-time [`VariableRegistry`], `VariableBindings` provides actual variable values
26/// at runtime and supports two types of bindings:
27///
28/// - **Value bindings**: Directly stored variable values
29/// - **Provider bindings**: Function-computed variable values through lazy evaluation
30///
31/// # Lifetime Parameters
32///
33/// - `'f`: Lifetime of function providers, allowing closures valid within specific scopes
34///
35/// # Examples
36///
37/// ```rust,no_run
38/// use cel_cxx::VariableBindings;
39///
40/// let mut bindings = VariableBindings::new();
41///
42/// // Bind static values
43/// bindings.bind("name", "Alice")?;
44/// bindings.bind("age", 30i64)?;
45///
46/// // Bind dynamic value providers
47/// bindings.bind_provider("current_time", || {
48/// std::time::SystemTime::now()
49/// })?;
50///
51/// assert_eq!(bindings.len(), 3);
52/// # Ok::<(), cel_cxx::Error>(())
53/// ```
54///
55/// [`VariableRegistry`]: crate::variable::VariableRegistry
56#[derive(Debug, Default)]
57pub struct VariableBindings<'f> {
58 entries: HashMap<String, VariableBinding<'f>>,
59}
60
61impl<'f> VariableBindings<'f> {
62 /// Creates a new empty variable bindings.
63 pub fn new() -> Self {
64 Self {
65 entries: HashMap::new(),
66 }
67 }
68}
69
70impl<'f> VariableBindings<'f> {
71 /// Binds a variable value.
72 ///
73 /// Binds a variable name to a concrete value. The value must implement [`IntoValue`] and [`TypedValue`] traits.
74 ///
75 /// # Type Parameters
76 ///
77 /// - `T`: Value type, must implement `IntoValue + TypedValue`
78 ///
79 /// # Parameters
80 ///
81 /// - `name`: Variable name
82 /// - `value`: Variable value
83 ///
84 /// # Returns
85 ///
86 /// Returns `&mut Self` to support method chaining
87 ///
88 /// # Examples
89 ///
90 /// ```rust,no_run
91 /// use cel_cxx::VariableBindings;
92 ///
93 /// let mut bindings = VariableBindings::new();
94 /// bindings
95 /// .bind("user_id", 123i64)?
96 /// .bind("is_admin", true)?
97 /// .bind("username", "alice")?;
98 /// # Ok::<(), cel_cxx::Error>(())
99 /// ```
100 pub fn bind<T>(&mut self, name: impl Into<String>, value: T) -> Result<&mut Self, Error>
101 where
102 T: IntoValue + TypedValue,
103 {
104 self.entries
105 .insert(name.into(), VariableBinding::from_value(value));
106 Ok(self)
107 }
108
109 /// Binds a variable to a value provider.
110 ///
111 /// Binds a variable name to a provider function that computes the value dynamically.
112 /// This enables lazy evaluation and allows variables to have values computed at runtime.
113 ///
114 /// # Type Parameters
115 ///
116 /// - `F`: Provider function type, must implement `IntoFunction`
117 /// - `Fm`: Function marker type (sync/async)
118 ///
119 /// # Parameters
120 ///
121 /// - `name`: Variable name
122 /// - `provider`: Provider function that computes the variable value
123 ///
124 /// # Returns
125 ///
126 /// Returns `&mut Self` to support method chaining
127 ///
128 /// # Examples
129 ///
130 /// ```rust,no_run
131 /// use cel_cxx::VariableBindings;
132 ///
133 /// let mut bindings = VariableBindings::new();
134 /// bindings.bind_provider("current_time", || -> i64 {
135 /// std::time::SystemTime::now()
136 /// .duration_since(std::time::UNIX_EPOCH)
137 /// .unwrap()
138 /// .as_secs() as i64
139 /// })?;
140 /// # Ok::<(), cel_cxx::Error>(())
141 /// ```
142 pub fn bind_provider<F, Fm>(
143 &mut self,
144 name: impl Into<String>,
145 provider: F,
146 ) -> Result<&mut Self, Error>
147 where
148 F: IntoFunction<'f, Fm>,
149 Fm: FnMarker,
150 {
151 self.entries
152 .insert(name.into(), VariableBinding::from_provider(provider));
153 Ok(self)
154 }
155
156 /// Finds a variable binding by name.
157 ///
158 /// # Parameters
159 ///
160 /// - `name`: Variable name
161 ///
162 /// # Returns
163 ///
164 /// Returns `Some(&VariableBinding)` if found, `None` otherwise
165 pub fn find(&self, name: &str) -> Option<&VariableBinding<'f>> {
166 self.entries.get(name)
167 }
168
169 /// Finds a mutable variable binding by name.
170 ///
171 /// # Parameters
172 ///
173 /// - `name`: Variable name
174 ///
175 /// # Returns
176 ///
177 /// Returns `Some(&mut VariableBinding)` if found, `None` otherwise
178 pub fn find_mut(&mut self, name: &str) -> Option<&mut VariableBinding<'f>> {
179 self.entries.get_mut(name)
180 }
181
182 /// Returns an iterator over all variable bindings.
183 ///
184 /// The iterator yields `(name, binding)` pairs for all bound variables.
185 ///
186 /// # Returns
187 ///
188 /// Iterator yielding `(&str, &VariableBinding)` pairs
189 pub fn entries(&self) -> impl Iterator<Item = (&str, &VariableBinding<'f>)> {
190 self.entries
191 .iter()
192 .map(|(name, entry)| (name.as_str(), entry))
193 }
194
195 /// Returns a mutable iterator over all variable bindings.
196 ///
197 /// The iterator yields `(name, binding)` pairs and allows modifying the bindings.
198 ///
199 /// # Returns
200 ///
201 /// Iterator yielding `(&str, &mut VariableBinding)` pairs
202 pub fn entries_mut(&mut self) -> impl Iterator<Item = (&str, &mut VariableBinding<'f>)> {
203 self.entries
204 .iter_mut()
205 .map(|(name, entry)| (name.as_str(), entry))
206 }
207
208 /// Removes a variable binding by name.
209 ///
210 /// # Parameters
211 ///
212 /// - `name`: Variable name to remove
213 ///
214 /// # Returns
215 ///
216 /// Returns `Ok(())` if successfully removed, or an error if the variable doesn't exist
217 pub fn remove(&mut self, name: &str) -> Result<(), Error> {
218 if self.entries.remove(name).is_none() {
219 return Err(Error::not_found(format!("Variable {name} not found")));
220 }
221 Ok(())
222 }
223
224 /// Clears all variable bindings.
225 pub fn clear(&mut self) {
226 self.entries.clear();
227 }
228
229 /// Returns the number of variable bindings.
230 ///
231 /// # Returns
232 ///
233 /// Number of bound variables
234 pub fn len(&self) -> usize {
235 self.entries.len()
236 }
237
238 /// Returns whether the bindings are empty.
239 ///
240 /// # Returns
241 ///
242 /// `true` if no variables are bound, `false` otherwise
243 pub fn is_empty(&self) -> bool {
244 self.entries.is_empty()
245 }
246}
247
248/// Variable binding: value or provider.
249///
250/// `VariableBinding` represents a runtime variable binding, which can be:
251///
252/// - **Value binding** (`Value`): Directly stored variable value
253/// - **Provider binding** (`Provider`): Dynamically computed variable value through functions
254///
255/// # Lifetime Parameters
256///
257/// - `'f`: Lifetime of provider functions
258#[derive(Debug, Clone)]
259pub enum VariableBinding<'f> {
260 /// Directly stored value binding, containing (type, value) tuple
261 Value((ValueType, Value)),
262 /// Dynamic provider binding, computing values through functions
263 Provider(Function<'f>),
264}
265
266impl<'f> VariableBinding<'f> {
267 /// Creates a variable binding from a value.
268 ///
269 /// # Type Parameters
270 ///
271 /// - `T`: Value type, must implement `IntoValue + TypedValue`
272 ///
273 /// # Parameters
274 ///
275 /// - `value`: The value to bind
276 ///
277 /// # Returns
278 ///
279 /// New `VariableBinding::Value` containing the value and its type
280 pub fn from_value<T: IntoValue + TypedValue>(value: T) -> Self {
281 Self::Value((T::value_type(), value.into_value()))
282 }
283
284 /// Creates a variable binding from a provider function.
285 ///
286 /// # Type Parameters
287 ///
288 /// - `F`: Provider function type, must implement `IntoFunction`
289 /// - `Fm`: Function marker type (sync/async)
290 ///
291 /// # Parameters
292 ///
293 /// - `provider`: The provider function
294 ///
295 /// # Returns
296 ///
297 /// New `VariableBinding::Provider` containing the provider function
298 pub fn from_provider<F, Fm>(provider: F) -> Self
299 where
300 F: IntoFunction<'f, Fm>,
301 Fm: FnMarker,
302 {
303 Self::Provider(provider.into_function())
304 }
305
306 /// Returns the value type of this binding.
307 ///
308 /// For value bindings, returns the stored type. For provider bindings,
309 /// returns the return type of the provider function.
310 ///
311 /// # Returns
312 ///
313 /// The [`ValueType`] of this binding
314 pub fn value_type(&self) -> ValueType {
315 match self {
316 Self::Value((ty, _)) => ty.clone(),
317 Self::Provider(f) => f.function_type().result().clone(),
318 }
319 }
320
321 /// Returns whether this is a value binding.
322 ///
323 /// # Returns
324 ///
325 /// `true` if this is a `Value` binding, `false` if it's a `Provider` binding
326 pub fn is_value(&self) -> bool {
327 matches!(self, Self::Value(_))
328 }
329
330 /// Returns whether this is a provider binding.
331 ///
332 /// # Returns
333 ///
334 /// `true` if this is a `Provider` binding, `false` if it's a `Value` binding
335 pub fn is_provider(&self) -> bool {
336 matches!(self, Self::Provider(_))
337 }
338
339 /// Returns the value if this is a value binding.
340 ///
341 /// # Returns
342 ///
343 /// `Some(&Value)` if this is a value binding, `None` if it's a provider binding
344 pub fn as_value(&self) -> Option<&Value> {
345 match self {
346 Self::Value((_, value)) => Some(value),
347 Self::Provider(_) => None,
348 }
349 }
350
351 /// Returns the provider function if this is a provider binding.
352 ///
353 /// # Returns
354 ///
355 /// `Some(&Function)` if this is a provider binding, `None` if it's a value binding
356 pub fn as_provider(&self) -> Option<&Function<'f>> {
357 match self {
358 Self::Value(_) => None,
359 Self::Provider(f) => Some(f),
360 }
361 }
362
363 /// Converts this binding into a value if it's a value binding.
364 ///
365 /// # Returns
366 ///
367 /// `Some(Value)` if this is a value binding, `None` if it's a provider binding
368 pub fn into_value(self) -> Option<Value> {
369 match self {
370 Self::Value((_, value)) => Some(value),
371 Self::Provider(_) => None,
372 }
373 }
374
375 /// Converts this binding into a provider function if it's a provider binding.
376 ///
377 /// # Returns
378 ///
379 /// `Some(Function)` if this is a provider binding, `None` if it's a value binding
380 pub fn into_provider(self) -> Option<Function<'f>> {
381 match self {
382 Self::Value(_) => None,
383 Self::Provider(f) => Some(f),
384 }
385 }
386}