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}