Macro

Struct Macro 

Source
pub struct Macro(/* private fields */);
Expand description

A CEL macro that expands expressions at compile time.

A Macro represents a compile-time transformation rule that can be registered with a CEL environment. When the parser encounters a matching function or method call, the macro’s expander is invoked to transform the expression tree.

§Macro Expansion

During compilation, macros are expanded before type checking:

  1. Parser identifies function/method calls matching registered macros
  2. Macro expander receives the call expression and its arguments
  3. Expander returns a new expression tree or None to keep original
  4. Type checker validates the expanded expression

§Thread Safety

The expander functions captured in macros must be Send + Sync + 'static, ensuring thread-safe usage across the CEL environment.

§Error Handling

Macro creation methods return Result<Self, Error> and can fail if:

  • The macro name is invalid
  • Internal FFI allocation fails
  • The expander closure cannot be registered

§Examples

§Fixed argument count

// A macro that requires exactly 1 argument
let add_one_macro = Macro::new_global("add_one", 1, |factory, mut args| {
    let arg = args.pop()?;
    Some(factory.new_call("_+_", &[arg, factory.new_const(1)]))
})?;

§Variable argument count

// A macro that accepts any number of arguments
let debug_macro = Macro::new_global_var_arg("debug", |factory, args| {
    // Wrap all arguments in a list for debugging
    let elements: Vec<_> = args.iter()
        .map(|arg| factory.new_list_element(arg, false))
        .collect();
    Some(factory.new_list(&elements))
})?;

Implementations§

Source§

impl Macro

Source

pub fn new_global( name: impl AsRef<str>, argument_count: usize, expander: impl GlobalMacroExpander + 'static, ) -> Result<Self, Error>

Creates a new global macro with a fixed number of arguments.

Global macros are invoked like regular functions, e.g., macro_name(arg1, arg2). The macro will only be expanded when called with exactly argument_count arguments.

§Parameters
  • name: The function name that triggers this macro (as a string reference)
  • argument_count: The exact number of arguments required
  • expander: The expansion function that transforms the expression
§Returns
  • Ok(Macro): Successfully created macro
  • Err(Error): Failed to create macro (e.g., invalid name or FFI error)
§Expander Function

The expander receives:

  • factory: A mutable reference to MacroExprFactory for creating new expressions
  • args: A vector of argument expressions

The expander should return:

  • Some(Expr): The expanded expression to replace the original call
  • None: Keep the original expression unchanged
§Examples
// Macro: not_zero(x) expands to x != 0
let macro_def = Macro::new_global("not_zero", 1, |factory, mut args| {
    let arg = args.pop()?;
    Some(factory.new_call("_!=_", &[arg, factory.new_const(0)]))
})?;
Source

pub fn new_global_var_arg( name: impl AsRef<str>, expander: impl GlobalMacroExpander + 'static, ) -> Result<Self, Error>

Creates a new global macro that accepts a variable number of arguments.

Variable-argument macros can handle any number of arguments, from zero to many. The expander function receives all arguments and decides how to handle them.

§Parameters
  • name: The function name that triggers this macro (as a string reference)
  • expander: The expansion function that transforms the expression
§Returns
  • Ok(Macro): Successfully created macro
  • Err(Error): Failed to create macro (e.g., invalid name or FFI error)
§Expander Function

The expander receives:

  • factory: A mutable reference to MacroExprFactory for creating new expressions
  • args: A vector of argument expressions (can be empty)

The expander should return:

  • Some(Expr): The expanded expression to replace the original call
  • None: Keep the original expression unchanged
§Examples
// Macro: max(args...) finds maximum of all arguments
let max_macro = Macro::new_global_var_arg("max", |factory, args| {
    if args.is_empty() {
        return None;
    }
    // Implementation would build comparison chain
    Some(args.into_iter().reduce(|acc, arg| {
        factory.new_call("_>_?_:_", &[acc, arg.clone(), acc.clone(), arg])
    })?)
})?;
Source

pub fn new_receiver( name: impl AsRef<str>, argument_count: usize, expander: impl ReceiverMacroExpander + 'static, ) -> Result<Self, Error>

Creates a new receiver macro with a fixed number of arguments.

Receiver macros are invoked as method calls on a target expression, e.g., target.macro_name(arg1, arg2). The macro will only be expanded when called with exactly argument_count arguments.

§Parameters
  • name: The method name that triggers this macro (as a string reference)
  • argument_count: The exact number of arguments required (not including receiver)
  • expander: The expansion function that receives the target and arguments
§Returns
  • Ok(Macro): Successfully created macro
  • Err(Error): Failed to create macro (e.g., invalid name or FFI error)
§Expander Function

The expander receives:

  • factory: A mutable reference to MacroExprFactory for creating new expressions
  • target: The receiver expression (the object before the dot)
  • args: A vector of argument expressions

The expander should return:

  • Some(Expr): The expanded expression to replace the original call
  • None: Keep the original expression unchanged
§Examples
// Macro: list.is_empty() expands to size(list) == 0
let is_empty_macro = Macro::new_receiver("is_empty", 0, |factory, target, _args| {
    let size_call = factory.new_call("size", &[target]);
    Some(factory.new_call("_==_", &[size_call, factory.new_const(0)]))
})?;
Source

pub fn new_receiver_var_arg( name: impl AsRef<str>, expander: impl ReceiverMacroExpander + 'static, ) -> Result<Self, Error>

Creates a new receiver macro that accepts a variable number of arguments.

Variable-argument receiver macros can handle any number of arguments beyond the target expression. The expander receives the target and all arguments.

§Parameters
  • name: The method name that triggers this macro (as a string reference)
  • expander: The expansion function that receives the target and arguments
§Returns
  • Ok(Macro): Successfully created macro
  • Err(Error): Failed to create macro (e.g., invalid name or FFI error)
§Expander Function

The expander receives:

  • factory: A mutable reference to MacroExprFactory for creating new expressions
  • target: The receiver expression (the object before the dot)
  • args: A vector of argument expressions (can be empty)

The expander should return:

  • Some(Expr): The expanded expression to replace the original call
  • None: Keep the original expression unchanged
§Examples
// Macro: str.concat(parts...) concatenates multiple strings
let concat_macro = Macro::new_receiver_var_arg("concat", |factory, target, args| {
    let mut result = target;
    for arg in args {
        result = factory.new_call("_+_", &[result, arg]);
    }
    Some(result)
})?;
Source

pub fn name(&self) -> &[u8]

Returns the name of this macro as a byte slice.

This is the function or method name that triggers macro expansion. The returned byte slice is UTF-8 encoded.

§Examples
let name = macro_def.name();
let name_str = std::str::from_utf8(name).unwrap();
println!("Macro name: {}", name_str);
Source

pub fn argument_count(&self) -> Option<usize>

Returns the expected number of arguments for this macro.

Returns Some(n) for fixed-argument macros requiring exactly n arguments, or None for variable-argument macros that accept any number of arguments.

§Examples
match macro_def.argument_count() {
    Some(n) => println!("Fixed macro with {} arguments", n),
    None => println!("Variable-argument macro"),
}
Source

pub fn is_receiver_style(&self) -> bool

Returns whether this is a receiver-style macro.

Receiver-style macros are invoked as method calls (e.g., target.method(args)), while non-receiver macros are invoked as function calls (e.g., function(args)).

§Returns
Source

pub fn key(&self) -> &[u8]

Returns the unique key identifying this macro.

The key is an internal identifier used by the CEL parser to match macro invocations. It encodes the macro name, receiver style, and argument count.

§Returns

A byte slice representing the macro’s unique key (UTF-8 encoded).

§Note

This method is primarily for internal use and debugging. Most users should use name(), is_receiver_style(), and argument_count() instead.

Trait Implementations§

Source§

impl Debug for Macro

Source§

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

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

impl From<Macro> for UniquePtr<Macro>

Source§

fn from(value: Macro) -> Self

Converts to this type from the input type.
Source§

impl From<UniquePtr<Macro>> for Macro

Source§

fn from(value: UniquePtr<Macro>) -> Self

Converts to this type from the input type.
Source§

impl Hash for Macro

Source§

fn hash<H: Hasher>(&self, state: &mut H)

Feeds this value into the given Hasher. Read more
1.3.0§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for Macro

Source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Eq for Macro

Auto Trait Implementations§

§

impl Freeze for Macro

§

impl RefUnwindSafe for Macro

§

impl Send for Macro

§

impl Sync for Macro

§

impl Unpin for Macro

§

impl UnwindSafe for Macro

Blanket Implementations§

§

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

§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

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

§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
§

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

§

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

Mutably borrows from an owned value. Read more
§

impl<T> From<T> for T

§

fn from(t: T) -> T

Returns the argument unchanged.

§

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

§

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
§

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

§

type Error = Infallible

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

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

Performs the conversion.
§

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

§

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

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

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

Performs the conversion.