#decorator #macro #member #wrapper #static #function #decorating

macro fn-decorator

The crate contains macros for implementing wrapper functions around member or static functions

5 stable releases

1.1.2 Jul 9, 2023
1.0.1 Jul 5, 2023

#205 in Procedural macros

Download history 16/week @ 2024-02-18 4/week @ 2024-02-25 6/week @ 2024-03-10 79/week @ 2024-03-31

85 downloads per month

MIT license

20KB
227 lines

fn-decorator

The crate contains macros for implementing wrapper functions around member or static functions.

Using the use_decorator

Decorator function

fn decorator(f: fn() -> i64) -> i64 {
    f() + 1
}

Decorating a function

#[use_decorator(decorator())]
fn get_1() -> i64 {
    1
}

When get_1() is called, then decorator(get_1) is executed instead. The decorator function can decide whether it calls the received function or not.

There is also a use_impl_decorator macro that works in impl blocks.

Both macros can have the same parameters:

  • Decorator function call that should be executed. This can contain parameters. See examples for exact usage!
  • hide_parameters = [...]: if the decorator function signature does not match the decorated, then this list can be used to hide some parameters from the decorator function.
  • debug: when this parameter is given, then the code will generate a compile error with the generated source code. This is useful for debugging purposes.

Fully working examples

Decorating a function that has no parameters

use fn_decorator::use_decorator;

fn decorator(f: fn() -> i64) -> i64 {
    f() + 1
}

#[use_decorator(decorator())]
fn get_1() -> i64 {
    1
}

#[test]
fn fn_without_params_decorator() {
    let result = get_1();
    assert_eq!(result, 2);
}

Decorating a function that has parameters

use fn_decorator::use_decorator;

fn decorator(f: fn(x: i64) -> i64, x: i64) -> i64 {
    f(x) * 2
}

#[use_decorator(decorator())]
fn double(x: i64) -> i64 {
    x * 2
}

#[test]
fn decorator_without_params() {
    let result = double(2);
    assert_eq!(result, 8);
}

Using a parameterized decorator function

use fn_decorator::use_decorator;

fn decorator(
    middle: String,
    f: fn(String, String) -> String,
    left: String,
    right: String,
) -> String {
    let left = left + &middle;
    f(left, right)
}

#[use_decorator(decorator("_middle_".to_string()))]
fn concat(left: String, right: String) -> String {
    left + &right
}

#[test]
fn decorator_with_param() {
    let result = concat("left".into(), "right".into());
    assert_eq!(result, "left_middle_right");
}

Debugging an fn decorator

Please be aware that the tests does not contain this code, because it produces a compile time error.

use fn_decorator::use_decorator;

fn decorator(f: fn() -> i64) -> i64 {
    f() + 1
}

#[use_decorator(decorator(), debug)]
fn get_1() -> i64 {
    1
}

#[test]
fn fn_without_params_decorator() {
    let result = get_1();
    assert_eq!(result, 2);
}

Decorating a member function

use fn_decorator::use_impl_decorator;

fn decorator(f: fn(&MyStruct, y: i64) -> i64, receiver: &MyStruct, y: i64) -> i64 {
    f(receiver, y) + 1
}

struct MyStruct {
    x: i64,
}

impl MyStruct {
    #[use_impl_decorator(decorator())]
    fn add(&self, y: i64) -> i64 {
        self.x + y
    }
}

#[test]
fn impl_member_decorator() {
    let obj = MyStruct { x: 1 };
    let result = obj.add(1);
    assert_eq!(result, 3);
}

Decorating a static member function

use fn_decorator::use_impl_decorator;

fn decorator(f: fn(x: i64) -> i64, x: i64) -> i64 {
    f(x) + 1
}

struct MyStruct;

impl MyStruct {
    #[use_impl_decorator(decorator())]
    fn double(x: i64) -> i64 {
        x * 2
    }
}

#[test]
fn impl_static_member_decorator() {
    let result = MyStruct::double(2);
    assert_eq!(result, 5);
}

Decorating an async function

use std::future::Future;

use fn_decorator::use_decorator;

async fn decorator<FutureType: Future<Output = i64>>(f: fn(x: i64) -> FutureType, x: i64) -> i64 {
    f(x).await * 2
}

#[use_decorator(decorator())]
async fn double(x: i64) -> i64 {
    x * 2
}

#[tokio::test]
async fn async_decorator() {
    let result = double(2).await;
    assert_eq!(result, 8);
}

Hiding parameters of an async function from a decorator

use std::future::Future;

use fn_decorator::use_decorator;

async fn decorator<FutureType: Future<Output = String>>(
    middle: String,
    f: impl FnOnce(String) -> FutureType,
    right: String,
) -> String {
    let right = middle + &right;
    f(right).await
}

#[use_decorator(decorator("_middle_".to_string()), hide_parameters = [left])]
async fn concat(left: String, right: String) -> String {
    left + &right
}

#[tokio::test]
async fn hiding_params_of_async_fn_decorator() {
    let result = concat("left".into(), "right".into()).await;
    assert_eq!(result, "left_middle_right");
}

Hiding parameters of an async member function from a decorator

use std::future::Future;

use fn_decorator::use_impl_decorator;

async fn decorator<'a, FutureType: Future<Output = &'a String>>(
    middle: String,
    f: impl FnOnce(&'a mut MyStruct) -> FutureType,
    receiver: &'a mut MyStruct,
) -> &'a String {
    receiver.left.push_str(&middle);
    f(receiver).await
}

struct MyStruct {
    left: String,
}

impl MyStruct {
    #[use_impl_decorator(decorator("_middle_".to_string()), hide_parameters = [right])]
    async fn concat(&mut self, right: String) -> &String {
        self.left.push_str(&right);
        &self.left
    }
}

#[tokio::test]
async fn hiding_params_of_async_impl_member_decorator() {
    let mut obj = MyStruct {
        left: "left".into(),
    };
    let result = obj.concat("right".into()).await;
    assert_eq!(result, "left_middle_right");
}

Hiding parameters of a function from a decorator

use fn_decorator::use_decorator;

fn decorator(middle: String, f: impl FnOnce(String) -> String, right: String) -> String {
    let right = middle + &right;
    f(right)
}

#[use_decorator(decorator("_middle_".to_string()), hide_parameters = [left])]
fn concat(left: String, right: String) -> String {
    left + &right
}

#[test]
fn hiding_params_of_fn_decorator() {
    let result = concat("left".into(), "right".into());
    assert_eq!(result, "left_middle_right");
}

Hiding parameters of a member function from a decorator

use fn_decorator::use_impl_decorator;

fn decorator(middle: String, f: impl FnOnce(&MyStruct) -> String, receiver: &MyStruct) -> String {
    let new_struct = MyStruct {
        left: receiver.left.clone() + &middle,
    };
    f(&new_struct)
}

struct MyStruct {
    left: String,
}

impl MyStruct {
    #[use_impl_decorator(decorator("_middle_".to_string()), hide_parameters = [right])]
    fn concat(&self, right: String) -> String {
        self.left.clone() + &right
    }
}

#[test]
fn hiding_params_of_impl_member_decorator() {
    let obj = MyStruct {
        left: "left".into(),
    };
    let result = obj.concat("right".into());
    assert_eq!(result, "left_middle_right");
}

Hiding self of a member function from a decorator

use fn_decorator::use_impl_decorator;

fn decorator(middle: String, f: impl FnOnce(String) -> String, right: String) -> String {
    let right = middle + &right;
    f(right)
}

struct MyStruct {
    left: String,
}

impl MyStruct {
    #[use_impl_decorator(decorator("_middle_".to_string()), hide_parameters = [self])]
    fn concat(&self, right: String) -> String {
        self.left.clone() + &right
    }
}

#[test]
fn hiding_self_param_in_impl_member_decorator() {
    let obj = MyStruct {
        left: "left".into(),
    };
    let result = obj.concat("right".into());
    assert_eq!(result, "left_middle_right");
}

Decorating an async member function

use std::future::Future;

use fn_decorator::use_impl_decorator;

async fn decorator<'a, FutureType: Future<Output = i64>>(
    f: fn(&'a MyStruct, y: i64) -> FutureType,
    receiver: &'a MyStruct,
    y: i64,
) -> i64 {
    f(receiver, y).await + 1
}

struct MyStruct {
    x: i64,
}

impl MyStruct {
    #[use_impl_decorator(decorator())]
    async fn add(&self, y: i64) -> i64 {
        self.x + y
    }
}

#[tokio::test]
async fn async_impl_member_decorator() {
    let obj = MyStruct { x: 1 };
    let result = obj.add(1).await;
    assert_eq!(result, 3);
}

Decorating an async static member function

use std::future::Future;

use fn_decorator::use_impl_decorator;

async fn decorator<FutureType: Future<Output = i64>>(f: fn(y: i64) -> FutureType, x: i64) -> i64 {
    f(x).await + 1
}

struct MyStruct;

impl MyStruct {
    #[use_impl_decorator(decorator())]
    async fn double(x: i64) -> i64 {
        x * 2
    }
}

#[tokio::test]
async fn async_impl_member_decorator() {
    let result = MyStruct::double(2).await;
    assert_eq!(result, 5);
}

Dependencies

~315–760KB
~18K SLoC