Crate axin

Crate axin 

Source
Expand description

§Axin

Axin is a powerful Rust procedural macro library that provides function instrumentation capabilities through the axin attribute macro. This enables clean separation of concerns and reduces boilerplate code in applications that require cross-cutting functionality like logging, timing, validation, or resource management.

§What can Axin do?

Axin allows you change the behavior of functions in a declarative way using attributes. You can:

  • add entry and exit hooks,
  • insert statements at the beginning of function execution, and
  • wrap functions with decorators.

§Entry and Exit Hooks

These hooks allow you to execute custom functions when entering or exiting the target function. It’s also possible to specify arguments for these hooks, which can be used to pass context or configuration.

use axin::axin;

fn setup() {
    println!("Setting up");
}

fn cleanup(msg: &str) {
    println!("Cleaning up: {}", msg);
}

#[axin(on_enter(setup), on_exit(cleanup("Goodbye from function1!")))]
fn function1() {
    println!("Main logic");
}

#[axin(on_enter(setup), on_exit(cleanup("Goodnight from function2!")))]
fn function2() {
    println!("Different logic");
}

fn main() {
    function1();
    function2();
    // Output:
    // Setting up
    // Main logic
    // Cleaning up: Goodbye from function1!
    // Setting up
    // Different logic
    // Cleaning up: Goodnight from function2!
}

§Prologue Statements

Prologue statements allow you to insert arbitrary Rust code at the beginning of the function body. This can be very useful sometimes, as the inserted code shares the same scope as the function, though hooks and decorators are better choices for most use cases.

use axin::axin;

#[axin(prologue(
    println!("Function starting");
))]
fn my_function() {
    println!("Main logic");
}

fn main() {
    my_function();
    // Output:
    // Function starting
    // Main logic
}

§Decorators

Decorators allow you to wrap the function with additional behavior. This is useful for cross-cutting concerns like logging, monitoring, or authentication. Unlike decorators in some other languages, Axin decorators are called every time the function is invoked, not just once at definition time.

Decorators can have parameters, allowing you to customize their behavior based on the context in which they are called. You can refer to the example below to see how to use decorators with and without parameters.

use axin::axin;

fn timing_decorator<F, R>(func: F) -> R
where
   F: FnOnce() -> R,
{
   let start = std::time::Instant::now();
   println!("Starting timer...");
   let result = func();
   println!("Took: {:?}", start.elapsed());
   result
}

#[axin(decorator(timing_decorator))]
fn expensive_computation() -> i32 {
    // Simulate work
    println!("Doing expensive computation...");
    std::thread::sleep(std::time::Duration::from_millis(100));
    42
}

fn custom_logging_decorator<F, R>(msg: &'static str) -> impl FnOnce(F) -> R
where
    F: FnOnce() -> R,
{
    move |f| {
        println!("Custom log: {}", msg);
        f()
    }
}

#[axin(decorator(custom_logging_decorator("Hello from decorated function!")))]
fn decorated_function() -> String {
    println!("Doing something important...");
    "Decorated result".to_string()
}

fn main() {
    let result = expensive_computation();
    println!("Result: {}", result);
    // Output:
    // Starting timer...
    // Doing expensive computation...
    // Took: ...
    // Result: 42

    let decorated_result = decorated_function();
    println!("Decorated result: {}", decorated_result);
    // Output:
    // Custom log: Hello from decorated function!
    // Doing something important...
    // Decorated result: Decorated result
}

Decorators do not support variadic arguments, due to the limitation of Rust.

§Order of Execution

The order of execution for the various Axin features is as follows:

  1. Entry hook function (if specified) is executed first, then
  2. Decorator function (if specified) is called, and when it calls the original function,
  3. Prologue statements (if specified) are executed, and then
  4. The original function body is executed, after which
  5. The control flow returns to the decorator, and after it completes,
  6. The exit hook function (if specified) is executed last.

Attribute Macros§

axin
An attribute procedural macro that enhances functions with entry and exit hooks, decorators, and prologue statements.