Hone logo
Hone
Problems

Implement a Custom log_execution Attribute Macro in Rust

This challenge will guide you through the creation of a procedural attribute macro in Rust. You will build a macro named log_execution that, when applied to a function, will automatically log a message to the console before the function's execution begins and after it completes. This is a common pattern for debugging and performance monitoring, allowing you to easily track when and how often functions are called.

Problem Description

Your task is to create a procedural attribute macro in Rust called log_execution. This macro should be applicable to functions.

Key Requirements:

  1. Macro Definition: Define a procedural attribute macro named log_execution.
  2. Function Wrapping: When #[log_execution] is applied to a function, the macro should wrap the original function's body.
  3. Logging Before Execution: Before the original function's code is executed, the macro should print a message to stdout indicating that the function is about to be executed. The message should include the name of the function.
  4. Logging After Execution: After the original function's code has finished executing (regardless of whether it returns successfully or panics), the macro should print a message to stdout indicating that the function has completed. This message should also include the function's name.
  5. Preserve Function Signature: The macro must preserve the original function's signature, including its arguments, return type, and visibility.
  6. Handle Different Return Types: The macro should correctly handle functions that return values, functions that return () (unit type), and functions that panic!.

Expected Behavior:

When a function is annotated with #[log_execution], its execution should be surrounded by logging statements.

Edge Cases to Consider:

  • Functions with no arguments.
  • Functions with various argument types.
  • Functions returning different types (including ()).
  • Functions that might panic!.

Examples

Example 1: Simple Function with No Return Value

#[log_execution]
fn greet(name: &str) {
    println!("Hello, {}!", name);
}

fn main() {
    greet("World");
}

Expected Output:

[LOG] Entering function: greet
Hello, World!
[LOG] Exiting function: greet

Example 2: Function with a Return Value

#[log_execution]
fn add(a: i32, b: i32) -> i32 {
    println!("Performing addition...");
    a + b
}

fn main() {
    let sum = add(5, 3);
    println!("Result: {}", sum);
}

Expected Output:

[LOG] Entering function: add
Performing addition...
[LOG] Exiting function: add
Result: 8

Example 3: Function that Panics

#[log_execution]
fn divide(a: i32, b: i32) -> i32 {
    if b == 0 {
        panic!("Division by zero!");
    }
    a / b
}

fn main() {
    // This call will panic
    let _ = std::panic::catch_unwind(|| {
        divide(10, 0);
    });
    println!("This line might not be reached if the panic is unhandled.");
}

Expected Output (when run in a context that catches the panic, like catch_unwind or a test that allows panics):

[LOG] Entering function: divide
[LOG] Exiting function: divide (panicked: Division by zero!)

Note: The exact panic message formatting might vary slightly depending on the Rust version and how the panic is handled.

Constraints

  • The macro must be implemented using Rust's procedural macro system.
  • You should use the syn and quote crates for parsing and generating Rust code.
  • The logging output should be directed to stdout.
  • The macro should be part of a library crate (e.g., proc-macro = true in Cargo.toml).

Notes

  • You'll need to create a separate crate for your procedural macro.
  • Consider how to handle the function's return value, especially when logging after execution. A std::panic::catch_unwind block within the macro's generated code is a good approach to ensure the "exiting" log message is always printed.
  • The function name can be extracted from the syn AST.
  • This challenge is about understanding procedural macros, AST manipulation, and code generation. Focus on correctness and clarity of the generated code.
Loading editor...
rust