cel_cxx/program/
mod.rs

1//! Compiled CEL program evaluation.
2//!
3//! This module provides the [`Program`] type, which represents a compiled CEL expression
4//! ready for evaluation. Programs are created by compiling CEL expressions using an
5//! [`Env`](crate::Env) and can be evaluated multiple times with different variable
6//! bindings (activations).
7//!
8//! # Key Features
9//!
10//! - **Compiled expressions**: CEL expressions are parsed and compiled once, then evaluated many times
11//! - **Type safety**: Programs know their return type at compile time
12//! - **Variable binding**: Support for dynamic variable values through activations
13//! - **Async support**: Programs can contain and evaluate async functions
14//! - **Runtime selection**: Choose between different async runtimes (Tokio, async-std)
15//!
16//! # Program Types
17//!
18//! Programs are parameterized by function and runtime markers:
19//!
20//! - **`Program<'f>`**: Synchronous program with sync functions only
21//! - **`AsyncProgram<'f>`**: Program that can contain async functions
22//! - **`Program<'f, Fm, Rm>`**: Full type with function marker `Fm` and runtime marker `Rm`
23//!
24//! # Evaluation Model
25//!
26//! Programs use an activation-based evaluation model:
27//!
28//! 1. **Compilation**: Parse and type-check the CEL expression
29//! 2. **Activation**: Bind variables and functions for a specific evaluation
30//! 3. **Evaluation**: Execute the compiled expression with the bound values
31//!
32//! # Examples
33//!
34//! ## Basic synchronous evaluation
35//!
36//! ```rust,no_run
37//! use cel_cxx::*;
38//!
39//! // Create environment and compile expression
40//! let env = Env::builder()
41//!     .declare_variable::<String>("user_name")?
42//!     .declare_variable::<i64>("user_age")?
43//!     .build()?;
44//!
45//! let program = env.compile("'Hello ' + user_name + ', you are ' + string(user_age)")?;
46//!
47//! // Create activation with variable bindings
48//! let activation = Activation::new()
49//!     .bind_variable("user_name", "Alice".to_string())?
50//!     .bind_variable("user_age", 30i64)?;
51//!
52//! // Evaluate the program
53//! let result = program.evaluate(activation)?;
54//! println!("{}", result); // "Hello Alice, you are 30"
55//! # Ok::<(), cel_cxx::Error>(())
56//! ```
57//!
58//! ## Working with functions
59//!
60//! ```rust,no_run
61//! use cel_cxx::*;
62//!
63//! // Register custom function
64//! let env = Env::builder()
65//!     .register_global_function("multiply", |a: i64, b: i64| a * b)?
66//!     .declare_variable::<i64>("x")?
67//!     .build()?;
68//!
69//! let program = env.compile("multiply(x, 2) + 1")?;
70//!
71//! let activation = Activation::new()
72//!     .bind_variable("x", 21i64)?;
73//!
74//! let result = program.evaluate(activation)?;
75//! assert_eq!(result, Value::Int(43));
76//! # Ok::<(), cel_cxx::Error>(())
77//! ```
78//!
79//! ## Async evaluation
80//!
81//! ```rust,no_run
82//! # #[cfg(feature = "async")]
83//! # async fn example() -> Result<(), cel_cxx::Error> {
84//! use cel_cxx::*;
85//! use cel_cxx::r#async::Tokio;
86//!
87//! // Register async function
88//! let env = Env::builder()
89//!     .register_global_function("fetch_data", |url: String| async move {
90//!         // Simulate async work
91//!         tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
92//!         format!("Data from {}", url)
93//!     })?
94//!     .build()?;
95//!
96//! let program = env.compile("fetch_data('https://api.example.com')")?
97//!     .use_runtime::<Tokio>();
98//!
99//! let result = program.evaluate(()).await?;
100//! println!("{}", result); // "Data from https://api.example.com"
101//! # Ok(())
102//! # }
103//! ```
104//!
105//! ## Reusing programs
106//!
107//! ```rust,no_run
108//! use cel_cxx::*;
109//!
110//! let env = Env::builder()
111//!     .declare_variable::<i64>("value")?
112//!     .build()?;
113//!
114//! // Compile once
115//! let program = env.compile("value * value")?;
116//!
117//! // Evaluate multiple times with different values
118//! for i in 1..=5 {
119//!     let activation = Activation::new()
120//!         .bind_variable("value", i)?;
121//!     
122//!     let result = program.evaluate(activation)?;
123//!     println!("{} * {} = {}", i, i, result);
124//! }
125//! # Ok::<(), cel_cxx::Error>(())
126//! ```
127
128use std::sync::Arc;
129mod eval_dispatch;
130mod inner;
131use super::{ActivationInterface, Error, Value, ValueType};
132use crate::{FnMarker, FnMarkerAggr, FnResult, RuntimeMarker};
133use eval_dispatch::{EvalDispatch, EvalDispatcher};
134
135pub(crate) use inner::ProgramInner;
136
137#[cfg(feature = "async")]
138#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
139use crate::marker::Async;
140
141/// Compiled CEL program ready for evaluation.
142///
143/// A `Program` represents a compiled CEL expression that can be evaluated
144/// multiple times with different variable bindings (activations). Programs
145/// are created by compiling CEL expressions using an [`Env`](crate::Env).
146///
147/// # Type Parameters
148///
149/// - `'f`: Lifetime of functions registered in the environment
150/// - `Fm`: Function marker type indicating sync/async function support
151/// - `Rm`: Runtime marker type indicating the async runtime (if any)
152///
153/// # Examples
154///
155/// ## Basic Usage
156///
157/// ```rust,no_run
158/// use cel_cxx::*;
159///
160/// let env = Env::builder()
161///     .declare_variable::<String>("name")?
162///     .build()?;
163///     
164/// let program = env.compile("'Hello, ' + name")?;
165///
166/// let activation = Activation::new()
167///     .bind_variable("name", "World")?;
168///     
169/// let result = program.evaluate(activation)?;
170/// # Ok::<(), cel_cxx::Error>(())
171/// ```
172///
173/// ## Type Information
174///
175/// ```rust,no_run
176/// use cel_cxx::*;
177///
178/// let env = Env::builder().build()?;
179/// let program = env.compile("42")?;
180///
181/// // Check the return type
182/// println!("Return type: {:?}", program.return_type());
183/// # Ok::<(), cel_cxx::Error>(())
184/// ```
185pub struct Program<'f, Fm: FnMarker = (), Rm: RuntimeMarker = ()> {
186    pub(crate) inner: Arc<ProgramInner<'f>>,
187    pub(crate) _fn_marker: std::marker::PhantomData<Fm>,
188    pub(crate) _rt_marker: std::marker::PhantomData<Rm>,
189}
190
191/// Type alias for asynchronous CEL programs.
192///
193/// This is a convenience type alias for programs that support asynchronous
194/// evaluation with async functions and/or async runtime.
195#[cfg(feature = "async")]
196#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
197pub type AsyncProgram<'f, Rm = ()> = Program<'f, Async, Rm>;
198
199impl<'f, Fm: FnMarker, Rm: RuntimeMarker> Program<'f, Fm, Rm> {
200    /// Returns the return type of this program.
201    ///
202    /// This method returns the CEL type that this program will produce when
203    /// evaluated. The type is determined during compilation based on the
204    /// expression and the declared variables and functions.
205    ///
206    /// # Returns
207    ///
208    /// A reference to the [`ValueType`] that this program returns.
209    ///
210    /// # Examples
211    ///
212    /// ```rust,no_run
213    /// use cel_cxx::*;
214    ///
215    /// let env = Env::builder().build()?;
216    /// let program = env.compile("42")?;
217    ///
218    /// println!("Return type: {:?}", program.return_type());
219    /// // Output: Return type: Int
220    /// # Ok::<(), cel_cxx::Error>(())
221    /// ```
222    pub fn return_type(&self) -> &ValueType {
223        self.inner.return_type()
224    }
225
226    /// Evaluates the program with the given activation.
227    ///
228    /// This method evaluates the compiled CEL expression using the variable
229    /// and function bindings provided in the activation. The return type
230    /// of this method depends on the program and activation markers:
231    ///
232    /// - For synchronous programs: Returns `Result<Value, Error>`
233    /// - For asynchronous programs: Returns `BoxFuture<Result<Value, Error>>`
234    ///
235    /// # Arguments
236    ///
237    /// * `activation` - The activation containing variable and function bindings
238    ///
239    /// # Type Parameters
240    ///
241    /// * `A` - The activation type
242    /// * `Afm` - The activation's function marker type
243    ///
244    /// # Examples
245    ///
246    /// ## Synchronous Evaluation
247    ///
248    /// ```rust,no_run
249    /// use cel_cxx::*;
250    ///
251    /// let env = Env::builder()
252    ///     .declare_variable::<i64>("x")?
253    ///     .build()?;
254    ///     
255    /// let program = env.compile("x * 2")?;
256    ///
257    /// let activation = Activation::new()
258    ///     .bind_variable("x", 21i64)?;
259    ///     
260    /// let result = program.evaluate(activation)?;
261    /// // result == Value::Int(42)
262    /// # Ok::<(), cel_cxx::Error>(())
263    /// ```
264    ///
265    /// ## With Empty Activation
266    ///
267    /// ```rust,no_run
268    /// use cel_cxx::*;
269    ///
270    /// let env = Env::builder().build()?;
271    /// let program = env.compile("1 + 2 * 3")?;
272    ///
273    /// let result = program.evaluate(())?;
274    /// // result == Value::Int(7)
275    /// # Ok::<(), cel_cxx::Error>(())
276    /// ```
277    pub fn evaluate<'a, A, Afm>(
278        &self,
279        activation: A,
280    ) -> <<Afm as FnMarkerAggr<Fm>>::Output as FnResult<'f, Result<Value, Error>>>::Output
281    where
282        'f: 'a,
283        A: ActivationInterface<'f, Afm> + 'a,
284        Afm: FnMarkerAggr<Fm>,
285        <Afm as FnMarkerAggr<Fm>>::Output: FnResult<'f, Result<Value, Error>>,
286        EvalDispatcher<<Afm as FnMarkerAggr<Fm>>::Output, Rm>:
287            EvalDispatch<
288                'f,
289                A,
290                Afm,
291                Output = <<Afm as FnMarkerAggr<Fm>>::Output as FnResult<
292                    'f,
293                    Result<Value, Error>,
294                >>::Output,
295            >,
296    {
297        EvalDispatcher::<<Afm as FnMarkerAggr<Fm>>::Output, Rm>::new()
298            .eval(self.inner.clone(), activation)
299    }
300}
301
302/// Async-specific methods for programs.
303///
304/// These methods are only available when the `async` feature is enabled
305/// and provide utilities for working with async runtimes.
306#[cfg(feature = "async")]
307#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
308const _: () = {
309    use crate::r#async::*;
310
311    impl<'f, Fm: FnMarker> Program<'f, Fm, ()> {
312        /// Configures this program to use a specific async runtime.
313        ///
314        /// This method allows you to specify which async runtime should be
315        /// used for evaluating this program when it contains async functions.
316        ///
317        /// # Type Parameters
318        ///
319        /// * `Rt` - The runtime type to use
320        ///
321        /// # Examples
322        ///
323        /// ```rust,no_run
324        /// # #[cfg(feature = "tokio")]
325        /// # fn example() -> Result<(), cel_cxx::Error> {
326        /// use cel_cxx::*;
327        /// use cel_cxx::r#async::Tokio;
328        ///
329        /// let env = Env::builder().build()?;
330        /// let program = env.compile("42")?;
331        ///
332        /// let async_program = program.use_runtime::<Tokio>();
333        /// # Ok::<(), cel_cxx::Error>(())
334        /// # }
335        /// ```
336        pub fn use_runtime<Rt: Runtime>(self) -> Program<'f, Fm, Rt> {
337            Program {
338                inner: self.inner,
339                _fn_marker: self._fn_marker,
340                _rt_marker: std::marker::PhantomData,
341            }
342        }
343
344        /// Configures this program to use the Tokio async runtime.
345        ///
346        /// This is a convenience method equivalent to `use_runtime::<Tokio>()`.
347        ///
348        /// # Examples
349        ///
350        /// ```rust,no_run
351        /// # #[cfg(feature = "tokio")]
352        /// # fn example() -> Result<(), cel_cxx::Error> {
353        /// use cel_cxx::*;
354        ///
355        /// let env = Env::builder().build()?;
356        /// let program = env.compile("42")?;
357        ///
358        /// let tokio_program = program.use_tokio();
359        /// # Ok::<(), cel_cxx::Error>(())
360        /// # }
361        /// ```
362        #[cfg(feature = "tokio")]
363        #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
364        pub fn use_tokio(self) -> Program<'f, Fm, Tokio> {
365            self.use_runtime::<Tokio>()
366        }
367
368        /// Configures this program to use the async-std runtime.
369        ///
370        /// This is a convenience method equivalent to `use_runtime::<AsyncStd>()`.
371        ///
372        /// # Examples
373        ///
374        /// ```rust,no_run
375        /// # #[cfg(feature = "async-std")]
376        /// # fn example() -> Result<(), cel_cxx::Error> {
377        /// use cel_cxx::*;
378        ///
379        /// let env = Env::builder().build()?;
380        /// let program = env.compile("42")?;
381        ///
382        /// let async_std_program = program.use_async_std();
383        /// # Ok::<(), cel_cxx::Error>(())
384        /// # }
385        /// ```
386        #[cfg(feature = "async-std")]
387        #[cfg_attr(docsrs, doc(cfg(feature = "async-std")))]
388        pub fn use_async_std(self) -> Program<'f, Fm, AsyncStd> {
389            self.use_runtime::<AsyncStd>()
390        }
391    }
392};
393
394impl<'f, Fm: FnMarker, Rm: RuntimeMarker> std::fmt::Debug for Program<'f, Fm, Rm> {
395    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
396        f.debug_struct("Program")
397            .field("inner", &self.inner)
398            .finish()
399    }
400}
401
402impl<'f, Fm: FnMarker, Rm: RuntimeMarker> Clone for Program<'f, Fm, Rm> {
403    fn clone(&self) -> Self {
404        Program {
405            inner: self.inner.clone(),
406            _fn_marker: self._fn_marker,
407            _rt_marker: self._rt_marker,
408        }
409    }
410}
411
412#[cfg(test)]
413mod test {
414    #![allow(unused)]
415
416    use super::*;
417    use crate::Activation;
418
419    fn assert_eval_type<'f>(program: Program<'f, (), ()>, activation: Activation<'f, ()>) {
420        let _result: Result<Value, Error> = program.evaluate(activation);
421    }
422
423    #[cfg(feature = "async")]
424    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
425    const _: () = {
426        use crate::r#async::Tokio;
427        use crate::Async;
428        use futures::future::BoxFuture;
429
430        #[cfg(feature = "tokio")]
431        const _: () = {
432            fn assert_eval_type_async1<'f>(
433                program: Program<'f, Async, Tokio>,
434                activation: Activation<'f, ()>,
435            ) {
436                let _result: BoxFuture<'f, Result<Value, Error>> = program.evaluate(activation);
437            }
438
439            fn assert_eval_type_async2<'f>(
440                program: Program<'f, (), Tokio>,
441                activation: Activation<'f, Async>,
442            ) {
443                let _result: BoxFuture<'f, Result<Value, Error>> = program.evaluate(activation);
444            }
445
446            fn assert_eval_type_async3<'f>(
447                program: Program<'f, Async, Tokio>,
448                activation: Activation<'f, Async>,
449            ) {
450                let _result: BoxFuture<'f, Result<Value, Error>> = program.evaluate(activation);
451            }
452
453            fn assert_eval_type_async4<'f>(
454                program: Program<'f, Async, ()>,
455                activation: Activation<'f, Async>,
456            ) {
457                let _result: BoxFuture<'f, Result<Value, Error>> =
458                    program.use_tokio().evaluate(activation);
459            }
460        };
461    };
462}