context_manager/
lib.rs

1#![deny(
2    dead_code,
3    missing_debug_implementations,
4    missing_docs,
5    rust_2018_idioms,
6    rustdoc::all,
7    rustdoc::missing_crate_level_docs,
8    unreachable_pub,
9    unused_imports,
10    unused_variables
11)]
12// Use README.md file as module documentation
13// This makes easy to have a proper home for the github project as well as
14// ensuring that the content is always up to date and tested
15#![doc = include_str!("../README.md")]
16//!
17#![doc = include_str!("../CHANGELOG.md")]
18
19mod t_async;
20mod t_sync;
21pub use crate::t_async::AsyncWrapContext;
22pub use crate::t_sync::SyncWrapContext;
23
24/// Context about the caller propagated into the context.
25#[derive(Debug)]
26#[non_exhaustive]
27pub struct CallerContext {
28    /// Name of the wrapped function
29    fn_name: &'static str,
30}
31
32impl CallerContext {
33    /// Create a new instance of the `CallerContext`
34    #[must_use]
35    pub const fn new(fn_name: &'static str) -> Self {
36        Self { fn_name }
37    }
38
39    /// Name of the wrapped function
40    #[must_use]
41    pub const fn fn_name(&self) -> &'static str {
42        self.fn_name
43    }
44}
45
46/// Procedural macro that will decorate the incoming async function with the provided context.
47///
48/// The context is expected to be a type that implements the `AsyncWrapContext` trait.
49///
50/// Usage example:
51/// ```
52/// # use context_manager_macro::async_wrap;
53/// struct AsyncPrintDuration;
54/// impl<T> context_manager::AsyncWrapContext<T> for AsyncPrintDuration {
55///   async fn new() -> Self { Self }
56/// }
57///
58/// #[async_wrap(AsyncPrintDuration)]
59/// async fn foo<'a, T>(int_value: usize, str_ref: &'a str, generic: T) -> usize {
60///     let type_name = std::any::type_name::<T>();
61///     println!("Async call with int_value={int_value}, str_ref={str_ref}, type_of(T)={type_name}");
62///     10
63/// }
64/// ```
65///
66/// The decorator does not induce limits on the shape of the incoming function, in terms
67/// of generics, sync/async, lifetime, etc.
68///
69/// The decorator will expand the incoming function by adding the context handling
70/// rendering something similar to
71/// ```
72/// # use context_manager::{AsyncWrapContext, CallerContext};
73/// # struct AsyncPrintDuration;
74/// # impl<T> AsyncWrapContext<T> for AsyncPrintDuration {
75/// #   async fn new() -> Self { Self }
76/// # }
77/// async fn foo<'a, T>(int_value: usize, str_ref: &'a str, generic: T) -> usize {
78///     AsyncPrintDuration::run(CallerContext { fn_name: "foo" }, async {
79///         let type_name = std::any::type_name::<T>();
80///         println!("Async call with int_value={int_value}, str_ref={str_ref}, type_of(T)={type_name}");
81///         10
82///     }).await
83/// }
84/// ```
85///
86/// The structuring of the generated code is though to avoid any clone/copy of data,
87/// as well as reducing the number of jumps needed to execute the original code.
88///
89/// # Possible compile errors
90/// ## Passing a type that does not implement `AsyncWrapContext` trait will lead to compile errors.
91/// ```compile_fail
92/// # use context_manager_macro::async_wrap;
93/// struct PrintDuration;
94/// impl<T> context_manager::SyncWrapContext<T> for PrintDuration {
95///   fn new() -> Self { Self }
96/// }
97///
98/// #[async_wrap(PrintDuration)]
99/// async fn foo() {}
100/// ```
101/// would lead to the following compile error
102/// ```text
103/// error[E0277]: the trait bound `PrintDuration: AsyncWrapContext<_>` is not satisfied
104///   --> src/lib.rs:206:1
105///    |
106/// 11 | #[async_wrap(PrintDuration)]
107///    |                  ^^^^^^^^^^^^^ the trait `AsyncWrapContext<_>` is not implemented for `PrintDuration`
108///    |
109///    = help: the trait `AsyncWrapContext<T>` is implemented for `AsyncTraceDuration`
110/// ```
111///
112/// ## Decorating a synchronous function
113/// This is intentional in order to avoid possibly stalling the async-runtime in use.
114/// Please consider wrapping yourself the synchronous code in an async block, or using `#[decorate]` whether possible.
115/// ```compile_fail
116/// # use context_manager_macro::async_wrap;
117/// struct AsyncPrintDuration;
118/// impl<T> context_manager::AsyncWrapContext<T> for AsyncPrintDuration {
119///   async fn new() -> Self { Self }
120/// }
121///
122/// #[async_wrap(PrintDuration)]
123/// fn foo() {}
124/// ```
125/// would lead to the following error
126/// ```text
127/// error: #[async_wrap] cannot operate on sync functions. Please consider using a #[decorate] macro or converting/wrapping the function to be async.
128///   --> src/lib.rs:228:1
129///    |
130/// 11 | #[async_wrap(PrintDuration)]
131///    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
132///    |
133/// ```
134pub use context_manager_macro::async_wrap;
135
136/// Procedural macro that will decorate the incoming function with the provided context.
137///
138/// The context is expected to be a type that implements the `SyncWrapContext` trait.
139///
140/// Usage example:
141/// ```
142/// # use context_manager_macro::wrap;
143/// struct PrintDuration;
144/// impl<T> context_manager::SyncWrapContext<T> for PrintDuration {
145///   fn new() -> Self { Self }
146/// }
147///
148/// #[wrap(PrintDuration)]
149/// fn sync_foo<'a, T>(int_value: usize, str_ref: &'a str, generic: T) -> usize {
150///     let type_name = std::any::type_name::<T>();
151///     println!("Sync call with int_value={int_value}, str_ref={str_ref}, type_of(T)={type_name}");
152///     10
153/// }
154///
155/// #[wrap(PrintDuration)]
156/// async fn async_foo<'a, T>(int_value: usize, str_ref: &'a str, generic: T) -> usize {
157///     let type_name = std::any::type_name::<T>();
158///     println!("Async call with int_value={int_value}, str_ref={str_ref}, type_of(T)={type_name}");
159///     10
160/// }
161/// ```
162///
163/// The decorator does not induce limits on the shape of the incoming function, in terms
164/// of generics, sync/async, lifetime, etc.
165///
166/// The decorator will expand the incoming function by adding the context handling
167/// rendering something similar to
168/// ```
169/// # use context_manager::{CallerContext, SyncWrapContext};
170/// # struct PrintDuration;
171/// # impl<T> SyncWrapContext<T> for PrintDuration {
172/// #   fn new() -> Self { Self }
173/// # }
174/// fn sync_foo<'a, T>(int_value: usize, str_ref: &'a str, generic: T) -> usize {
175///     PrintDuration::run_sync(CallerContext { fn_name: "sync_foo" }, move || {
176///         let type_name = std::any::type_name::<T>();
177///         println!("Sync call with int_value={int_value}, str_ref={str_ref}, type_of(T)={type_name}");
178///         10
179///     })
180/// }
181///
182/// async fn async_foo<'a, T>(int_value: usize, str_ref: &'a str, generic: T) -> usize {
183///     PrintDuration::run_async(CallerContext { fn_name: "async_foo" }, async {
184///         let type_name = std::any::type_name::<T>();
185///         println!("Async call with int_value={int_value}, str_ref={str_ref}, type_of(T)={type_name}");
186///         10
187///     }).await
188/// }
189/// ```
190///
191/// The structuring of the generated code is though to avoid any clone/copy of data,
192/// as well as reducing the number of jumps needed to execute the original code.
193///
194/// # Possible compile errors
195/// ## Passing a type that does not implement `SyncWrapContext` trait will lead to compile errors.
196/// ```compile_fail
197/// # use context_manager_macro::wrap;
198/// struct AsyncPrintDuration;
199/// impl<T> context_manager::AsyncWrapContext<T> for AsyncPrintDuration {
200///   async fn new() -> Self { Self }
201/// }
202///
203/// #[wrap(AsyncPrintDuration)]
204/// fn foo() {}
205/// ```
206/// would lead to the following compile error
207/// ```text
208/// ---- src/lib.rs - decorate (line 98) stdout ----
209/// error[E0277]: the trait bound `AsyncPrintDuration: SyncWrapContext<_>` is not satisfied
210///   --> src/lib.rs:106:12
211///    |
212/// 11 | #[wrap(AsyncPrintDuration)]
213///    |            ^^^^^^^^^^^^^^^^^^ the trait `SyncWrapContext<_>` is not implemented for `AsyncPrintDuration`
214///    |
215///    = help: the trait `SyncWrapContext<T>` is implemented for `TraceDuration`
216/// ```
217///
218/// ## Decorating a constant function
219/// Const functions are not supported for decoration.
220/// This is a side-effect of embedding code that is not const compatible (like async blocks and closures).
221///
222/// ```compile_fail
223/// # use context_manager_macro::wrap;
224/// struct PrintDuration;
225/// impl<T> context_manager::SyncWrapContext<T> for PrintDuration {
226///   fn new() -> Self { Self }
227/// }
228///
229/// #[wrap(PrintDuration)]
230/// const fn foo() {}
231/// ```
232/// would lead to the following error
233/// ```text
234/// error: #[wrap] cannot operate on const functions
235///   --> context_manager_macro/src/lib.rs:131:1
236///    |
237/// 11 | #[wrap(PrintDuration)]
238///    | ^^^^^^^^^^^^^^^^^^^^^^
239///    |
240/// ```
241pub use context_manager_macro::wrap;
242
243#[cfg(test)]
244mod tests {
245    use trybuild::TestCases;
246
247    #[test]
248    fn procedural_macros_ui_tests() {
249        let t = TestCases::new();
250        t.pass("tests/ui/pass/*.rs");
251        t.compile_fail("tests/ui/fail/*.rs");
252    }
253}