#![warn(missing_docs)]
#![doc = include_str!("../README.md")]
use proc_macro::TokenStream;
use quote::ToTokens;
use syn::{FnArg, Pat};
fn fn_arg_to_name(arg: &FnArg) -> Option<String> {
match arg {
FnArg::Typed(typed) => match typed.pat.as_ref() {
Pat::Ident(ident) => Some(ident.ident.to_string()),
other => panic!("BEGIN\n{:#?}END\n\nHello. Sadly, what ever\
you are trying to do is not supported yet. Please post \
The code between BEGIN and END on GitHub, so I can implement it.", other),
},
FnArg::Receiver(_self) => None,
}
}
#[cfg(not(feature = "tokio"))]
#[proc_macro_attribute]
pub fn also_sync_tokio(_attr: TokenStream, item: TokenStream) -> TokenStream {
item
}
#[cfg(feature = "tokio")]
#[proc_macro_attribute]
pub fn also_sync_tokio(_attr: TokenStream, item: TokenStream) -> TokenStream {
let mut ast: syn::ItemFn =
syn::parse(item.clone()).expect("Annotated method appears to have a syntax error.");
if ast.sig.asyncness.is_none() {
panic!("also_sync can only be used on async functions and methods.")
}
ast.sig.asyncness = None;
let function_calling = ast
.sig
.inputs
.iter()
.filter_map(fn_arg_to_name)
.collect::<Vec<String>>()
.join(", ");
let function_name = match ast.sig.inputs.iter().next() {
Some(FnArg::Receiver(_)) => format!("self.{}", ast.sig.ident),
_ => ast.sig.ident.to_string(),
};
let new_body = format!(
"{{
let handle = also_sync::TOKIO_RUNTIME.handle();
handle.block_on(async move {{ {function_name}({arguments}).await }})
}}",
function_name = function_name,
arguments = function_calling
);
let ast_new_block: syn::Block = syn::parse_str(new_body.as_str())
.expect("Code generated by macro appears to have syntax errors.");
ast.block = Box::new(ast_new_block);
ast.sig.ident = proc_macro2::Ident::new(
format!("{}_sync", ast.sig.ident).as_str(),
proc_macro2::Span::call_site(),
);
let mut item = item;
item.extend(std::iter::once::<TokenStream>(
ast.into_token_stream().into(),
));
item
}