also_sync_macros 0.1.0

Macros to automatically wrap async functions as sync
Documentation
#![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),
        },
        // skip self, as self is already part of the "function name"
        FnArg::Receiver(_self) => None,
    }
}

#[cfg(not(feature = "tokio"))]
#[proc_macro_attribute]
pub fn also_sync_tokio(_attr: TokenStream, item: TokenStream) -> TokenStream {
    item
}

/// The main macro converting a `async fn x` into a sync `fn x_sync()`.
/// It is currently highly experimental, so test it before using it.
#[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.");
    // println!("{:#?}", ast);

    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
    );
    // println!("New body: {}", new_body);
    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(),
    );

    // println!("START");
    // println!("{:#?}", ast);
    // println!("END");
    let mut item = item;
    item.extend(std::iter::once::<TokenStream>(
        ast.into_token_stream().into(),
    ));
    item
}