use proc_macro::TokenStream;
use quote::quote;
use syn::ext::IdentExt;
use syn::{parse_quote, Ident, ImplItemMethod, ReturnType, AttributeArgs};
use darling::FromMeta;
#[derive(Debug, FromMeta)]
struct StackfutureArgs {
#[darling(default)]
size: usize,
}
#[allow(unused)]
fn snake_to_camel(ident_str: &str) -> String {
let mut camel_ty = String::with_capacity(ident_str.len());
let mut last_char_was_underscore = true;
for c in ident_str.chars() {
match c {
'_' => last_char_was_underscore = true,
c if last_char_was_underscore => {
camel_ty.extend(c.to_uppercase());
last_char_was_underscore = false;
}
c => camel_ty.extend(c.to_lowercase()),
}
}
camel_ty.shrink_to_fit();
camel_ty
}
#[allow(unused)]
fn associated_type_for_rpc(method: &ImplItemMethod) -> String {
snake_to_camel(&method.sig.ident.unraw().to_string()) + "Fut"
}
#[cfg(feature = "stackfuture")]
#[proc_macro_attribute]
pub fn tarpc_stackfuture(attr: TokenStream, input: TokenStream) -> TokenStream {
let attr_args = syn::parse_macro_input!(attr as AttributeArgs);
let mut method = syn::parse_macro_input!(input as ImplItemMethod);
let StackfutureArgs { size } = match StackfutureArgs::from_list(&attr_args) {
Ok(v) => v,
Err(e) => { return TokenStream::from(e.write_errors()); }
};
method.sig.asyncness = None;
let ret = match &method.sig.output {
ReturnType::Default => quote!(()),
ReturnType::Type(_, ret) => quote!(#ret),
};
let fut_name = associated_type_for_rpc(&method);
let fut_name_ident = Ident::new(&fut_name, method.sig.ident.span());
method.sig.output = parse_quote! {
-> ::stackfuture::StackFuture<'static, #ret, #size>
};
let block = method.block.clone();
method.block = parse_quote! [{
::stackfuture::StackFuture::from(async move
#block
)
}];
TokenStream::from(quote! {
type #fut_name_ident = ::stackfuture::StackFuture<'static, #ret, #size>;
#method
})
}