Skip to main content

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 with an explicit type and pre-converted value.
110    ///
111    /// Unlike [`bind`](Self::bind) which infers the type from the value, this method
112    /// takes the type and value separately. This is useful for types like protobuf
113    /// messages where the concrete type name is not known at compile time.
114    pub fn bind_with_value(
115        &mut self,
116        name: impl Into<String>,
117        value_type: ValueType,
118        value: Value,
119    ) -> Result<&mut Self, Error> {
120        self.entries
121            .insert(name.into(), VariableBinding::Value((value_type, value)));
122        Ok(self)
123    }
124
125    /// Binds a variable to a value provider.
126    ///
127    /// Binds a variable name to a provider function that computes the value dynamically.
128    /// This enables lazy evaluation and allows variables to have values computed at runtime.
129    ///
130    /// # Type Parameters
131    ///
132    /// - `F`: Provider function type, must implement `IntoFunction`
133    /// - `Fm`: Function marker type (sync/async)
134    ///
135    /// # Parameters
136    ///
137    /// - `name`: Variable name
138    /// - `provider`: Provider function that computes the variable value
139    ///
140    /// # Returns
141    ///
142    /// Returns `&mut Self` to support method chaining
143    ///
144    /// # Examples
145    ///
146    /// ```rust,no_run
147    /// use cel_cxx::VariableBindings;
148    ///
149    /// let mut bindings = VariableBindings::new();
150    /// bindings.bind_provider("current_time", || -> i64 {
151    ///     std::time::SystemTime::now()
152    ///         .duration_since(std::time::UNIX_EPOCH)
153    ///         .unwrap()
154    ///         .as_secs() as i64
155    /// })?;
156    /// # Ok::<(), cel_cxx::Error>(())
157    /// ```
158    pub fn bind_provider<F, Fm>(
159        &mut self,
160        name: impl Into<String>,
161        provider: F,
162    ) -> Result<&mut Self, Error>
163    where
164        F: IntoFunction<'f, Fm>,
165        Fm: FnMarker,
166    {
167        self.entries
168            .insert(name.into(), VariableBinding::from_provider(provider));
169        Ok(self)
170    }
171
172    /// Finds a variable binding by name.
173    ///
174    /// # Parameters
175    ///
176    /// - `name`: Variable name
177    ///
178    /// # Returns
179    ///
180    /// Returns `Some(&VariableBinding)` if found, `None` otherwise
181    pub fn find(&self, name: &str) -> Option<&VariableBinding<'f>> {
182        self.entries.get(name)
183    }
184
185    /// Finds a mutable variable binding by name.
186    ///
187    /// # Parameters
188    ///
189    /// - `name`: Variable name
190    ///
191    /// # Returns
192    ///
193    /// Returns `Some(&mut VariableBinding)` if found, `None` otherwise
194    pub fn find_mut(&mut self, name: &str) -> Option<&mut VariableBinding<'f>> {
195        self.entries.get_mut(name)
196    }
197
198    /// Returns an iterator over all variable bindings.
199    ///
200    /// The iterator yields `(name, binding)` pairs for all bound variables.
201    ///
202    /// # Returns
203    ///
204    /// Iterator yielding `(&str, &VariableBinding)` pairs
205    pub fn entries(&self) -> impl Iterator<Item = (&str, &VariableBinding<'f>)> {
206        self.entries
207            .iter()
208            .map(|(name, entry)| (name.as_str(), entry))
209    }
210
211    /// Returns a mutable iterator over all variable bindings.
212    ///
213    /// The iterator yields `(name, binding)` pairs and allows modifying the bindings.
214    ///
215    /// # Returns
216    ///
217    /// Iterator yielding `(&str, &mut VariableBinding)` pairs
218    pub fn entries_mut(&mut self) -> impl Iterator<Item = (&str, &mut VariableBinding<'f>)> {
219        self.entries
220            .iter_mut()
221            .map(|(name, entry)| (name.as_str(), entry))
222    }
223
224    /// Removes a variable binding by name.
225    ///
226    /// # Parameters
227    ///
228    /// - `name`: Variable name to remove
229    ///
230    /// # Returns
231    ///
232    /// Returns `Ok(())` if successfully removed, or an error if the variable doesn't exist
233    pub fn remove(&mut self, name: &str) -> Result<(), Error> {
234        if self.entries.remove(name).is_none() {
235            return Err(Error::not_found(format!("Variable {name} not found")));
236        }
237        Ok(())
238    }
239
240    /// Clears all variable bindings.
241    pub fn clear(&mut self) {
242        self.entries.clear();
243    }
244
245    /// Returns the number of variable bindings.
246    ///
247    /// # Returns
248    ///
249    /// Number of bound variables
250    pub fn len(&self) -> usize {
251        self.entries.len()
252    }
253
254    /// Returns whether the bindings are empty.
255    ///
256    /// # Returns
257    ///
258    /// `true` if no variables are bound, `false` otherwise
259    pub fn is_empty(&self) -> bool {
260        self.entries.is_empty()
261    }
262}
263
264/// Variable binding: value or provider.
265///
266/// `VariableBinding` represents a runtime variable binding, which can be:
267///
268/// - **Value binding** (`Value`): Directly stored variable value
269/// - **Provider binding** (`Provider`): Dynamically computed variable value through functions
270///
271/// # Lifetime Parameters
272///
273/// - `'f`: Lifetime of provider functions
274#[derive(Debug, Clone)]
275pub enum VariableBinding<'f> {
276    /// Directly stored value binding, containing (type, value) tuple
277    Value((ValueType, Value)),
278    /// Dynamic provider binding, computing values through functions
279    Provider(Function<'f>),
280}
281
282impl<'f> VariableBinding<'f> {
283    /// Creates a variable binding from a value.
284    ///
285    /// # Type Parameters
286    ///
287    /// - `T`: Value type, must implement `IntoValue + TypedValue`
288    ///
289    /// # Parameters
290    ///
291    /// - `value`: The value to bind
292    ///
293    /// # Returns
294    ///
295    /// New `VariableBinding::Value` containing the value and its type
296    pub fn from_value<T: IntoValue + TypedValue>(value: T) -> Self {
297        Self::Value((T::value_type(), value.into_value()))
298    }
299
300    /// Creates a variable binding from a provider function.
301    ///
302    /// # Type Parameters
303    ///
304    /// - `F`: Provider function type, must implement `IntoFunction`
305    /// - `Fm`: Function marker type (sync/async)
306    ///
307    /// # Parameters
308    ///
309    /// - `provider`: The provider function
310    ///
311    /// # Returns
312    ///
313    /// New `VariableBinding::Provider` containing the provider function
314    pub fn from_provider<F, Fm>(provider: F) -> Self
315    where
316        F: IntoFunction<'f, Fm>,
317        Fm: FnMarker,
318    {
319        Self::Provider(provider.into_function())
320    }
321
322    /// Returns the value type of this binding.
323    ///
324    /// For value bindings, returns the stored type. For provider bindings,
325    /// returns the return type of the provider function.
326    ///
327    /// # Returns
328    ///
329    /// The [`ValueType`] of this binding
330    pub fn value_type(&self) -> ValueType {
331        match self {
332            Self::Value((ty, _)) => ty.clone(),
333            Self::Provider(f) => f.function_type().result().clone(),
334        }
335    }
336
337    /// Returns whether this is a value binding.
338    ///
339    /// # Returns
340    ///
341    /// `true` if this is a `Value` binding, `false` if it's a `Provider` binding
342    pub fn is_value(&self) -> bool {
343        matches!(self, Self::Value(_))
344    }
345
346    /// Returns whether this is a provider binding.
347    ///
348    /// # Returns
349    ///
350    /// `true` if this is a `Provider` binding, `false` if it's a `Value` binding
351    pub fn is_provider(&self) -> bool {
352        matches!(self, Self::Provider(_))
353    }
354
355    /// Returns the value if this is a value binding.
356    ///
357    /// # Returns
358    ///
359    /// `Some(&Value)` if this is a value binding, `None` if it's a provider binding
360    pub fn as_value(&self) -> Option<&Value> {
361        match self {
362            Self::Value((_, value)) => Some(value),
363            Self::Provider(_) => None,
364        }
365    }
366
367    /// Returns the provider function if this is a provider binding.
368    ///
369    /// # Returns
370    ///
371    /// `Some(&Function)` if this is a provider binding, `None` if it's a value binding
372    pub fn as_provider(&self) -> Option<&Function<'f>> {
373        match self {
374            Self::Value(_) => None,
375            Self::Provider(f) => Some(f),
376        }
377    }
378
379    /// Converts this binding into a value if it's a value binding.
380    ///
381    /// # Returns
382    ///
383    /// `Some(Value)` if this is a value binding, `None` if it's a provider binding
384    pub fn into_value(self) -> Option<Value> {
385        match self {
386            Self::Value((_, value)) => Some(value),
387            Self::Provider(_) => None,
388        }
389    }
390
391    /// Converts this binding into a provider function if it's a provider binding.
392    ///
393    /// # Returns
394    ///
395    /// `Some(Function)` if this is a provider binding, `None` if it's a value binding
396    pub fn into_provider(self) -> Option<Function<'f>> {
397        match self {
398            Self::Value(_) => None,
399            Self::Provider(f) => Some(f),
400        }
401    }
402}