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
use proc_macro2::{Span, TokenStream as TokenStream2};
use syn::{
parse2, spanned::Spanned, Error, FnArg, GenericParam, ItemFn, Lifetime, LifetimeParam, Result,
ReturnType, Type,
};
/// The implementation of the hook macro, this macro takes the given function and changes
/// it's output and body to fit into a `Pin<Box<dyn Future>>`
pub fn hook(input: TokenStream2) -> Result<TokenStream2> {
let fun = parse2::<ItemFn>(input)?;
let ItemFn {
attrs,
vis,
mut sig,
block,
} = fun;
if sig.asyncness.is_none() {
/*
In order to return a `Future` object, the function must be async, so if this function is
not even async, this makes no sense to even try to execute this macro's function
*/
return Err(Error::new(sig.span(), "Function must be marked async"));
}
// As we will change the return to return a Pin<Box<dyn Future>>
// we don't need the function to be async anymore.
sig.asyncness = None;
// The output of the function as a token stream, so we can quote it after
let output = match &sig.output {
ReturnType::Default => quote::quote!(()),
ReturnType::Type(_, t) => quote::quote!(#t),
};
// Wrap the original result in a Pin<Box<dyn Future>>
sig.output = parse2(quote::quote!(
-> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = #output> + 'future + Send>>
))?;
/*
As we know all functions marked with this macro have an `SlashContext` reference, we have to
add a lifetime which will be assigned to all references used in the function and to the returned
future
*/
sig.generics.params.insert(
0,
GenericParam::Lifetime(LifetimeParam {
attrs: Default::default(),
lifetime: Lifetime::new("'future", Span::call_site()),
colon_token: None,
bounds: Default::default(),
}),
);
for i in sig.inputs.iter_mut() {
if let FnArg::Typed(kind) = i {
// If the argument is a reference, assign the previous defined lifetime to it
if let Type::Reference(ty) = &mut *kind.ty {
ty.lifetime = Some(Lifetime::new("'future", Span::call_site()));
}
}
}
Ok(quote::quote! {
#(#attrs)*
#vis #sig {
Box::pin(async move #block)
}
})
}