Expand description
Function registration and implementation utilities.
The function system provides a flexible way to register and call functions in CEL expressions. Functions can be either compile-time declarations (type signatures only) or runtime implementations (callable code).
§Key Components
FunctionRegistry
: Compile-time function registry for declaring function signatures and registering implementationsFunctionBindings
: Runtime function bindings for calling functions during evaluationFunctionOverloads
: Function overload management supporting multiple implementations with different signatures- Declarations: Use
FunctionDecl
trait for compile-time type checking - Implementations: Use
IntoFunction
trait for runtime function calls
§Examples
use cel_cxx::*;
// Register a function implementation
let mut env = Env::builder()
.register_global_function("greet", |name: String| -> String {
format!("Hello, {}!", name)
})?
.build()?;
§Detailed Documentation
Zero-annotation function registration for CEL expression evaluation.
This module provides a type-safe, zero-annotation function registration system that allows Rust functions to be called from CEL expressions without manual type annotations or wrapper code.
§Two Function Systems
This module provides two distinct but complementary function systems:
§1. Function Registration (Runtime Implementation)
- Purpose: Register actual callable Rust functions/closures
- Entry point:
IntoFunction
trait and registration methods - Usage:
env.register_function("name", function_impl)
- Provides: Executable code that can be called during expression evaluation
§2. Function Declaration (Compile-Time Signatures)
- Purpose: Declare function signatures for type checking without implementation
- Entry point:
FunctionDecl
trait and declaration methods - Usage:
env.declare_function::<SignatureType>("name")
- Provides: Type information for compile-time validation and overload resolution
These systems work together: you can declare functions for type checking during development, then provide implementations later, or register complete functions that include both signature and implementation.
§Features
- Zero-annotation registration: Functions can be registered without explicit type annotations
- Lifetime-aware closures: Support for closures that capture environment variables
- Reference return types: Safe handling of functions returning borrowed data like
&str
- Unified error handling: Automatic conversion of
Result<T, E>
return types - Async function support: Optional support for async functions (requires
async
feature) - Thread safety: All function implementations are
Send + Sync
§How Zero-Annotation Works
The zero-annotation system is built on top of Rust’s type system and Generic Associated Types (GATs). When you register a function, the system automatically:
- Extracts argument types from the function signature using
FunctionDecl
- Infers return types using the
IntoResult
trait - Generates type-safe converters that handle lifetime erasure safely
- Creates a unified interface through the
Function
struct
§Type Conversion Process
For each argument type T
, the system:
- Uses
T: FromValue + TypedValue
to convert from CEL values - Leverages GATs (
FromValue::Output<'a>
) to handle borrowed data like&str
- Safely handles lifetime relationships through internal conversion mechanisms
§Safety Guarantees
The lifetime handling is safe because:
- Source CEL values remain valid for the entire function call
- Converted arguments are immediately consumed by the target function
- No references escape the function call scope
§Examples
§Basic function registration
use cel_cxx::{function::IntoFunction, Error};
// Simple function
fn add(a: i64, b: i64) -> i64 {
a + b
}
let func = add.into_function();
// Function with error handling
fn divide(a: i64, b: i64) -> Result<i64, Error> {
if b == 0 {
Err(Error::invalid_argument("division by zero"))
} else {
Ok(a / b)
}
}
let func = divide.into_function();
§Advanced: Reference return types
The system handles functions that return borrowed data:
use cel_cxx::function::*;
// Function returning borrowed data
fn get_first(items: Vec<&str>) -> &str {
items.first().map_or("", |s| *s)
}
let func = get_first.into_function();
// The system automatically handles the lifetime relationships
§Closure registration
use cel_cxx::function::*;
// Capturing closure
let multiplier = 3;
let multiply = move |x: i64| -> i64 { x * multiplier };
let func = multiply.into_function();
// String processing closure
let prefix = String::from("Hello, ");
let with_prefix = move |name: &str| -> String {
format!("{}{}", prefix, name)
};
let func = with_prefix.into_function();
§Function metadata and invocation
use cel_cxx::function::*;
fn add(a: i64, b: i64) -> i64 { a + b }
let func = add.into_function();
// Get function metadata
let arg_types = func.arguments(); // Vec<ValueType>
let return_type = func.result(); // ValueType
// Call the function (would need proper Value instances in real code)
// let args = vec![Value::from(10i64), Value::from(20i64)];
// let result = func.call(args);
§Async Functions
When the async
feature is enabled, you can register async functions:
// Async function
async fn fetch_data(url: String) -> String {
// Simulate async work
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
format!("Data from {}", url)
}
let func = fetch_data.into_function();
// func.call() returns a Future that can be awaited
Re-exports§
Modules§
- decl
- Function declaration trait for CEL expression evaluation.
- overload
- Function overload management and resolution.
Structs§
- Function
- A type-erased function implementation that can be called from CEL expressions.
- Function
Bindings - Runtime function bindings.
- Function
Registry - Compile-time function registry.
Traits§
- Arguments
- Marker trait for function argument tuples.
- Into
Function - Trait for types that can be converted into function implementations.