1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*!
Macros for defining function `wrappers` and `wrapped` functions.


# Usage:

`wrap` provides two macros, `def_wrapper!` and `wrap_with!`.
`def_wrapper!` lets you define `before` and `after` actions to wrap any given function.
`wrappers` can then be applied to arbitrary functions using `wrap_with!` which takes a `wrapper` name and
function definition and expands to a wrapped form.

`before` blocks can be used to evaluate the wrapped function's arguments, create and pass on contextual info to the wrapped function,
and short-circuit to return before evaluation of the wrapped function. `before` blocks' access to the wrapped function arguments
is limited to a tuple of references, `(&arg1, &arg2, ...)`. `before` blocks cannot own or mutate function arguments as that would
require cloning all arguments and defining `wrap_with!` functions to take `mut` arguments.

`after` blocks can be used to mutate the wrapped function's result and can access the `before` block's value. `after` blocks cannot
access the wrapped function's arguments as the wrapped function is expected to consume them.

See examples for more complex wrappers.


```rust
#[macro_use] extern crate wrap;


def_wrapper!{log_wrap =
    before = (fn_args) >> {
        println!("* [log] >> before everything! fn_args: {:?}", fn_args);
    };
    after  = (wrapped_result) >> {
        println!("* [log] >> after everything! wrapped_result: {:?}", wrapped_result);
    };
}


wrap_with!{log_wrap >>
fn greet(name: &str) -> String = {
    format!("Hello, {}!", name)
}}


pub fn main() {
    greet("bean");
}
```

*/

#[macro_export]
macro_rules! def_wrapper {
    ($name:ident = before = ($fn_args:ident) >> $before:expr ; after = ($wrapped_res:ident) >> $after:expr;) => {
        macro_rules! $name {
            (_ : _; $args:expr => $wrapped_body_ret:ty = $wrapped_body:expr) => {
                {
                    let mut _before  = {
                        let $fn_args = $args;
                        $before
                    };
                    let mut $wrapped_res: $wrapped_body_ret = (|| $wrapped_body)();
                    let _ = $after;
                    $wrapped_res
                }
            };
            ($before_block_res:ident : $before_block_ty:ty ; $args:expr => $wrapped_body_ret:ty = $wrapped_body:expr) => {
                {

                    let mut $before_block_res: $before_block_ty = {
                        // assign wrapped function's args (refs to args collected in a tuple)
                        // to our `fn_args` ident, to make available to the `before` block
                        let $fn_args = $args;

                        // eval the `before` block and assign to the `before_block_res` ident supplied by `wrap_with!`
                        $before
                    };

                    // eval our wrapped function, providing `&mut before_block_res` to the `wrapped_body`,
                    // assigning the result to our ``wrapped_res` ident to make available to our `after` block and
                    // also keep `before_block_res` in scope for the `after` block
                    let mut $wrapped_res: $wrapped_body_ret = (|$before_block_res: &mut $before_block_ty| $wrapped_body)(&mut $before_block_res);

                    // eval the `after` block. Can access and mutate both `wrapped_res` and `before_block_res` if necessary.
                    // cannot access `fn_args` because they are likely consumed by the wrapped function.
                    let _ = $after;

                    $wrapped_res
                }
            };

            // same as above, but accept and pass along `mut` keyword
            (mut $before_block_res:ident : $before_block_ty:ty ; $args:expr => $wrapped_body_ret:ty = $wrapped_body:expr) => {
                {
                    let mut $before_block_res: $before_block_ty = {
                        let $fn_args = $args;
                        $before
                    };
                    let mut $wrapped_res: $wrapped_body_ret = (|mut $before_block_res: &mut $before_block_ty| $wrapped_body)(&mut $before_block_res);
                    let _ = $after;
                    $wrapped_res
                }
            };
        }
    }
}


#[macro_export]
macro_rules! wrap_with {
    ($wrapper:ident >> fn $name:ident ($($arg:ident : $argtype:ty),*) -> $ret:ty = $body:expr) => {
        #[allow(unused_mut, unused_parens)]
        pub fn $name($($arg : $argtype),*) -> $ret {
            $wrapper!( _ : _ ; ($(&$arg),*) => $ret = $body)
        }
    };
    ($wrapper:ident -> $before_block_res:ident : $before_block_ty:ty; >> fn $name:ident ($($arg:ident : $argtype:ty),*) -> $ret:ty = $body:expr) => {
        #[allow(unused_mut, unused_parens)]
        pub fn $name($($arg : $argtype),*) -> $ret {
            $wrapper!( $before_block_res : $before_block_ty ;  ($(&$arg),*) => $ret = $body)
        }
    };
    // pass along `mut ident`
    ($wrapper:ident -> mut $before_block_res:ident : $before_block_ty:ty; >> fn $name:ident ($($arg:ident : $argtype:ty),*) -> $ret:ty = $body:expr) => {
        #[allow(unused_mut, unused_parens)]
        pub fn $name($($arg : $argtype),*) -> $ret {
            $wrapper!( mut $before_block_res : $before_block_ty ;  ($(&$arg),*) => $ret = $body)
        }
    };
}