aspect/
lib.rs

1//! # An Aspect Toolkit for Rust
2//!
3//! Aspect-RS is a project aiming to provide common ground for the main Aspect-Oriented use cases in Rust. By leveraging the trait system, declarative and procedural macros, Aspect-RS provides blocks that let you wrap methods with your custom logic.
4//!
5//! The project has been extracted from the [Metered project](https://github.com/magnet/metered-rs), which uses the technique to build metrics that can work on expressions or methods, whether they're `async` or not. The technique seemed general enough to be in its own crate and see if it is of any interest to other parties.
6//!
7//! Aspect-RS provides "pointcut" traits when entering or exiting an expression (`OnEnter` and `OnResult`), experimental `Update` and `UpdateRef` traits that can use parameter shadowing to intercept and update method parameters, and weaving constructs useful when building procedural macros. Please look at the [Metered project](https://github.com/magnet/metered-rs) to see Aspect-RS in action.
8
9#![deny(missing_docs)]
10#![deny(warnings)]
11
12pub mod update;
13pub use aspect_weave::Weave;
14
15/// The `Enter` trait is called when entering in an aspect, before the wrapped expression is called.
16pub trait Enter {
17    /// The type returned by the `enter` function and carried to `OnResult`
18    type E;
19
20    /// `enter` is called when entering in an aspect
21    ///
22    /// Use it to set-up some context before calling the expression. For instance, the `ResponseTime` metric in the [metered](https://github.com/magnet/metered-rs) crate uses it to get the current time before the invocation, and pass it over to `OnResult` to compute the elapsed time.
23    ///
24    /// Aspects which don't need enter can simply do nothing and return unit.
25    fn enter(&self) -> Self::E;
26}
27
28/// An `Advice` describes what the aspect should do on return
29pub enum Advice {
30    /// Return the expression value
31    Return,
32    /// Call the expression again.
33    ///
34    /// Experimental. Use with caution regarding side-effects in the expression and possible loops.
35    Retry,
36}
37
38/// The `OnResult` trait is implemented on Aspects to get notified when an expression has returned.
39pub trait OnResult<R>: Enter {
40    /// Called when an expression has returned.
41    ///
42    /// This function is passed both the enter return value, and the expression return value.
43    ///
44    /// `on_result` does not get a chance to alter the returned result. Use `OnResultMut` for that purpose.
45    fn on_result(&self, enter: <Self as Enter>::E, _result: &R) -> Advice {
46        self.leave_scope(enter)
47    }
48
49    /// Called when an expression has exited, but the return value isn't known.
50    /// This can happen because of a panic, or if control flow bypasses a macro.
51    /// This is also called by the default implementation of `on_result`.
52    fn leave_scope(&self, _enter: <Self as Enter>::E) -> Advice {
53        Advice::Return
54    }
55}
56
57/// The `OnResult` trait is implemented on Aspects to get notified when an expression has returned, and provide
58/// the possibility to alter the result.
59pub trait OnResultMut<R>: Enter {
60    /// Called when an expression has returned.
61    ///
62    /// This function is passed both the enter return value, and the expression return value.
63    fn on_result(&self, enter: <Self as Enter>::E, _result: &mut R) -> Advice {
64        self.leave_scope(enter)
65    }
66
67    /// Called when an expression has exited, but the return value isn't known.
68    /// This can happen because of a panic, or if control flow bypasses a macro.
69    /// This is also called by the default implementation of `on_result`.
70    fn leave_scope(&self, _enter: <Self as Enter>::E) -> Advice {
71        Advice::Return
72    }
73}
74
75impl<R, A: OnResult<R>> OnResultMut<R> for A {
76    fn on_result(&self, enter: <Self as Enter>::E, result: &mut R) -> Advice {
77        <Self as OnResult<R>>::on_result(self, enter, result)
78    }
79
80    fn leave_scope(&self, enter: <Self as Enter>::E) -> Advice {
81        <Self as OnResult<R>>::leave_scope(self, enter)
82    }
83}