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}