#![deny(
dead_code,
missing_debug_implementations,
missing_docs,
rust_2018_idioms,
rustdoc::all,
rustdoc::missing_crate_level_docs,
unreachable_pub,
unused_imports,
unused_variables
)]
use proc_macro::TokenStream;
use quote::quote;
use syn::parse::Parse;
use syn::parse::ParseStream;
use syn::parse_macro_input;
use syn::parse_quote;
use syn::Block;
use syn::Error;
use syn::ItemFn;
use syn::Type;
struct Args {
context_type: Type,
}
impl Parse for Args {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
if input.is_empty() {
Err(Error::new(
input.span(),
"Expected a type as argument: `#[wrap(Type)]` or `#[async_wrap(Type)]`",
))
} else {
Ok(Self {
context_type: input.parse::<Type>()?,
})
}
}
}
#[proc_macro_attribute]
pub fn wrap(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut in_func = parse_macro_input!(item as ItemFn);
if in_func.sig.constness.is_some() {
in_func.block.stmts.insert(
0,
parse_quote!(::std::compile_error!("#[wrap] cannot operate on const functions.");),
);
return quote! { #in_func }.into();
};
let args: Args = parse_macro_input!(attr);
let context_type = &args.context_type;
let block = &in_func.block;
let ident = in_func.sig.ident.to_string();
let caller_context = quote! { ::context_manager::CallerContext::new(#ident) };
let new_body: TokenStream = if in_func.sig.asyncness.is_some() {
quote! {
{
<#context_type as ::context_manager::SyncWrapContext<_>>::run_async(#caller_context, async #block).await
}
}
.into()
} else {
quote! {
{
<#context_type as ::context_manager::SyncWrapContext<_>>::run_sync(#caller_context, move || #block)
}
}
.into()
};
in_func.block.stmts = parse_macro_input!(new_body as Block).stmts;
quote! { #in_func }.into()
}
#[proc_macro_attribute]
pub fn async_wrap(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut in_func = parse_macro_input!(item as ItemFn);
if in_func.sig.constness.is_some() {
in_func.block.stmts.insert(
0,
parse_quote!(::std::compile_error!("#[wrap] cannot operate on const functions.");),
);
return quote! { #in_func }.into();
};
let args: Args = parse_macro_input!(attr);
let context_type = &args.context_type;
let block = &in_func.block;
if in_func.sig.asyncness.is_some() {
let ident = in_func.sig.ident.to_string();
let caller_context = quote! { ::context_manager::CallerContext::new(#ident) };
let new_body: TokenStream = quote! {
{
<#context_type as ::context_manager::AsyncWrapContext<_>>::run(#caller_context, async #block).await
}
}
.into();
in_func.block.stmts = parse_macro_input!(new_body as Block).stmts;
} else {
in_func.block.stmts.insert(
0,
parse_quote!({::std::compile_error!(
"#[async_wrap] cannot operate on sync functions. Please consider using a #[wrap] macro or converting/wrapping the function to be async."
)})
);
};
quote! { #in_func }.into()
}