Struct EnvBuilder

Source
pub struct EnvBuilder<'f, Fm: FnMarker = (), Rm: RuntimeMarker = ()> { /* private fields */ }
Expand description

Builder for creating CEL environments.

The EnvBuilder allows you to configure a CEL environment by registering functions, declaring variables, and setting up runtime options before building the final environment.

§Type Parameters

  • 'f: Lifetime of functions that will be registered
  • Fm: Function marker type indicating sync/async function support
  • Rm: Runtime marker type indicating the async runtime (if any)

§Examples

use cel_cxx::*;

let env = Env::builder()
    .register_global_function("double", |x: i64| -> i64 { x * 2 })?
    .declare_variable::<String>("message")?
    .build()?;

Implementations§

Source§

impl<'f, Fm: FnMarker, Rm: RuntimeMarker> EnvBuilder<'f, Fm, Rm>

Source

pub fn new() -> Self

Creates a new environment builder.

§Examples
use cel_cxx::*;

let builder = EnvBuilder::<()>::new();
Source§

impl<'f, Fm: FnMarker, Rm: RuntimeMarker> EnvBuilder<'f, Fm, Rm>

Source

pub fn register_function<F, Ffm, Args>( self, name: impl Into<String>, member: bool, f: F, ) -> Result<EnvBuilder<'f, <Ffm as FnMarkerAggr<Fm>>::Output, Rm>, Error>
where F: IntoFunction<'f, Ffm, Args>, Ffm: FnMarker + FnMarkerAggr<Fm>, Args: Arguments,

Registers a function (either global or member).

This method allows you to register custom functions that can be called from CEL expressions. The function can be either a global function or a member function of a type.

§Function Registration Process

When you register a function, the system:

  1. Extracts type information from the function signature
  2. Creates type-safe conversion wrappers
  3. Stores both the type signature and implementation
  4. Updates the function marker type to track sync/async status
§Zero-Annotation Benefits

Functions are registered without explicit type annotations:

  • Argument types are automatically inferred
  • Return types are automatically determined
  • Error handling is automatically supported for Result<T, E> returns
  • Reference parameters like &str are handled safely
§Arguments
  • name - The name of the function as it will appear in CEL expressions
  • member - Whether this is a member function (true) or global function (false)
  • f - The function implementation (function pointer, closure, etc.)
§Type Parameters
  • F - The function implementation type
  • Ffm - The function marker type (sync/async) inferred from the function
  • Args - The argument tuple type (automatically inferred)
§Returns

A new EnvBuilder with updated function marker type. If this is the first async function registered, the marker changes from () to Async.

§Member vs Global Functions
§Global Functions

Called as function_name(args...):

max(a, b)           // max function with two arguments
calculate(x, y, z)  // calculate function with three arguments
§Member Functions

Called as object.method(args...):

text.contains(substring)    // contains method on string
list.size()                // size method on list
§Function Signature Support

Supports various function signatures:

  • Simple functions: fn(T) -> U
  • Functions with errors: fn(T) -> Result<U, E>
  • Reference parameters: fn(&str, i64) -> String
  • Multiple parameters: Up to 10 parameters supported
  • Closures: Move closures that capture environment
§Errors

Returns Error if:

  • Function name conflicts with existing registration
  • Function signature is invalid or unsupported
  • Type inference fails
§Examples
§Basic Functions
use cel_cxx::*;

let builder = Env::builder()
    .register_function("add", false, |a: i64, b: i64| a + b)?
    .register_function("greet", false, |name: &str| format!("Hello, {}!", name))?;
§Member Functions
use cel_cxx::*;

let builder = Env::builder()
    .register_function("contains", true, |text: &str, substr: &str| text.contains(substr))?
    .register_function("length", true, |text: &str| text.len() as i64)?;

// Usage in expressions:
// text.contains("hello")
// text.length()
§Functions with Error Handling
use cel_cxx::*;

let builder = Env::builder()
    .register_function("divide", false, |a: f64, b: f64| -> Result<f64, Error> {
        if b == 0.0 {
            Err(Error::invalid_argument("division by zero"))
        } else {
            Ok(a / b)
        }
    })?;
§Closures with Captured Data
use cel_cxx::*;

let multiplier = 5;
let threshold = 100.0;

let builder = Env::builder()
    .register_function("scale", false, move |x: i64| x * multiplier)?
    .register_function("check_limit", false, move |value: f64| value < threshold)?;
Source

pub fn register_member_function<F, Ffm, Args>( self, name: impl Into<String>, f: F, ) -> Result<EnvBuilder<'f, <Ffm as FnMarkerAggr<Fm>>::Output, Rm>, Error>
where F: IntoFunction<'f, Ffm, Args>, Ffm: FnMarker + FnMarkerAggr<Fm>, Args: Arguments,

Registers a member function.

This is a convenience method for registering member functions, equivalent to calling register_function(name, true, f). Member functions are called using dot notation in CEL expressions: object.method(args...).

§Arguments
  • name - The method name as it will appear in CEL expressions
  • f - The function implementation
§Member Function Semantics

Member functions in CEL follow these patterns:

  • First parameter is the “receiver” (the object before the dot)
  • Additional parameters become method arguments
  • Called as receiver.method(arg1, arg2, ...)
§Examples
§String Methods
use cel_cxx::*;

let builder = Env::builder()
    .register_member_function("upper", |s: &str| s.to_uppercase())?
    .register_member_function("contains", |s: &str, substr: &str| s.contains(substr))?
    .register_member_function("repeat", |s: &str, n: i64| s.repeat(n as usize))?;

// Usage in expressions:
// "hello".upper()           -> "HELLO"
// "hello world".contains("world") -> true
// "abc".repeat(3)           -> "abcabcabc"
§Numeric Methods
use cel_cxx::*;

let builder = Env::builder()
    .register_member_function("abs", |x: f64| x.abs())?
    .register_member_function("pow", |x: f64, exp: f64| x.powf(exp))?;

// Usage in expressions:
// (-5.5).abs()     -> 5.5
// (2.0).pow(3.0)   -> 8.0
Examples found in repository?
examples/comprehensive.rs (line 275)
269fn demo3_opaque_member_functions() -> Result<(), Error> {
270    println!("📌 Demo 3: Opaque Types & Member Functions");
271
272    let env = Env::builder()
273        .declare_variable::<Student>("student")?
274        // ✨ Register struct methods directly using RustType::method_name syntax
275        .register_member_function("get_name", Student::get_name)?
276        .register_member_function("get_age", Student::get_age)?
277        .register_member_function("get_grade", Student::get_grade)?
278        .register_member_function("is_passing", Student::is_passing)?
279        .register_member_function("has_subject", Student::has_subject)?
280        .register_member_function("get_letter_grade", Student::get_letter_grade)?
281        .build()?;
282
283    let student = Student {
284        name: "John Doe".to_string(),
285        age: 18,
286        grade: 87.5,
287        subjects: vec!["Math".to_string(), "Physics".to_string()],
288    };
289
290    let activation = Activation::new().bind_variable("student", student)?;
291
292    let test_expressions = vec![
293        ("student.get_name()", "Get student name"),
294        ("student.get_age()", "Get student age"),
295        ("student.get_grade()", "Get numerical grade"),
296        ("student.get_letter_grade()", "Get letter grade"),
297        ("student.is_passing()", "Check if passing"),
298        ("student.has_subject('Math')", "Check if has Math subject"),
299        (
300            "student.has_subject('Chemistry')",
301            "Check if has Chemistry subject",
302        ),
303    ];
304
305    for (expr, description) in test_expressions {
306        let program = env.compile(expr)?;
307        let result = program.evaluate(&activation)?;
308        println!("  {} = {} ({})", expr, result, description);
309    }
310
311    println!();
312    Ok(())
313}
314
315/// Demo 4: Type conversions and standard Rust types
316fn demo4_type_conversions() -> Result<(), Error> {
317    println!("📌 Demo 4: Type Conversions & Standard Rust Types");
318
319    // Functions returning different types (both direct values and Results)
320    fn return_string_direct() -> String {
321        "hello world".to_string()
322    }
323    fn return_int_result() -> Result<i64, std::io::Error> {
324        Ok(42)
325    }
326    fn return_list() -> Vec<i64> {
327        vec![1, 2, 3, 4, 5]
328    }
329    fn return_map() -> HashMap<String, i64> {
330        let mut map = HashMap::new();
331        map.insert("key1".to_string(), 100);
332        map.insert("key2".to_string(), 200);
333        map
334    }
335    fn return_optional_some() -> Option<String> {
336        Some("optional value".to_string())
337    }
338    fn return_optional_none() -> Option<String> {
339        None
340    }
341
342    let env = Env::builder()
343        .register_global_function("return_string_direct", return_string_direct)?
344        .register_global_function("return_int_result", return_int_result)?
345        .register_global_function("return_list", return_list)?
346        .register_global_function("return_map", return_map)?
347        .register_global_function("return_optional_some", return_optional_some)?
348        .register_global_function("return_optional_none", return_optional_none)?
349        .build()?;
350
351    let test_cases = vec![
352        ("return_string_direct()", "String conversion"),
353        ("return_int_result()", "Result<i64> conversion"),
354        ("return_list()", "Vec<i64> conversion"),
355        ("return_map()", "HashMap conversion"),
356        ("return_optional_some()", "Option<String> Some conversion"),
357        ("return_optional_none()", "Option<String> None conversion"),
358    ];
359
360    for (expr, description) in test_cases {
361        let program = env.compile(expr)?;
362        let result = program.evaluate(())?;
363        println!("  {} = {} ({})", expr, result, description);
364
365        // Demonstrate type conversion back to Rust types
366        match expr {
367            "return_string_direct()" => {
368                let rust_string: String = result
369                    .try_into()
370                    .map_err(|_| Error::invalid_argument("string conversion failed".to_string()))?;
371                println!("    Converted back to Rust String: '{}'", rust_string);
372            }
373            "return_int_result()" => {
374                let rust_int: i64 = result
375                    .try_into()
376                    .map_err(|_| Error::invalid_argument("int conversion failed".to_string()))?;
377                println!("    Converted back to Rust i64: {}", rust_int);
378            }
379            "return_list()" => {
380                let rust_list: Vec<i64> = result
381                    .try_into()
382                    .map_err(|_| Error::invalid_argument("list conversion failed".to_string()))?;
383                println!("    Converted back to Rust Vec<i64>: {:?}", rust_list);
384            }
385            _ => {}
386        }
387    }
388
389    println!();
390    Ok(())
391}
392
393/// Demo 5: Generic functions and type annotations
394fn demo5_generic_functions() -> Result<(), Error> {
395    println!("📌 Demo 5: Generic Functions & Function Overloads");
396
397    // Generic function that counts items in any Vec<T>
398    fn count_items<T>(items: Vec<T>) -> i64 {
399        items.len() as i64
400    }
401
402    // Generic function that processes maps
403    fn get_map_size<K, V>(map: HashMap<K, V>) -> i64 {
404        map.len() as i64
405    }
406
407    // Function working with references in containers
408    fn join_strings(strings: Vec<&str>, separator: &str) -> String {
409        strings.join(separator)
410    }
411
412    let env = Env::builder()
413        .declare_variable::<Vec<String>>("string_list")?
414        .declare_variable::<Vec<i64>>("int_list")?
415        .declare_variable::<HashMap<String, i64>>("score_map")?
416        .declare_variable::<Vec<f64>>("floats")?
417        // Register generic functions with specific type annotations
418        .register_global_function("count_strings", count_items::<String>)?
419        .register_global_function("count_ints", count_items::<i64>)?
420        .register_global_function("get_string_map_size", get_map_size::<String, i64>)?
421        .register_global_function("join_strings", join_strings)?
422        // Multiple functions with same name, different signatures (overloads)
423        .register_global_function("process", |x: i64| x * 2)?
424        .register_global_function("process", |x: f64| (x * 2.0).round())?
425        .register_global_function("process", |x: String| x.to_uppercase())?
426        // Overloaded member functions for different container types
427        .register_member_function("sum", |numbers: Vec<i64>| numbers.iter().sum::<i64>())?
428        .register_member_function("sum", |floats: Vec<f64>| floats.iter().sum::<f64>())?
429        .build()?;
430
431    let test_cases = vec![
432        // Generic function tests
433        ("count_strings(string_list)", "Count strings in list"),
434        ("count_ints(int_list)", "Count integers in list"),
435        ("get_string_map_size(score_map)", "Get map size"),
436        ("join_strings(string_list, ' ')", "Join strings with space"),
437        // Function overload tests
438        ("process(42)", "Process integer (multiply by 2)"),
439        ("process(3.14)", "Process float (multiply by 2, round)"),
440        ("process('hello')", "Process string (uppercase)"),
441        ("int_list.sum()", "Sum integers"),
442        ("floats.sum()", "Sum floats"),
443    ];
444
445    let activation = Activation::new()
446        .bind_variable(
447            "string_list",
448            vec!["hello".to_string(), "world".to_string(), "rust".to_string()],
449        )?
450        .bind_variable("int_list", vec![1, 2, 3, 4, 5, 6])?
451        .bind_variable("floats", vec![1.5, 2.7, 3.143, 4.0])?
452        .bind_variable("score_map", {
453            let mut map = HashMap::new();
454            map.insert("alice".to_string(), 95);
455            map.insert("bob".to_string(), 87);
456            map.insert("charlie".to_string(), 92);
457            map
458        })?;
459
460    for (expr, description) in test_cases {
461        let program = env.compile(expr)?;
462        let result = program.evaluate(&activation)?;
463        println!("  {} = {} ({})", expr, result, description);
464    }
465
466    println!();
467    Ok(())
468}
Source

pub fn register_global_function<F, Ffm, Args>( self, name: impl Into<String>, f: F, ) -> Result<EnvBuilder<'f, <Ffm as FnMarkerAggr<Fm>>::Output, Rm>, Error>
where F: IntoFunction<'f, Ffm, Args>, Ffm: FnMarker + FnMarkerAggr<Fm>, Args: Arguments,

Registers a global function.

This is a convenience method for registering global functions, equivalent to calling register_function(name, false, f). Global functions are called directly by name in CEL expressions: function_name(args...).

§Arguments
  • name - The function name as it will appear in CEL expressions
  • f - The function implementation
§Global Function Characteristics

Global functions:

  • Are called directly by name without a receiver object
  • Can have 0 to 10 parameters
  • Support all CEL-compatible parameter and return types
  • Can capture environment variables (for closures)
§Function Naming Guidelines
  • Use clear, descriptive names: calculate_tax, format_date
  • Follow CEL naming conventions (snake_case is recommended)
  • Avoid conflicts with built-in CEL functions
  • Consider namespacing for domain-specific functions: math_sqrt, string_trim
§Examples
§Mathematical Functions
use cel_cxx::*;

let builder = Env::builder()
    .register_global_function("add", |a: i64, b: i64| a + b)?
    .register_global_function("multiply", |a: f64, b: f64| a * b)?
    .register_global_function("max", |a: i64, b: i64| if a > b { a } else { b })?;

// Usage in expressions:
// add(10, 20)          -> 30
// multiply(2.5, 4.0)   -> 10.0
// max(15, 8)           -> 15
§String Processing Functions
use cel_cxx::*;

let builder = Env::builder()
    .register_global_function("concat", |a: &str, b: &str| format!("{}{}", a, b))?
    .register_global_function("trim_prefix", |s: &str, prefix: &str| {
        s.strip_prefix(prefix).unwrap_or(s).to_string()
    })?;

// Usage in expressions:
// concat("Hello, ", "World!")     -> "Hello, World!"
// trim_prefix("prefixed_text", "prefixed_")  -> "text"
§Business Logic Functions
use cel_cxx::*;

let builder = Env::builder()
    .register_global_function("calculate_discount", |price: f64, rate: f64| {
        price * (1.0 - rate.min(1.0).max(0.0))
    })?
    .register_global_function("is_valid_email", |email: &str| {
        email.contains('@') && email.contains('.')
    })?;

// Usage in expressions:
// calculate_discount(100.0, 0.15)     -> 85.0
// is_valid_email("user@domain.com")   -> true
§Functions with Complex Logic
use cel_cxx::*;
use std::collections::HashMap;

// Function that processes collections
let builder = Env::builder()
    .register_global_function("sum_positive", |numbers: Vec<i64>| {
        numbers.iter().filter(|&x| *x > 0).sum::<i64>()
    })?;

// Usage in expressions:
// sum_positive([1, -2, 3, -4, 5])  -> 9
Examples found in repository?
examples/basic.rs (line 14)
7fn main() -> Result<(), Error> {
8    println!("🚀 CEL-CXX Basic Example\n");
9
10    // Create an environment with variables and functions
11    let env = Env::builder()
12        .declare_variable::<String>("name")?
13        .declare_variable::<i64>("age")?
14        .register_global_function("greet", |name: &str| format!("Hello, {}!", name))?
15        .register_global_function("is_adult", |age: i64| age >= 18)?
16        .build()?;
17
18    // Compile and evaluate expressions
19    let expressions = vec![
20        "greet(name)",
21        "is_adult(age)",
22        "'Name: ' + name + ', Age: ' + string(age)",
23        "age >= 18 ? 'adult' : 'minor'",
24    ];
25
26    let activation = Activation::new()
27        .bind_variable("name", "Alice")?
28        .bind_variable("age", 25i64)?;
29
30    for expr in expressions {
31        let program = env.compile(expr)?;
32        let result = program.evaluate(&activation)?;
33        println!("{} = {}", expr, result);
34    }
35
36    println!("\n✅ Basic example completed!");
37    Ok(())
38}
More examples
Hide additional examples
examples/comprehensive.rs (line 154)
146fn demo1_basic_operations() -> Result<(), Error> {
147    println!("📌 Demo 1: Basic Expressions & Zero-Annotation Functions");
148
149    let env = Env::builder()
150        .declare_variable::<String>("name")?
151        .declare_variable::<i64>("age")?
152        .declare_variable::<f64>("score")?
153        // ✨ Zero-annotation functions - types automatically inferred!
154        .register_global_function("greet", |name: &str| format!("Hello, {}!", name))?
155        .register_global_function("is_adult", |age: i64| age >= 18)?
156        .register_global_function("grade", |score: f64| -> String {
157            match score {
158                90.0..=100.0 => "A".to_string(),
159                80.0..=89.9 => "B".to_string(),
160                70.0..=79.9 => "C".to_string(),
161                60.0..=69.9 => "D".to_string(),
162                _ => "F".to_string(),
163            }
164        })?
165        .register_global_function("calculate_discount", |age: i64, score: f64| -> f64 {
166            let base_discount = if age >= 65 { 0.2 } else { 0.0 };
167            let score_bonus = if score >= 90.0 { 0.1 } else { 0.0 };
168            base_discount + score_bonus
169        })?
170        .build()?;
171
172    // Test basic expressions including simple arithmetic
173    let test_cases = vec![
174        ("1 + 1", "Alice", 25i64, 95.0, "Simple arithmetic"),
175        (
176            "greet(name)",
177            "Alice",
178            25i64,
179            95.0,
180            "Function with string parameter",
181        ),
182        ("is_adult(age)", "Bob", 16i64, 85.0, "Boolean function"),
183        (
184            "grade(score)",
185            "Charlie",
186            30i64,
187            78.5,
188            "String return function",
189        ),
190        (
191            "calculate_discount(age, score)",
192            "Diana",
193            67i64,
194            92.0,
195            "Multi-parameter function",
196        ),
197    ];
198
199    for (expr, name, age, score, description) in test_cases {
200        let program = env.compile(expr)?;
201        let activation = Activation::new()
202            .bind_variable("name", name)?
203            .bind_variable("age", age)?
204            .bind_variable("score", score)?;
205
206        let result = program.evaluate(&activation)?;
207        println!("  {} = {} ({})", expr, result, description);
208    }
209
210    println!();
211    Ok(())
212}
213
214/// Demo 2: Variable binding and providers
215fn demo2_variable_operations() -> Result<(), Error> {
216    println!("📌 Demo 2: Variable Binding & Providers");
217
218    let env = Env::builder()
219        .declare_variable::<i64>("a")?
220        .declare_variable::<i64>("b")?
221        .declare_global_function::<fn() -> i64>("get_const")?
222        .register_global_function("multiply", |x: i64, y: i64| x * y)?
223        .build()?;
224
225    // Test direct variable binding
226    {
227        println!("  Variable binding:");
228        let program = env.compile("a + b")?;
229        let activation = Activation::new()
230            .bind_variable("a", 10)?
231            .bind_variable("b", 20)?;
232        let result = program.evaluate(&activation)?;
233        println!("    a + b = {} (direct binding)", result);
234    }
235
236    // Test variable provider binding
237    {
238        println!("  Variable provider binding:");
239        let program = env.compile("a * b")?;
240        let activation = Activation::new()
241            .bind_variable("a", 5)?
242            .bind_variable_provider("b", || -> Result<i64, Error> {
243                println!("    Provider called for variable 'b'");
244                Ok(7)
245            })?;
246        let result = program.evaluate(&activation)?;
247        println!("    a * b = {} (provider binding)", result);
248    }
249
250    // Test function declaration and binding
251    {
252        println!("  Function declaration & binding:");
253        let program = env.compile("get_const() + multiply(a, 3)")?;
254        let activation = Activation::new()
255            .bind_variable("a", 4)?
256            .bind_global_function("get_const", || -> Result<i64, Error> { Ok(100) })?;
257        let result = program.evaluate(&activation)?;
258        println!(
259            "    get_const() + multiply(a, 3) = {} (function binding)",
260            result
261        );
262    }
263
264    println!();
265    Ok(())
266}
267
268/// Demo 3: Opaque types with member functions
269fn demo3_opaque_member_functions() -> Result<(), Error> {
270    println!("📌 Demo 3: Opaque Types & Member Functions");
271
272    let env = Env::builder()
273        .declare_variable::<Student>("student")?
274        // ✨ Register struct methods directly using RustType::method_name syntax
275        .register_member_function("get_name", Student::get_name)?
276        .register_member_function("get_age", Student::get_age)?
277        .register_member_function("get_grade", Student::get_grade)?
278        .register_member_function("is_passing", Student::is_passing)?
279        .register_member_function("has_subject", Student::has_subject)?
280        .register_member_function("get_letter_grade", Student::get_letter_grade)?
281        .build()?;
282
283    let student = Student {
284        name: "John Doe".to_string(),
285        age: 18,
286        grade: 87.5,
287        subjects: vec!["Math".to_string(), "Physics".to_string()],
288    };
289
290    let activation = Activation::new().bind_variable("student", student)?;
291
292    let test_expressions = vec![
293        ("student.get_name()", "Get student name"),
294        ("student.get_age()", "Get student age"),
295        ("student.get_grade()", "Get numerical grade"),
296        ("student.get_letter_grade()", "Get letter grade"),
297        ("student.is_passing()", "Check if passing"),
298        ("student.has_subject('Math')", "Check if has Math subject"),
299        (
300            "student.has_subject('Chemistry')",
301            "Check if has Chemistry subject",
302        ),
303    ];
304
305    for (expr, description) in test_expressions {
306        let program = env.compile(expr)?;
307        let result = program.evaluate(&activation)?;
308        println!("  {} = {} ({})", expr, result, description);
309    }
310
311    println!();
312    Ok(())
313}
314
315/// Demo 4: Type conversions and standard Rust types
316fn demo4_type_conversions() -> Result<(), Error> {
317    println!("📌 Demo 4: Type Conversions & Standard Rust Types");
318
319    // Functions returning different types (both direct values and Results)
320    fn return_string_direct() -> String {
321        "hello world".to_string()
322    }
323    fn return_int_result() -> Result<i64, std::io::Error> {
324        Ok(42)
325    }
326    fn return_list() -> Vec<i64> {
327        vec![1, 2, 3, 4, 5]
328    }
329    fn return_map() -> HashMap<String, i64> {
330        let mut map = HashMap::new();
331        map.insert("key1".to_string(), 100);
332        map.insert("key2".to_string(), 200);
333        map
334    }
335    fn return_optional_some() -> Option<String> {
336        Some("optional value".to_string())
337    }
338    fn return_optional_none() -> Option<String> {
339        None
340    }
341
342    let env = Env::builder()
343        .register_global_function("return_string_direct", return_string_direct)?
344        .register_global_function("return_int_result", return_int_result)?
345        .register_global_function("return_list", return_list)?
346        .register_global_function("return_map", return_map)?
347        .register_global_function("return_optional_some", return_optional_some)?
348        .register_global_function("return_optional_none", return_optional_none)?
349        .build()?;
350
351    let test_cases = vec![
352        ("return_string_direct()", "String conversion"),
353        ("return_int_result()", "Result<i64> conversion"),
354        ("return_list()", "Vec<i64> conversion"),
355        ("return_map()", "HashMap conversion"),
356        ("return_optional_some()", "Option<String> Some conversion"),
357        ("return_optional_none()", "Option<String> None conversion"),
358    ];
359
360    for (expr, description) in test_cases {
361        let program = env.compile(expr)?;
362        let result = program.evaluate(())?;
363        println!("  {} = {} ({})", expr, result, description);
364
365        // Demonstrate type conversion back to Rust types
366        match expr {
367            "return_string_direct()" => {
368                let rust_string: String = result
369                    .try_into()
370                    .map_err(|_| Error::invalid_argument("string conversion failed".to_string()))?;
371                println!("    Converted back to Rust String: '{}'", rust_string);
372            }
373            "return_int_result()" => {
374                let rust_int: i64 = result
375                    .try_into()
376                    .map_err(|_| Error::invalid_argument("int conversion failed".to_string()))?;
377                println!("    Converted back to Rust i64: {}", rust_int);
378            }
379            "return_list()" => {
380                let rust_list: Vec<i64> = result
381                    .try_into()
382                    .map_err(|_| Error::invalid_argument("list conversion failed".to_string()))?;
383                println!("    Converted back to Rust Vec<i64>: {:?}", rust_list);
384            }
385            _ => {}
386        }
387    }
388
389    println!();
390    Ok(())
391}
392
393/// Demo 5: Generic functions and type annotations
394fn demo5_generic_functions() -> Result<(), Error> {
395    println!("📌 Demo 5: Generic Functions & Function Overloads");
396
397    // Generic function that counts items in any Vec<T>
398    fn count_items<T>(items: Vec<T>) -> i64 {
399        items.len() as i64
400    }
401
402    // Generic function that processes maps
403    fn get_map_size<K, V>(map: HashMap<K, V>) -> i64 {
404        map.len() as i64
405    }
406
407    // Function working with references in containers
408    fn join_strings(strings: Vec<&str>, separator: &str) -> String {
409        strings.join(separator)
410    }
411
412    let env = Env::builder()
413        .declare_variable::<Vec<String>>("string_list")?
414        .declare_variable::<Vec<i64>>("int_list")?
415        .declare_variable::<HashMap<String, i64>>("score_map")?
416        .declare_variable::<Vec<f64>>("floats")?
417        // Register generic functions with specific type annotations
418        .register_global_function("count_strings", count_items::<String>)?
419        .register_global_function("count_ints", count_items::<i64>)?
420        .register_global_function("get_string_map_size", get_map_size::<String, i64>)?
421        .register_global_function("join_strings", join_strings)?
422        // Multiple functions with same name, different signatures (overloads)
423        .register_global_function("process", |x: i64| x * 2)?
424        .register_global_function("process", |x: f64| (x * 2.0).round())?
425        .register_global_function("process", |x: String| x.to_uppercase())?
426        // Overloaded member functions for different container types
427        .register_member_function("sum", |numbers: Vec<i64>| numbers.iter().sum::<i64>())?
428        .register_member_function("sum", |floats: Vec<f64>| floats.iter().sum::<f64>())?
429        .build()?;
430
431    let test_cases = vec![
432        // Generic function tests
433        ("count_strings(string_list)", "Count strings in list"),
434        ("count_ints(int_list)", "Count integers in list"),
435        ("get_string_map_size(score_map)", "Get map size"),
436        ("join_strings(string_list, ' ')", "Join strings with space"),
437        // Function overload tests
438        ("process(42)", "Process integer (multiply by 2)"),
439        ("process(3.14)", "Process float (multiply by 2, round)"),
440        ("process('hello')", "Process string (uppercase)"),
441        ("int_list.sum()", "Sum integers"),
442        ("floats.sum()", "Sum floats"),
443    ];
444
445    let activation = Activation::new()
446        .bind_variable(
447            "string_list",
448            vec!["hello".to_string(), "world".to_string(), "rust".to_string()],
449        )?
450        .bind_variable("int_list", vec![1, 2, 3, 4, 5, 6])?
451        .bind_variable("floats", vec![1.5, 2.7, 3.143, 4.0])?
452        .bind_variable("score_map", {
453            let mut map = HashMap::new();
454            map.insert("alice".to_string(), 95);
455            map.insert("bob".to_string(), 87);
456            map.insert("charlie".to_string(), 92);
457            map
458        })?;
459
460    for (expr, description) in test_cases {
461        let program = env.compile(expr)?;
462        let result = program.evaluate(&activation)?;
463        println!("  {} = {} ({})", expr, result, description);
464    }
465
466    println!();
467    Ok(())
468}
469
470/// Demo 6: Error handling with different error types including Box<dyn std::error::Error>
471fn demo6_error_handling() -> Result<(), Error> {
472    println!("📌 Demo 6: Error Handling & Validation");
473
474    let env = Env::builder()
475        .declare_variable::<String>("input")?
476        .declare_variable::<i64>("divisor")?
477        .declare_variable::<String>("email")?
478        // Functions returning different error types using thiserror-derived types
479        .register_global_function("safe_parse", |s: &str| -> Result<i64, ValidationError> {
480            s.parse::<i64>()
481                .map_err(|e| ValidationError::ParseError(e.to_string()))
482        })?
483        .register_global_function(
484            "safe_divide",
485            |a: i64, b: i64| -> Result<f64, ValidationError> {
486                if b == 0 {
487                    Err(ValidationError::DivisionByZero)
488                } else {
489                    Ok(a as f64 / b as f64)
490                }
491            },
492        )?
493        // Additional validation functions with ValidationError
494        .register_global_function(
495            "validate_email",
496            |email: &str| -> Result<bool, ValidationError> {
497                if email.is_empty() {
498                    return Err(ValidationError::EmailError(
499                        "Email cannot be empty".to_string(),
500                    ));
501                }
502                if !email.contains('@') {
503                    return Err(ValidationError::EmailError(
504                        "Email must contain @ symbol".to_string(),
505                    ));
506                }
507                if !email.contains('.') {
508                    return Err(ValidationError::EmailError(
509                        "Email must contain . symbol".to_string(),
510                    ));
511                }
512                let parts: Vec<&str> = email.split('@').collect();
513                if parts.len() != 2 {
514                    return Err(ValidationError::EmailError(
515                        "Email must contain exactly one @ symbol".to_string(),
516                    ));
517                }
518                if parts[0].is_empty() || parts[1].is_empty() {
519                    return Err(ValidationError::EmailError(
520                        "Email local and domain parts cannot be empty".to_string(),
521                    ));
522                }
523                Ok(true)
524            },
525        )?
526        .register_global_function(
527            "validate_age",
528            |age: i64| -> Result<String, ValidationError> {
529                match age {
530                    0..=17 => Ok("minor".to_string()),
531                    18..=64 => Ok("adult".to_string()),
532                    65..=120 => Ok("senior".to_string()),
533                    _ => Err(ValidationError::AgeError(format!("Invalid age: {}", age))),
534                }
535            },
536        )?
537        .register_global_function(
538            "validate_range",
539            |x: i64, min: i64, max: i64| -> Result<i64, ValidationError> {
540                if x < min || x > max {
541                    Err(ValidationError::RangeError { value: x, min, max })
542                } else {
543                    Ok(x)
544                }
545            },
546        )?
547        .build()?;
548
549    let test_cases = vec![
550        // Success cases
551        (
552            "safe_parse('42')",
553            "42",
554            2i64,
555            "valid@email.com",
556            true,
557            "Parse valid number",
558        ),
559        (
560            "safe_divide(10, divisor)",
561            "10",
562            2i64,
563            "test@example.com",
564            true,
565            "Safe division",
566        ),
567        (
568            "validate_email(email)",
569            "25",
570            1i64,
571            "user@domain.com",
572            true,
573            "Valid email",
574        ),
575        (
576            "validate_age(safe_parse(input))",
577            "25",
578            1i64,
579            "user@test.com",
580            true,
581            "Valid age",
582        ),
583        // Error cases
584        (
585            "safe_parse('invalid')",
586            "invalid",
587            2i64,
588            "test@test.com",
589            false,
590            "Parse invalid number",
591        ),
592        (
593            "safe_divide(10, divisor)",
594            "10",
595            0i64,
596            "test@test.com",
597            false,
598            "Division by zero",
599        ),
600        (
601            "validate_email(email)",
602            "10",
603            1i64,
604            "invalid-email",
605            false,
606            "Invalid email format",
607        ),
608        (
609            "validate_age(safe_parse(input))",
610            "150",
611            1i64,
612            "test@test.com",
613            false,
614            "Invalid age",
615        ),
616        (
617            "validate_range(safe_parse(input), 1, 100)",
618            "150",
619            2i64,
620            "test@test.com",
621            false,
622            "Value out of range",
623        ),
624    ];
625
626    for (expr, input, divisor, email, should_succeed, description) in test_cases {
627        let program = env.compile(expr)?;
628        let activation = Activation::new()
629            .bind_variable("input", input)?
630            .bind_variable("divisor", divisor)?
631            .bind_variable("email", email)?;
632
633        match program.evaluate(&activation) {
634            Ok(result) => {
635                println!("  {} = {} ✅ ({})", expr, result, description);
636                if !should_succeed {
637                    println!("    ⚠️  Expected this to fail!");
638                }
639            }
640            Err(e) => {
641                println!("  {} -> ERROR: {} ❌ ({})", expr, e, description);
642                if should_succeed {
643                    println!("    ⚠️  Expected this to succeed!");
644                }
645            }
646        }
647    }
648
649    println!();
650    Ok(())
651}
652
653/// Demo 7: Program introspection and return types
654fn demo7_program_introspection() -> Result<(), Error> {
655    println!("📌 Demo 7: Program Introspection & Return Types");
656
657    // Test with numeric expression
658    {
659        let env = Env::builder()
660            .declare_variable::<i64>("a")?
661            .declare_variable::<i64>("b")?
662            .build()?;
663        let program = env.compile("a + b")?;
664        let return_type = program.return_type();
665        println!("  Expression: 'a + b'");
666        println!("    Return type: {}", return_type);
667
668        let activation = Activation::new()
669            .bind_variable("a", 10)?
670            .bind_variable("b", 20)?;
671        let result = program.evaluate(&activation)?;
672        println!("    Result: {}", result);
673    }
674
675    // Test with string expression
676    {
677        let env = Env::builder()
678            .declare_variable::<String>("a")?
679            .declare_variable::<String>("b")?
680            .build()?;
681        let program = env.compile("a + b")?;
682        let return_type = program.return_type();
683        println!("  Expression: 'a + b' (strings)");
684        println!("    Return type: {}", return_type);
685
686        let activation = Activation::new()
687            .bind_variable("a", "Hello ".to_string())?
688            .bind_variable("b", "World!".to_string())?;
689        let result = program.evaluate(&activation)?;
690        println!("    Result: {}", result);
691    }
692
693    // Test with boolean expression
694    {
695        let env = Env::builder().declare_variable::<i64>("age")?.build()?;
696        let program = env.compile("age >= 18")?;
697        let return_type = program.return_type();
698        println!("  Expression: 'age >= 18'");
699        println!("    Return type: {}", return_type);
700
701        let activation = Activation::new().bind_variable("age", 25i64)?;
702        let result = program.evaluate(&activation)?;
703        println!("    Result: {}", result);
704    }
705
706    println!();
707    Ok(())
708}
709
710/// Demo 8: Advanced container operations and reference handling
711fn demo8_container_operations() -> Result<(), Error> {
712    println!("📌 Demo 8: Container Operations & Reference Handling");
713
714    let env = Env::builder()
715        .declare_variable::<Vec<&str>>("string_refs")?
716        .declare_variable::<HashMap<i64, &str>>("lookup_table")?
717        .declare_variable::<Option<&str>>("maybe_value")?
718        .declare_variable::<Vec<User>>("users")?
719        // Functions working with reference types
720        .register_global_function("longest_string", |strings: Vec<&str>| -> Option<String> {
721            strings
722                .iter()
723                .max_by_key(|s| s.len())
724                .map(|s| s.to_string())
725        })?
726        .register_global_function(
727            "lookup",
728            |table: HashMap<i64, &str>, key: i64| -> Option<String> {
729                table.get(&key).map(|s| s.to_string())
730            },
731        )?
732        .register_global_function("string_length", |s: Option<&str>| -> i64 {
733            s.map(|s| s.len() as i64).unwrap_or(0)
734        })?
735        // Advanced filtering and mapping
736        .register_global_function("filter_adults", |users: Vec<User>| -> Vec<User> {
737            users.into_iter().filter(|u| u.age >= 18).collect()
738        })?
739        .register_global_function(
740            "group_by_age_range",
741            |users: Vec<User>| -> HashMap<String, Vec<String>> {
742                let mut groups: HashMap<String, Vec<String>> = HashMap::new();
743                for user in users {
744                    let range = match user.age {
745                        0..=17 => "minor",
746                        18..=64 => "adult",
747                        _ => "senior",
748                    };
749                    groups.entry(range.to_string()).or_default().push(user.name);
750                }
751                groups
752            },
753        )?
754        .build()?;
755
756    // Prepare test data with proper lifetimes
757    let source_strings = [
758        "hello".to_string(),
759        "world".to_string(),
760        "rust".to_string(),
761        "programming".to_string(),
762    ];
763    let string_refs: Vec<&str> = source_strings.iter().map(|s| s.as_str()).collect();
764
765    let lookup_table: HashMap<i64, &str> = HashMap::from([(1, "one"), (2, "two"), (3, "three")]);
766
767    let maybe_value: Option<&str> = Some("test string");
768
769    let users = vec![
770        User {
771            id: 1,
772            name: "Alice".to_string(),
773            email: "alice@test.com".to_string(),
774            age: 25,
775            roles: vec![],
776            metadata: HashMap::new(),
777        },
778        User {
779            id: 2,
780            name: "Bob".to_string(),
781            email: "bob@test.com".to_string(),
782            age: 16,
783            roles: vec![],
784            metadata: HashMap::new(),
785        },
786        User {
787            id: 3,
788            name: "Carol".to_string(),
789            email: "carol@test.com".to_string(),
790            age: 67,
791            roles: vec![],
792            metadata: HashMap::new(),
793        },
794    ];
795
796    let test_expressions = vec![
797        ("longest_string(string_refs)", "Find longest string"),
798        ("lookup(lookup_table, 2)", "Lookup value by key"),
799        ("string_length(maybe_value)", "Get optional string length"),
800        ("filter_adults(users).size()", "Count adult users"),
801        (
802            "group_by_age_range(users).size()",
803            "Group users by age range",
804        ),
805    ];
806
807    let activation = Activation::new()
808        .bind_variable("string_refs", string_refs)?
809        .bind_variable("lookup_table", lookup_table)?
810        .bind_variable("maybe_value", maybe_value)?
811        .bind_variable("users", users)?;
812
813    for (expr, description) in test_expressions {
814        let program = env.compile(expr)?;
815        let result = program.evaluate(&activation)?;
816        println!("  {} = {} ({})", expr, result, description);
817    }
818
819    println!();
820    Ok(())
821}
Source

pub fn declare_function<D>( self, name: impl Into<String>, member: bool, ) -> Result<Self, Error>
where D: FunctionDecl,

Declares a function signature without providing an implementation.

This is useful when you want to declare that a function exists for type checking purposes, but will provide the implementation later via activation bindings.

§Arguments
  • name - The name of the function
  • member - Whether this is a member function (true) or global function (false)
§Type Parameters
  • D - The function declaration type that specifies the signature
Source

pub fn declare_member_function<D>( self, name: impl Into<String>, ) -> Result<Self, Error>
where D: FunctionDecl,

Declares a member function signature without providing an implementation.

§Arguments
  • name - The name of the member function
§Type Parameters
  • D - The function declaration type that specifies the signature
Source

pub fn declare_global_function<D>( self, name: impl Into<String>, ) -> Result<Self, Error>
where D: FunctionDecl,

Declares a global function signature without providing an implementation.

§Arguments
  • name - The name of the global function
§Type Parameters
  • D - The function declaration type that specifies the signature
Examples found in repository?
examples/comprehensive.rs (line 221)
215fn demo2_variable_operations() -> Result<(), Error> {
216    println!("📌 Demo 2: Variable Binding & Providers");
217
218    let env = Env::builder()
219        .declare_variable::<i64>("a")?
220        .declare_variable::<i64>("b")?
221        .declare_global_function::<fn() -> i64>("get_const")?
222        .register_global_function("multiply", |x: i64, y: i64| x * y)?
223        .build()?;
224
225    // Test direct variable binding
226    {
227        println!("  Variable binding:");
228        let program = env.compile("a + b")?;
229        let activation = Activation::new()
230            .bind_variable("a", 10)?
231            .bind_variable("b", 20)?;
232        let result = program.evaluate(&activation)?;
233        println!("    a + b = {} (direct binding)", result);
234    }
235
236    // Test variable provider binding
237    {
238        println!("  Variable provider binding:");
239        let program = env.compile("a * b")?;
240        let activation = Activation::new()
241            .bind_variable("a", 5)?
242            .bind_variable_provider("b", || -> Result<i64, Error> {
243                println!("    Provider called for variable 'b'");
244                Ok(7)
245            })?;
246        let result = program.evaluate(&activation)?;
247        println!("    a * b = {} (provider binding)", result);
248    }
249
250    // Test function declaration and binding
251    {
252        println!("  Function declaration & binding:");
253        let program = env.compile("get_const() + multiply(a, 3)")?;
254        let activation = Activation::new()
255            .bind_variable("a", 4)?
256            .bind_global_function("get_const", || -> Result<i64, Error> { Ok(100) })?;
257        let result = program.evaluate(&activation)?;
258        println!(
259            "    get_const() + multiply(a, 3) = {} (function binding)",
260            result
261        );
262    }
263
264    println!();
265    Ok(())
266}
Source

pub fn define_constant<T>( self, name: impl Into<String>, value: T, ) -> Result<Self, Error>
where T: IntoConstant,

Defines a constant value that can be referenced in expressions.

Constants are immutable values that are resolved at compile time.

§Arguments
  • name - The name of the constant
  • value - The constant value
§Examples
use cel_cxx::*;

let builder = Env::builder()
    .define_constant("PI", 3.14159)
    .unwrap();
Source

pub fn declare_variable<T>(self, name: impl Into<String>) -> Result<Self, Error>
where T: TypedValue,

Declares a variable of a specific type.

This declares that a variable of the given name and type may be provided during evaluation. The actual value must be bound in the activation when evaluating expressions.

§Arguments
  • name - The name of the variable
§Type Parameters
  • T - The type of the variable
§Examples
use cel_cxx::*;

let builder = Env::builder()
    .declare_variable::<String>("user_name")?
    .declare_variable::<i64>("age")?;
Examples found in repository?
examples/basic.rs (line 12)
7fn main() -> Result<(), Error> {
8    println!("🚀 CEL-CXX Basic Example\n");
9
10    // Create an environment with variables and functions
11    let env = Env::builder()
12        .declare_variable::<String>("name")?
13        .declare_variable::<i64>("age")?
14        .register_global_function("greet", |name: &str| format!("Hello, {}!", name))?
15        .register_global_function("is_adult", |age: i64| age >= 18)?
16        .build()?;
17
18    // Compile and evaluate expressions
19    let expressions = vec![
20        "greet(name)",
21        "is_adult(age)",
22        "'Name: ' + name + ', Age: ' + string(age)",
23        "age >= 18 ? 'adult' : 'minor'",
24    ];
25
26    let activation = Activation::new()
27        .bind_variable("name", "Alice")?
28        .bind_variable("age", 25i64)?;
29
30    for expr in expressions {
31        let program = env.compile(expr)?;
32        let result = program.evaluate(&activation)?;
33        println!("{} = {}", expr, result);
34    }
35
36    println!("\n✅ Basic example completed!");
37    Ok(())
38}
More examples
Hide additional examples
examples/comprehensive.rs (line 150)
146fn demo1_basic_operations() -> Result<(), Error> {
147    println!("📌 Demo 1: Basic Expressions & Zero-Annotation Functions");
148
149    let env = Env::builder()
150        .declare_variable::<String>("name")?
151        .declare_variable::<i64>("age")?
152        .declare_variable::<f64>("score")?
153        // ✨ Zero-annotation functions - types automatically inferred!
154        .register_global_function("greet", |name: &str| format!("Hello, {}!", name))?
155        .register_global_function("is_adult", |age: i64| age >= 18)?
156        .register_global_function("grade", |score: f64| -> String {
157            match score {
158                90.0..=100.0 => "A".to_string(),
159                80.0..=89.9 => "B".to_string(),
160                70.0..=79.9 => "C".to_string(),
161                60.0..=69.9 => "D".to_string(),
162                _ => "F".to_string(),
163            }
164        })?
165        .register_global_function("calculate_discount", |age: i64, score: f64| -> f64 {
166            let base_discount = if age >= 65 { 0.2 } else { 0.0 };
167            let score_bonus = if score >= 90.0 { 0.1 } else { 0.0 };
168            base_discount + score_bonus
169        })?
170        .build()?;
171
172    // Test basic expressions including simple arithmetic
173    let test_cases = vec![
174        ("1 + 1", "Alice", 25i64, 95.0, "Simple arithmetic"),
175        (
176            "greet(name)",
177            "Alice",
178            25i64,
179            95.0,
180            "Function with string parameter",
181        ),
182        ("is_adult(age)", "Bob", 16i64, 85.0, "Boolean function"),
183        (
184            "grade(score)",
185            "Charlie",
186            30i64,
187            78.5,
188            "String return function",
189        ),
190        (
191            "calculate_discount(age, score)",
192            "Diana",
193            67i64,
194            92.0,
195            "Multi-parameter function",
196        ),
197    ];
198
199    for (expr, name, age, score, description) in test_cases {
200        let program = env.compile(expr)?;
201        let activation = Activation::new()
202            .bind_variable("name", name)?
203            .bind_variable("age", age)?
204            .bind_variable("score", score)?;
205
206        let result = program.evaluate(&activation)?;
207        println!("  {} = {} ({})", expr, result, description);
208    }
209
210    println!();
211    Ok(())
212}
213
214/// Demo 2: Variable binding and providers
215fn demo2_variable_operations() -> Result<(), Error> {
216    println!("📌 Demo 2: Variable Binding & Providers");
217
218    let env = Env::builder()
219        .declare_variable::<i64>("a")?
220        .declare_variable::<i64>("b")?
221        .declare_global_function::<fn() -> i64>("get_const")?
222        .register_global_function("multiply", |x: i64, y: i64| x * y)?
223        .build()?;
224
225    // Test direct variable binding
226    {
227        println!("  Variable binding:");
228        let program = env.compile("a + b")?;
229        let activation = Activation::new()
230            .bind_variable("a", 10)?
231            .bind_variable("b", 20)?;
232        let result = program.evaluate(&activation)?;
233        println!("    a + b = {} (direct binding)", result);
234    }
235
236    // Test variable provider binding
237    {
238        println!("  Variable provider binding:");
239        let program = env.compile("a * b")?;
240        let activation = Activation::new()
241            .bind_variable("a", 5)?
242            .bind_variable_provider("b", || -> Result<i64, Error> {
243                println!("    Provider called for variable 'b'");
244                Ok(7)
245            })?;
246        let result = program.evaluate(&activation)?;
247        println!("    a * b = {} (provider binding)", result);
248    }
249
250    // Test function declaration and binding
251    {
252        println!("  Function declaration & binding:");
253        let program = env.compile("get_const() + multiply(a, 3)")?;
254        let activation = Activation::new()
255            .bind_variable("a", 4)?
256            .bind_global_function("get_const", || -> Result<i64, Error> { Ok(100) })?;
257        let result = program.evaluate(&activation)?;
258        println!(
259            "    get_const() + multiply(a, 3) = {} (function binding)",
260            result
261        );
262    }
263
264    println!();
265    Ok(())
266}
267
268/// Demo 3: Opaque types with member functions
269fn demo3_opaque_member_functions() -> Result<(), Error> {
270    println!("📌 Demo 3: Opaque Types & Member Functions");
271
272    let env = Env::builder()
273        .declare_variable::<Student>("student")?
274        // ✨ Register struct methods directly using RustType::method_name syntax
275        .register_member_function("get_name", Student::get_name)?
276        .register_member_function("get_age", Student::get_age)?
277        .register_member_function("get_grade", Student::get_grade)?
278        .register_member_function("is_passing", Student::is_passing)?
279        .register_member_function("has_subject", Student::has_subject)?
280        .register_member_function("get_letter_grade", Student::get_letter_grade)?
281        .build()?;
282
283    let student = Student {
284        name: "John Doe".to_string(),
285        age: 18,
286        grade: 87.5,
287        subjects: vec!["Math".to_string(), "Physics".to_string()],
288    };
289
290    let activation = Activation::new().bind_variable("student", student)?;
291
292    let test_expressions = vec![
293        ("student.get_name()", "Get student name"),
294        ("student.get_age()", "Get student age"),
295        ("student.get_grade()", "Get numerical grade"),
296        ("student.get_letter_grade()", "Get letter grade"),
297        ("student.is_passing()", "Check if passing"),
298        ("student.has_subject('Math')", "Check if has Math subject"),
299        (
300            "student.has_subject('Chemistry')",
301            "Check if has Chemistry subject",
302        ),
303    ];
304
305    for (expr, description) in test_expressions {
306        let program = env.compile(expr)?;
307        let result = program.evaluate(&activation)?;
308        println!("  {} = {} ({})", expr, result, description);
309    }
310
311    println!();
312    Ok(())
313}
314
315/// Demo 4: Type conversions and standard Rust types
316fn demo4_type_conversions() -> Result<(), Error> {
317    println!("📌 Demo 4: Type Conversions & Standard Rust Types");
318
319    // Functions returning different types (both direct values and Results)
320    fn return_string_direct() -> String {
321        "hello world".to_string()
322    }
323    fn return_int_result() -> Result<i64, std::io::Error> {
324        Ok(42)
325    }
326    fn return_list() -> Vec<i64> {
327        vec![1, 2, 3, 4, 5]
328    }
329    fn return_map() -> HashMap<String, i64> {
330        let mut map = HashMap::new();
331        map.insert("key1".to_string(), 100);
332        map.insert("key2".to_string(), 200);
333        map
334    }
335    fn return_optional_some() -> Option<String> {
336        Some("optional value".to_string())
337    }
338    fn return_optional_none() -> Option<String> {
339        None
340    }
341
342    let env = Env::builder()
343        .register_global_function("return_string_direct", return_string_direct)?
344        .register_global_function("return_int_result", return_int_result)?
345        .register_global_function("return_list", return_list)?
346        .register_global_function("return_map", return_map)?
347        .register_global_function("return_optional_some", return_optional_some)?
348        .register_global_function("return_optional_none", return_optional_none)?
349        .build()?;
350
351    let test_cases = vec![
352        ("return_string_direct()", "String conversion"),
353        ("return_int_result()", "Result<i64> conversion"),
354        ("return_list()", "Vec<i64> conversion"),
355        ("return_map()", "HashMap conversion"),
356        ("return_optional_some()", "Option<String> Some conversion"),
357        ("return_optional_none()", "Option<String> None conversion"),
358    ];
359
360    for (expr, description) in test_cases {
361        let program = env.compile(expr)?;
362        let result = program.evaluate(())?;
363        println!("  {} = {} ({})", expr, result, description);
364
365        // Demonstrate type conversion back to Rust types
366        match expr {
367            "return_string_direct()" => {
368                let rust_string: String = result
369                    .try_into()
370                    .map_err(|_| Error::invalid_argument("string conversion failed".to_string()))?;
371                println!("    Converted back to Rust String: '{}'", rust_string);
372            }
373            "return_int_result()" => {
374                let rust_int: i64 = result
375                    .try_into()
376                    .map_err(|_| Error::invalid_argument("int conversion failed".to_string()))?;
377                println!("    Converted back to Rust i64: {}", rust_int);
378            }
379            "return_list()" => {
380                let rust_list: Vec<i64> = result
381                    .try_into()
382                    .map_err(|_| Error::invalid_argument("list conversion failed".to_string()))?;
383                println!("    Converted back to Rust Vec<i64>: {:?}", rust_list);
384            }
385            _ => {}
386        }
387    }
388
389    println!();
390    Ok(())
391}
392
393/// Demo 5: Generic functions and type annotations
394fn demo5_generic_functions() -> Result<(), Error> {
395    println!("📌 Demo 5: Generic Functions & Function Overloads");
396
397    // Generic function that counts items in any Vec<T>
398    fn count_items<T>(items: Vec<T>) -> i64 {
399        items.len() as i64
400    }
401
402    // Generic function that processes maps
403    fn get_map_size<K, V>(map: HashMap<K, V>) -> i64 {
404        map.len() as i64
405    }
406
407    // Function working with references in containers
408    fn join_strings(strings: Vec<&str>, separator: &str) -> String {
409        strings.join(separator)
410    }
411
412    let env = Env::builder()
413        .declare_variable::<Vec<String>>("string_list")?
414        .declare_variable::<Vec<i64>>("int_list")?
415        .declare_variable::<HashMap<String, i64>>("score_map")?
416        .declare_variable::<Vec<f64>>("floats")?
417        // Register generic functions with specific type annotations
418        .register_global_function("count_strings", count_items::<String>)?
419        .register_global_function("count_ints", count_items::<i64>)?
420        .register_global_function("get_string_map_size", get_map_size::<String, i64>)?
421        .register_global_function("join_strings", join_strings)?
422        // Multiple functions with same name, different signatures (overloads)
423        .register_global_function("process", |x: i64| x * 2)?
424        .register_global_function("process", |x: f64| (x * 2.0).round())?
425        .register_global_function("process", |x: String| x.to_uppercase())?
426        // Overloaded member functions for different container types
427        .register_member_function("sum", |numbers: Vec<i64>| numbers.iter().sum::<i64>())?
428        .register_member_function("sum", |floats: Vec<f64>| floats.iter().sum::<f64>())?
429        .build()?;
430
431    let test_cases = vec![
432        // Generic function tests
433        ("count_strings(string_list)", "Count strings in list"),
434        ("count_ints(int_list)", "Count integers in list"),
435        ("get_string_map_size(score_map)", "Get map size"),
436        ("join_strings(string_list, ' ')", "Join strings with space"),
437        // Function overload tests
438        ("process(42)", "Process integer (multiply by 2)"),
439        ("process(3.14)", "Process float (multiply by 2, round)"),
440        ("process('hello')", "Process string (uppercase)"),
441        ("int_list.sum()", "Sum integers"),
442        ("floats.sum()", "Sum floats"),
443    ];
444
445    let activation = Activation::new()
446        .bind_variable(
447            "string_list",
448            vec!["hello".to_string(), "world".to_string(), "rust".to_string()],
449        )?
450        .bind_variable("int_list", vec![1, 2, 3, 4, 5, 6])?
451        .bind_variable("floats", vec![1.5, 2.7, 3.143, 4.0])?
452        .bind_variable("score_map", {
453            let mut map = HashMap::new();
454            map.insert("alice".to_string(), 95);
455            map.insert("bob".to_string(), 87);
456            map.insert("charlie".to_string(), 92);
457            map
458        })?;
459
460    for (expr, description) in test_cases {
461        let program = env.compile(expr)?;
462        let result = program.evaluate(&activation)?;
463        println!("  {} = {} ({})", expr, result, description);
464    }
465
466    println!();
467    Ok(())
468}
469
470/// Demo 6: Error handling with different error types including Box<dyn std::error::Error>
471fn demo6_error_handling() -> Result<(), Error> {
472    println!("📌 Demo 6: Error Handling & Validation");
473
474    let env = Env::builder()
475        .declare_variable::<String>("input")?
476        .declare_variable::<i64>("divisor")?
477        .declare_variable::<String>("email")?
478        // Functions returning different error types using thiserror-derived types
479        .register_global_function("safe_parse", |s: &str| -> Result<i64, ValidationError> {
480            s.parse::<i64>()
481                .map_err(|e| ValidationError::ParseError(e.to_string()))
482        })?
483        .register_global_function(
484            "safe_divide",
485            |a: i64, b: i64| -> Result<f64, ValidationError> {
486                if b == 0 {
487                    Err(ValidationError::DivisionByZero)
488                } else {
489                    Ok(a as f64 / b as f64)
490                }
491            },
492        )?
493        // Additional validation functions with ValidationError
494        .register_global_function(
495            "validate_email",
496            |email: &str| -> Result<bool, ValidationError> {
497                if email.is_empty() {
498                    return Err(ValidationError::EmailError(
499                        "Email cannot be empty".to_string(),
500                    ));
501                }
502                if !email.contains('@') {
503                    return Err(ValidationError::EmailError(
504                        "Email must contain @ symbol".to_string(),
505                    ));
506                }
507                if !email.contains('.') {
508                    return Err(ValidationError::EmailError(
509                        "Email must contain . symbol".to_string(),
510                    ));
511                }
512                let parts: Vec<&str> = email.split('@').collect();
513                if parts.len() != 2 {
514                    return Err(ValidationError::EmailError(
515                        "Email must contain exactly one @ symbol".to_string(),
516                    ));
517                }
518                if parts[0].is_empty() || parts[1].is_empty() {
519                    return Err(ValidationError::EmailError(
520                        "Email local and domain parts cannot be empty".to_string(),
521                    ));
522                }
523                Ok(true)
524            },
525        )?
526        .register_global_function(
527            "validate_age",
528            |age: i64| -> Result<String, ValidationError> {
529                match age {
530                    0..=17 => Ok("minor".to_string()),
531                    18..=64 => Ok("adult".to_string()),
532                    65..=120 => Ok("senior".to_string()),
533                    _ => Err(ValidationError::AgeError(format!("Invalid age: {}", age))),
534                }
535            },
536        )?
537        .register_global_function(
538            "validate_range",
539            |x: i64, min: i64, max: i64| -> Result<i64, ValidationError> {
540                if x < min || x > max {
541                    Err(ValidationError::RangeError { value: x, min, max })
542                } else {
543                    Ok(x)
544                }
545            },
546        )?
547        .build()?;
548
549    let test_cases = vec![
550        // Success cases
551        (
552            "safe_parse('42')",
553            "42",
554            2i64,
555            "valid@email.com",
556            true,
557            "Parse valid number",
558        ),
559        (
560            "safe_divide(10, divisor)",
561            "10",
562            2i64,
563            "test@example.com",
564            true,
565            "Safe division",
566        ),
567        (
568            "validate_email(email)",
569            "25",
570            1i64,
571            "user@domain.com",
572            true,
573            "Valid email",
574        ),
575        (
576            "validate_age(safe_parse(input))",
577            "25",
578            1i64,
579            "user@test.com",
580            true,
581            "Valid age",
582        ),
583        // Error cases
584        (
585            "safe_parse('invalid')",
586            "invalid",
587            2i64,
588            "test@test.com",
589            false,
590            "Parse invalid number",
591        ),
592        (
593            "safe_divide(10, divisor)",
594            "10",
595            0i64,
596            "test@test.com",
597            false,
598            "Division by zero",
599        ),
600        (
601            "validate_email(email)",
602            "10",
603            1i64,
604            "invalid-email",
605            false,
606            "Invalid email format",
607        ),
608        (
609            "validate_age(safe_parse(input))",
610            "150",
611            1i64,
612            "test@test.com",
613            false,
614            "Invalid age",
615        ),
616        (
617            "validate_range(safe_parse(input), 1, 100)",
618            "150",
619            2i64,
620            "test@test.com",
621            false,
622            "Value out of range",
623        ),
624    ];
625
626    for (expr, input, divisor, email, should_succeed, description) in test_cases {
627        let program = env.compile(expr)?;
628        let activation = Activation::new()
629            .bind_variable("input", input)?
630            .bind_variable("divisor", divisor)?
631            .bind_variable("email", email)?;
632
633        match program.evaluate(&activation) {
634            Ok(result) => {
635                println!("  {} = {} ✅ ({})", expr, result, description);
636                if !should_succeed {
637                    println!("    ⚠️  Expected this to fail!");
638                }
639            }
640            Err(e) => {
641                println!("  {} -> ERROR: {} ❌ ({})", expr, e, description);
642                if should_succeed {
643                    println!("    ⚠️  Expected this to succeed!");
644                }
645            }
646        }
647    }
648
649    println!();
650    Ok(())
651}
652
653/// Demo 7: Program introspection and return types
654fn demo7_program_introspection() -> Result<(), Error> {
655    println!("📌 Demo 7: Program Introspection & Return Types");
656
657    // Test with numeric expression
658    {
659        let env = Env::builder()
660            .declare_variable::<i64>("a")?
661            .declare_variable::<i64>("b")?
662            .build()?;
663        let program = env.compile("a + b")?;
664        let return_type = program.return_type();
665        println!("  Expression: 'a + b'");
666        println!("    Return type: {}", return_type);
667
668        let activation = Activation::new()
669            .bind_variable("a", 10)?
670            .bind_variable("b", 20)?;
671        let result = program.evaluate(&activation)?;
672        println!("    Result: {}", result);
673    }
674
675    // Test with string expression
676    {
677        let env = Env::builder()
678            .declare_variable::<String>("a")?
679            .declare_variable::<String>("b")?
680            .build()?;
681        let program = env.compile("a + b")?;
682        let return_type = program.return_type();
683        println!("  Expression: 'a + b' (strings)");
684        println!("    Return type: {}", return_type);
685
686        let activation = Activation::new()
687            .bind_variable("a", "Hello ".to_string())?
688            .bind_variable("b", "World!".to_string())?;
689        let result = program.evaluate(&activation)?;
690        println!("    Result: {}", result);
691    }
692
693    // Test with boolean expression
694    {
695        let env = Env::builder().declare_variable::<i64>("age")?.build()?;
696        let program = env.compile("age >= 18")?;
697        let return_type = program.return_type();
698        println!("  Expression: 'age >= 18'");
699        println!("    Return type: {}", return_type);
700
701        let activation = Activation::new().bind_variable("age", 25i64)?;
702        let result = program.evaluate(&activation)?;
703        println!("    Result: {}", result);
704    }
705
706    println!();
707    Ok(())
708}
709
710/// Demo 8: Advanced container operations and reference handling
711fn demo8_container_operations() -> Result<(), Error> {
712    println!("📌 Demo 8: Container Operations & Reference Handling");
713
714    let env = Env::builder()
715        .declare_variable::<Vec<&str>>("string_refs")?
716        .declare_variable::<HashMap<i64, &str>>("lookup_table")?
717        .declare_variable::<Option<&str>>("maybe_value")?
718        .declare_variable::<Vec<User>>("users")?
719        // Functions working with reference types
720        .register_global_function("longest_string", |strings: Vec<&str>| -> Option<String> {
721            strings
722                .iter()
723                .max_by_key(|s| s.len())
724                .map(|s| s.to_string())
725        })?
726        .register_global_function(
727            "lookup",
728            |table: HashMap<i64, &str>, key: i64| -> Option<String> {
729                table.get(&key).map(|s| s.to_string())
730            },
731        )?
732        .register_global_function("string_length", |s: Option<&str>| -> i64 {
733            s.map(|s| s.len() as i64).unwrap_or(0)
734        })?
735        // Advanced filtering and mapping
736        .register_global_function("filter_adults", |users: Vec<User>| -> Vec<User> {
737            users.into_iter().filter(|u| u.age >= 18).collect()
738        })?
739        .register_global_function(
740            "group_by_age_range",
741            |users: Vec<User>| -> HashMap<String, Vec<String>> {
742                let mut groups: HashMap<String, Vec<String>> = HashMap::new();
743                for user in users {
744                    let range = match user.age {
745                        0..=17 => "minor",
746                        18..=64 => "adult",
747                        _ => "senior",
748                    };
749                    groups.entry(range.to_string()).or_default().push(user.name);
750                }
751                groups
752            },
753        )?
754        .build()?;
755
756    // Prepare test data with proper lifetimes
757    let source_strings = [
758        "hello".to_string(),
759        "world".to_string(),
760        "rust".to_string(),
761        "programming".to_string(),
762    ];
763    let string_refs: Vec<&str> = source_strings.iter().map(|s| s.as_str()).collect();
764
765    let lookup_table: HashMap<i64, &str> = HashMap::from([(1, "one"), (2, "two"), (3, "three")]);
766
767    let maybe_value: Option<&str> = Some("test string");
768
769    let users = vec![
770        User {
771            id: 1,
772            name: "Alice".to_string(),
773            email: "alice@test.com".to_string(),
774            age: 25,
775            roles: vec![],
776            metadata: HashMap::new(),
777        },
778        User {
779            id: 2,
780            name: "Bob".to_string(),
781            email: "bob@test.com".to_string(),
782            age: 16,
783            roles: vec![],
784            metadata: HashMap::new(),
785        },
786        User {
787            id: 3,
788            name: "Carol".to_string(),
789            email: "carol@test.com".to_string(),
790            age: 67,
791            roles: vec![],
792            metadata: HashMap::new(),
793        },
794    ];
795
796    let test_expressions = vec![
797        ("longest_string(string_refs)", "Find longest string"),
798        ("lookup(lookup_table, 2)", "Lookup value by key"),
799        ("string_length(maybe_value)", "Get optional string length"),
800        ("filter_adults(users).size()", "Count adult users"),
801        (
802            "group_by_age_range(users).size()",
803            "Group users by age range",
804        ),
805    ];
806
807    let activation = Activation::new()
808        .bind_variable("string_refs", string_refs)?
809        .bind_variable("lookup_table", lookup_table)?
810        .bind_variable("maybe_value", maybe_value)?
811        .bind_variable("users", users)?;
812
813    for (expr, description) in test_expressions {
814        let program = env.compile(expr)?;
815        let result = program.evaluate(&activation)?;
816        println!("  {} = {} ({})", expr, result, description);
817    }
818
819    println!();
820    Ok(())
821}
Source

pub fn build(self) -> Result<Env<'f, Fm, Rm>, Error>

Builds the environment from the configured builder.

This method consumes the builder and creates the final Env instance that can be used to compile CEL expressions.

§Returns

Returns a Result containing the built Env or an Error if the environment could not be created.

§Examples
use cel_cxx::*;

let env = Env::builder()
    .declare_variable::<String>("name")?
    .build()?;
§Errors

Returns an error if the environment configuration is invalid or if the underlying CEL environment cannot be created.

Examples found in repository?
examples/basic.rs (line 16)
7fn main() -> Result<(), Error> {
8    println!("🚀 CEL-CXX Basic Example\n");
9
10    // Create an environment with variables and functions
11    let env = Env::builder()
12        .declare_variable::<String>("name")?
13        .declare_variable::<i64>("age")?
14        .register_global_function("greet", |name: &str| format!("Hello, {}!", name))?
15        .register_global_function("is_adult", |age: i64| age >= 18)?
16        .build()?;
17
18    // Compile and evaluate expressions
19    let expressions = vec![
20        "greet(name)",
21        "is_adult(age)",
22        "'Name: ' + name + ', Age: ' + string(age)",
23        "age >= 18 ? 'adult' : 'minor'",
24    ];
25
26    let activation = Activation::new()
27        .bind_variable("name", "Alice")?
28        .bind_variable("age", 25i64)?;
29
30    for expr in expressions {
31        let program = env.compile(expr)?;
32        let result = program.evaluate(&activation)?;
33        println!("{} = {}", expr, result);
34    }
35
36    println!("\n✅ Basic example completed!");
37    Ok(())
38}
More examples
Hide additional examples
examples/comprehensive.rs (line 170)
146fn demo1_basic_operations() -> Result<(), Error> {
147    println!("📌 Demo 1: Basic Expressions & Zero-Annotation Functions");
148
149    let env = Env::builder()
150        .declare_variable::<String>("name")?
151        .declare_variable::<i64>("age")?
152        .declare_variable::<f64>("score")?
153        // ✨ Zero-annotation functions - types automatically inferred!
154        .register_global_function("greet", |name: &str| format!("Hello, {}!", name))?
155        .register_global_function("is_adult", |age: i64| age >= 18)?
156        .register_global_function("grade", |score: f64| -> String {
157            match score {
158                90.0..=100.0 => "A".to_string(),
159                80.0..=89.9 => "B".to_string(),
160                70.0..=79.9 => "C".to_string(),
161                60.0..=69.9 => "D".to_string(),
162                _ => "F".to_string(),
163            }
164        })?
165        .register_global_function("calculate_discount", |age: i64, score: f64| -> f64 {
166            let base_discount = if age >= 65 { 0.2 } else { 0.0 };
167            let score_bonus = if score >= 90.0 { 0.1 } else { 0.0 };
168            base_discount + score_bonus
169        })?
170        .build()?;
171
172    // Test basic expressions including simple arithmetic
173    let test_cases = vec![
174        ("1 + 1", "Alice", 25i64, 95.0, "Simple arithmetic"),
175        (
176            "greet(name)",
177            "Alice",
178            25i64,
179            95.0,
180            "Function with string parameter",
181        ),
182        ("is_adult(age)", "Bob", 16i64, 85.0, "Boolean function"),
183        (
184            "grade(score)",
185            "Charlie",
186            30i64,
187            78.5,
188            "String return function",
189        ),
190        (
191            "calculate_discount(age, score)",
192            "Diana",
193            67i64,
194            92.0,
195            "Multi-parameter function",
196        ),
197    ];
198
199    for (expr, name, age, score, description) in test_cases {
200        let program = env.compile(expr)?;
201        let activation = Activation::new()
202            .bind_variable("name", name)?
203            .bind_variable("age", age)?
204            .bind_variable("score", score)?;
205
206        let result = program.evaluate(&activation)?;
207        println!("  {} = {} ({})", expr, result, description);
208    }
209
210    println!();
211    Ok(())
212}
213
214/// Demo 2: Variable binding and providers
215fn demo2_variable_operations() -> Result<(), Error> {
216    println!("📌 Demo 2: Variable Binding & Providers");
217
218    let env = Env::builder()
219        .declare_variable::<i64>("a")?
220        .declare_variable::<i64>("b")?
221        .declare_global_function::<fn() -> i64>("get_const")?
222        .register_global_function("multiply", |x: i64, y: i64| x * y)?
223        .build()?;
224
225    // Test direct variable binding
226    {
227        println!("  Variable binding:");
228        let program = env.compile("a + b")?;
229        let activation = Activation::new()
230            .bind_variable("a", 10)?
231            .bind_variable("b", 20)?;
232        let result = program.evaluate(&activation)?;
233        println!("    a + b = {} (direct binding)", result);
234    }
235
236    // Test variable provider binding
237    {
238        println!("  Variable provider binding:");
239        let program = env.compile("a * b")?;
240        let activation = Activation::new()
241            .bind_variable("a", 5)?
242            .bind_variable_provider("b", || -> Result<i64, Error> {
243                println!("    Provider called for variable 'b'");
244                Ok(7)
245            })?;
246        let result = program.evaluate(&activation)?;
247        println!("    a * b = {} (provider binding)", result);
248    }
249
250    // Test function declaration and binding
251    {
252        println!("  Function declaration & binding:");
253        let program = env.compile("get_const() + multiply(a, 3)")?;
254        let activation = Activation::new()
255            .bind_variable("a", 4)?
256            .bind_global_function("get_const", || -> Result<i64, Error> { Ok(100) })?;
257        let result = program.evaluate(&activation)?;
258        println!(
259            "    get_const() + multiply(a, 3) = {} (function binding)",
260            result
261        );
262    }
263
264    println!();
265    Ok(())
266}
267
268/// Demo 3: Opaque types with member functions
269fn demo3_opaque_member_functions() -> Result<(), Error> {
270    println!("📌 Demo 3: Opaque Types & Member Functions");
271
272    let env = Env::builder()
273        .declare_variable::<Student>("student")?
274        // ✨ Register struct methods directly using RustType::method_name syntax
275        .register_member_function("get_name", Student::get_name)?
276        .register_member_function("get_age", Student::get_age)?
277        .register_member_function("get_grade", Student::get_grade)?
278        .register_member_function("is_passing", Student::is_passing)?
279        .register_member_function("has_subject", Student::has_subject)?
280        .register_member_function("get_letter_grade", Student::get_letter_grade)?
281        .build()?;
282
283    let student = Student {
284        name: "John Doe".to_string(),
285        age: 18,
286        grade: 87.5,
287        subjects: vec!["Math".to_string(), "Physics".to_string()],
288    };
289
290    let activation = Activation::new().bind_variable("student", student)?;
291
292    let test_expressions = vec![
293        ("student.get_name()", "Get student name"),
294        ("student.get_age()", "Get student age"),
295        ("student.get_grade()", "Get numerical grade"),
296        ("student.get_letter_grade()", "Get letter grade"),
297        ("student.is_passing()", "Check if passing"),
298        ("student.has_subject('Math')", "Check if has Math subject"),
299        (
300            "student.has_subject('Chemistry')",
301            "Check if has Chemistry subject",
302        ),
303    ];
304
305    for (expr, description) in test_expressions {
306        let program = env.compile(expr)?;
307        let result = program.evaluate(&activation)?;
308        println!("  {} = {} ({})", expr, result, description);
309    }
310
311    println!();
312    Ok(())
313}
314
315/// Demo 4: Type conversions and standard Rust types
316fn demo4_type_conversions() -> Result<(), Error> {
317    println!("📌 Demo 4: Type Conversions & Standard Rust Types");
318
319    // Functions returning different types (both direct values and Results)
320    fn return_string_direct() -> String {
321        "hello world".to_string()
322    }
323    fn return_int_result() -> Result<i64, std::io::Error> {
324        Ok(42)
325    }
326    fn return_list() -> Vec<i64> {
327        vec![1, 2, 3, 4, 5]
328    }
329    fn return_map() -> HashMap<String, i64> {
330        let mut map = HashMap::new();
331        map.insert("key1".to_string(), 100);
332        map.insert("key2".to_string(), 200);
333        map
334    }
335    fn return_optional_some() -> Option<String> {
336        Some("optional value".to_string())
337    }
338    fn return_optional_none() -> Option<String> {
339        None
340    }
341
342    let env = Env::builder()
343        .register_global_function("return_string_direct", return_string_direct)?
344        .register_global_function("return_int_result", return_int_result)?
345        .register_global_function("return_list", return_list)?
346        .register_global_function("return_map", return_map)?
347        .register_global_function("return_optional_some", return_optional_some)?
348        .register_global_function("return_optional_none", return_optional_none)?
349        .build()?;
350
351    let test_cases = vec![
352        ("return_string_direct()", "String conversion"),
353        ("return_int_result()", "Result<i64> conversion"),
354        ("return_list()", "Vec<i64> conversion"),
355        ("return_map()", "HashMap conversion"),
356        ("return_optional_some()", "Option<String> Some conversion"),
357        ("return_optional_none()", "Option<String> None conversion"),
358    ];
359
360    for (expr, description) in test_cases {
361        let program = env.compile(expr)?;
362        let result = program.evaluate(())?;
363        println!("  {} = {} ({})", expr, result, description);
364
365        // Demonstrate type conversion back to Rust types
366        match expr {
367            "return_string_direct()" => {
368                let rust_string: String = result
369                    .try_into()
370                    .map_err(|_| Error::invalid_argument("string conversion failed".to_string()))?;
371                println!("    Converted back to Rust String: '{}'", rust_string);
372            }
373            "return_int_result()" => {
374                let rust_int: i64 = result
375                    .try_into()
376                    .map_err(|_| Error::invalid_argument("int conversion failed".to_string()))?;
377                println!("    Converted back to Rust i64: {}", rust_int);
378            }
379            "return_list()" => {
380                let rust_list: Vec<i64> = result
381                    .try_into()
382                    .map_err(|_| Error::invalid_argument("list conversion failed".to_string()))?;
383                println!("    Converted back to Rust Vec<i64>: {:?}", rust_list);
384            }
385            _ => {}
386        }
387    }
388
389    println!();
390    Ok(())
391}
392
393/// Demo 5: Generic functions and type annotations
394fn demo5_generic_functions() -> Result<(), Error> {
395    println!("📌 Demo 5: Generic Functions & Function Overloads");
396
397    // Generic function that counts items in any Vec<T>
398    fn count_items<T>(items: Vec<T>) -> i64 {
399        items.len() as i64
400    }
401
402    // Generic function that processes maps
403    fn get_map_size<K, V>(map: HashMap<K, V>) -> i64 {
404        map.len() as i64
405    }
406
407    // Function working with references in containers
408    fn join_strings(strings: Vec<&str>, separator: &str) -> String {
409        strings.join(separator)
410    }
411
412    let env = Env::builder()
413        .declare_variable::<Vec<String>>("string_list")?
414        .declare_variable::<Vec<i64>>("int_list")?
415        .declare_variable::<HashMap<String, i64>>("score_map")?
416        .declare_variable::<Vec<f64>>("floats")?
417        // Register generic functions with specific type annotations
418        .register_global_function("count_strings", count_items::<String>)?
419        .register_global_function("count_ints", count_items::<i64>)?
420        .register_global_function("get_string_map_size", get_map_size::<String, i64>)?
421        .register_global_function("join_strings", join_strings)?
422        // Multiple functions with same name, different signatures (overloads)
423        .register_global_function("process", |x: i64| x * 2)?
424        .register_global_function("process", |x: f64| (x * 2.0).round())?
425        .register_global_function("process", |x: String| x.to_uppercase())?
426        // Overloaded member functions for different container types
427        .register_member_function("sum", |numbers: Vec<i64>| numbers.iter().sum::<i64>())?
428        .register_member_function("sum", |floats: Vec<f64>| floats.iter().sum::<f64>())?
429        .build()?;
430
431    let test_cases = vec![
432        // Generic function tests
433        ("count_strings(string_list)", "Count strings in list"),
434        ("count_ints(int_list)", "Count integers in list"),
435        ("get_string_map_size(score_map)", "Get map size"),
436        ("join_strings(string_list, ' ')", "Join strings with space"),
437        // Function overload tests
438        ("process(42)", "Process integer (multiply by 2)"),
439        ("process(3.14)", "Process float (multiply by 2, round)"),
440        ("process('hello')", "Process string (uppercase)"),
441        ("int_list.sum()", "Sum integers"),
442        ("floats.sum()", "Sum floats"),
443    ];
444
445    let activation = Activation::new()
446        .bind_variable(
447            "string_list",
448            vec!["hello".to_string(), "world".to_string(), "rust".to_string()],
449        )?
450        .bind_variable("int_list", vec![1, 2, 3, 4, 5, 6])?
451        .bind_variable("floats", vec![1.5, 2.7, 3.143, 4.0])?
452        .bind_variable("score_map", {
453            let mut map = HashMap::new();
454            map.insert("alice".to_string(), 95);
455            map.insert("bob".to_string(), 87);
456            map.insert("charlie".to_string(), 92);
457            map
458        })?;
459
460    for (expr, description) in test_cases {
461        let program = env.compile(expr)?;
462        let result = program.evaluate(&activation)?;
463        println!("  {} = {} ({})", expr, result, description);
464    }
465
466    println!();
467    Ok(())
468}
469
470/// Demo 6: Error handling with different error types including Box<dyn std::error::Error>
471fn demo6_error_handling() -> Result<(), Error> {
472    println!("📌 Demo 6: Error Handling & Validation");
473
474    let env = Env::builder()
475        .declare_variable::<String>("input")?
476        .declare_variable::<i64>("divisor")?
477        .declare_variable::<String>("email")?
478        // Functions returning different error types using thiserror-derived types
479        .register_global_function("safe_parse", |s: &str| -> Result<i64, ValidationError> {
480            s.parse::<i64>()
481                .map_err(|e| ValidationError::ParseError(e.to_string()))
482        })?
483        .register_global_function(
484            "safe_divide",
485            |a: i64, b: i64| -> Result<f64, ValidationError> {
486                if b == 0 {
487                    Err(ValidationError::DivisionByZero)
488                } else {
489                    Ok(a as f64 / b as f64)
490                }
491            },
492        )?
493        // Additional validation functions with ValidationError
494        .register_global_function(
495            "validate_email",
496            |email: &str| -> Result<bool, ValidationError> {
497                if email.is_empty() {
498                    return Err(ValidationError::EmailError(
499                        "Email cannot be empty".to_string(),
500                    ));
501                }
502                if !email.contains('@') {
503                    return Err(ValidationError::EmailError(
504                        "Email must contain @ symbol".to_string(),
505                    ));
506                }
507                if !email.contains('.') {
508                    return Err(ValidationError::EmailError(
509                        "Email must contain . symbol".to_string(),
510                    ));
511                }
512                let parts: Vec<&str> = email.split('@').collect();
513                if parts.len() != 2 {
514                    return Err(ValidationError::EmailError(
515                        "Email must contain exactly one @ symbol".to_string(),
516                    ));
517                }
518                if parts[0].is_empty() || parts[1].is_empty() {
519                    return Err(ValidationError::EmailError(
520                        "Email local and domain parts cannot be empty".to_string(),
521                    ));
522                }
523                Ok(true)
524            },
525        )?
526        .register_global_function(
527            "validate_age",
528            |age: i64| -> Result<String, ValidationError> {
529                match age {
530                    0..=17 => Ok("minor".to_string()),
531                    18..=64 => Ok("adult".to_string()),
532                    65..=120 => Ok("senior".to_string()),
533                    _ => Err(ValidationError::AgeError(format!("Invalid age: {}", age))),
534                }
535            },
536        )?
537        .register_global_function(
538            "validate_range",
539            |x: i64, min: i64, max: i64| -> Result<i64, ValidationError> {
540                if x < min || x > max {
541                    Err(ValidationError::RangeError { value: x, min, max })
542                } else {
543                    Ok(x)
544                }
545            },
546        )?
547        .build()?;
548
549    let test_cases = vec![
550        // Success cases
551        (
552            "safe_parse('42')",
553            "42",
554            2i64,
555            "valid@email.com",
556            true,
557            "Parse valid number",
558        ),
559        (
560            "safe_divide(10, divisor)",
561            "10",
562            2i64,
563            "test@example.com",
564            true,
565            "Safe division",
566        ),
567        (
568            "validate_email(email)",
569            "25",
570            1i64,
571            "user@domain.com",
572            true,
573            "Valid email",
574        ),
575        (
576            "validate_age(safe_parse(input))",
577            "25",
578            1i64,
579            "user@test.com",
580            true,
581            "Valid age",
582        ),
583        // Error cases
584        (
585            "safe_parse('invalid')",
586            "invalid",
587            2i64,
588            "test@test.com",
589            false,
590            "Parse invalid number",
591        ),
592        (
593            "safe_divide(10, divisor)",
594            "10",
595            0i64,
596            "test@test.com",
597            false,
598            "Division by zero",
599        ),
600        (
601            "validate_email(email)",
602            "10",
603            1i64,
604            "invalid-email",
605            false,
606            "Invalid email format",
607        ),
608        (
609            "validate_age(safe_parse(input))",
610            "150",
611            1i64,
612            "test@test.com",
613            false,
614            "Invalid age",
615        ),
616        (
617            "validate_range(safe_parse(input), 1, 100)",
618            "150",
619            2i64,
620            "test@test.com",
621            false,
622            "Value out of range",
623        ),
624    ];
625
626    for (expr, input, divisor, email, should_succeed, description) in test_cases {
627        let program = env.compile(expr)?;
628        let activation = Activation::new()
629            .bind_variable("input", input)?
630            .bind_variable("divisor", divisor)?
631            .bind_variable("email", email)?;
632
633        match program.evaluate(&activation) {
634            Ok(result) => {
635                println!("  {} = {} ✅ ({})", expr, result, description);
636                if !should_succeed {
637                    println!("    ⚠️  Expected this to fail!");
638                }
639            }
640            Err(e) => {
641                println!("  {} -> ERROR: {} ❌ ({})", expr, e, description);
642                if should_succeed {
643                    println!("    ⚠️  Expected this to succeed!");
644                }
645            }
646        }
647    }
648
649    println!();
650    Ok(())
651}
652
653/// Demo 7: Program introspection and return types
654fn demo7_program_introspection() -> Result<(), Error> {
655    println!("📌 Demo 7: Program Introspection & Return Types");
656
657    // Test with numeric expression
658    {
659        let env = Env::builder()
660            .declare_variable::<i64>("a")?
661            .declare_variable::<i64>("b")?
662            .build()?;
663        let program = env.compile("a + b")?;
664        let return_type = program.return_type();
665        println!("  Expression: 'a + b'");
666        println!("    Return type: {}", return_type);
667
668        let activation = Activation::new()
669            .bind_variable("a", 10)?
670            .bind_variable("b", 20)?;
671        let result = program.evaluate(&activation)?;
672        println!("    Result: {}", result);
673    }
674
675    // Test with string expression
676    {
677        let env = Env::builder()
678            .declare_variable::<String>("a")?
679            .declare_variable::<String>("b")?
680            .build()?;
681        let program = env.compile("a + b")?;
682        let return_type = program.return_type();
683        println!("  Expression: 'a + b' (strings)");
684        println!("    Return type: {}", return_type);
685
686        let activation = Activation::new()
687            .bind_variable("a", "Hello ".to_string())?
688            .bind_variable("b", "World!".to_string())?;
689        let result = program.evaluate(&activation)?;
690        println!("    Result: {}", result);
691    }
692
693    // Test with boolean expression
694    {
695        let env = Env::builder().declare_variable::<i64>("age")?.build()?;
696        let program = env.compile("age >= 18")?;
697        let return_type = program.return_type();
698        println!("  Expression: 'age >= 18'");
699        println!("    Return type: {}", return_type);
700
701        let activation = Activation::new().bind_variable("age", 25i64)?;
702        let result = program.evaluate(&activation)?;
703        println!("    Result: {}", result);
704    }
705
706    println!();
707    Ok(())
708}
709
710/// Demo 8: Advanced container operations and reference handling
711fn demo8_container_operations() -> Result<(), Error> {
712    println!("📌 Demo 8: Container Operations & Reference Handling");
713
714    let env = Env::builder()
715        .declare_variable::<Vec<&str>>("string_refs")?
716        .declare_variable::<HashMap<i64, &str>>("lookup_table")?
717        .declare_variable::<Option<&str>>("maybe_value")?
718        .declare_variable::<Vec<User>>("users")?
719        // Functions working with reference types
720        .register_global_function("longest_string", |strings: Vec<&str>| -> Option<String> {
721            strings
722                .iter()
723                .max_by_key(|s| s.len())
724                .map(|s| s.to_string())
725        })?
726        .register_global_function(
727            "lookup",
728            |table: HashMap<i64, &str>, key: i64| -> Option<String> {
729                table.get(&key).map(|s| s.to_string())
730            },
731        )?
732        .register_global_function("string_length", |s: Option<&str>| -> i64 {
733            s.map(|s| s.len() as i64).unwrap_or(0)
734        })?
735        // Advanced filtering and mapping
736        .register_global_function("filter_adults", |users: Vec<User>| -> Vec<User> {
737            users.into_iter().filter(|u| u.age >= 18).collect()
738        })?
739        .register_global_function(
740            "group_by_age_range",
741            |users: Vec<User>| -> HashMap<String, Vec<String>> {
742                let mut groups: HashMap<String, Vec<String>> = HashMap::new();
743                for user in users {
744                    let range = match user.age {
745                        0..=17 => "minor",
746                        18..=64 => "adult",
747                        _ => "senior",
748                    };
749                    groups.entry(range.to_string()).or_default().push(user.name);
750                }
751                groups
752            },
753        )?
754        .build()?;
755
756    // Prepare test data with proper lifetimes
757    let source_strings = [
758        "hello".to_string(),
759        "world".to_string(),
760        "rust".to_string(),
761        "programming".to_string(),
762    ];
763    let string_refs: Vec<&str> = source_strings.iter().map(|s| s.as_str()).collect();
764
765    let lookup_table: HashMap<i64, &str> = HashMap::from([(1, "one"), (2, "two"), (3, "three")]);
766
767    let maybe_value: Option<&str> = Some("test string");
768
769    let users = vec![
770        User {
771            id: 1,
772            name: "Alice".to_string(),
773            email: "alice@test.com".to_string(),
774            age: 25,
775            roles: vec![],
776            metadata: HashMap::new(),
777        },
778        User {
779            id: 2,
780            name: "Bob".to_string(),
781            email: "bob@test.com".to_string(),
782            age: 16,
783            roles: vec![],
784            metadata: HashMap::new(),
785        },
786        User {
787            id: 3,
788            name: "Carol".to_string(),
789            email: "carol@test.com".to_string(),
790            age: 67,
791            roles: vec![],
792            metadata: HashMap::new(),
793        },
794    ];
795
796    let test_expressions = vec![
797        ("longest_string(string_refs)", "Find longest string"),
798        ("lookup(lookup_table, 2)", "Lookup value by key"),
799        ("string_length(maybe_value)", "Get optional string length"),
800        ("filter_adults(users).size()", "Count adult users"),
801        (
802            "group_by_age_range(users).size()",
803            "Group users by age range",
804        ),
805    ];
806
807    let activation = Activation::new()
808        .bind_variable("string_refs", string_refs)?
809        .bind_variable("lookup_table", lookup_table)?
810        .bind_variable("maybe_value", maybe_value)?
811        .bind_variable("users", users)?;
812
813    for (expr, description) in test_expressions {
814        let program = env.compile(expr)?;
815        let result = program.evaluate(&activation)?;
816        println!("  {} = {} ({})", expr, result, description);
817    }
818
819    println!();
820    Ok(())
821}
Source§

impl<'f, Rm: RuntimeMarker> EnvBuilder<'f, (), Rm>

Source

pub fn force_async(self) -> EnvBuilder<'f, Async, Rm>

Available on crate feature async only.

Forces conversion to an async environment builder.

This method converts a synchronous environment builder to an asynchronous one, allowing it to register async functions and build async environments.

§Examples
use cel_cxx::*;

let async_builder = Env::builder().force_async();
Source§

impl<'f, Fm: FnMarker> EnvBuilder<'f, Fm, ()>

Source

pub fn use_runtime<Rt: Runtime>(self) -> EnvBuilder<'f, Fm, Rt>

Available on crate feature async only.

Sets the async runtime for the environment builder.

This method specifies which async runtime should be used by environments built from this builder.

§Type Parameters
  • Rt - The runtime type to use (must implement Runtime)
§Examples
use cel_cxx::*;

let builder = Env::builder().use_runtime::<Tokio>();
Source

pub fn use_tokio(self) -> EnvBuilder<'f, Fm, Tokio>

Available on crate features async and tokio only.

Configures the builder to use the Tokio async runtime.

This is a convenience method for setting the runtime to Tokio. Requires the tokio feature to be enabled.

§Examples
use cel_cxx::*;

let builder = Env::builder().use_tokio();
Source

pub fn use_async_std(self) -> EnvBuilder<'f, Fm, AsyncStd>

Available on crate features async and async-std only.

Configures the builder to use the async-std runtime.

This is a convenience method for setting the runtime to async-std. Requires the async-std feature to be enabled.

§Examples
use cel_cxx::*;

let builder = Env::builder().use_async_std();
Source

pub fn use_smol(self) -> EnvBuilder<'f, Fm, Smol>

Available on crate features async and smol only.

Configures the builder to use the smol runtime.

This is a convenience method for setting the runtime to smol. Requires the smol feature to be enabled.

§Examples
use cel_cxx::*;

let builder = Env::builder().use_smol();

Trait Implementations§

Source§

impl<'f, Fm: FnMarker, Rm: RuntimeMarker> Debug for EnvBuilder<'f, Fm, Rm>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'f, Fm: FnMarker, Rm: RuntimeMarker> Default for EnvBuilder<'f, Fm, Rm>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl<'f, Fm, Rm> Freeze for EnvBuilder<'f, Fm, Rm>

§

impl<'f, Fm = (), Rm = ()> !RefUnwindSafe for EnvBuilder<'f, Fm, Rm>

§

impl<'f, Fm, Rm> Send for EnvBuilder<'f, Fm, Rm>
where Fm: Send, Rm: Send,

§

impl<'f, Fm, Rm> Sync for EnvBuilder<'f, Fm, Rm>
where Fm: Sync, Rm: Sync,

§

impl<'f, Fm, Rm> Unpin for EnvBuilder<'f, Fm, Rm>
where Fm: Unpin, Rm: Unpin,

§

impl<'f, Fm = (), Rm = ()> !UnwindSafe for EnvBuilder<'f, Fm, Rm>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more