use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemTrait, Signature, TraitItem};
#[proc_macro_attribute]
pub fn arc_handle(_args: TokenStream, input: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(input as ItemTrait);
let original_trait_name = &input.ident;
let impl_trait_name = syn::Ident::new(
&format!("{}Impl", original_trait_name),
original_trait_name.span(),
);
let handle_name = original_trait_name.clone();
input.ident = impl_trait_name.clone();
let mut async_methods = Vec::new();
let mut sync_methods = Vec::new();
for item in &input.items {
if let TraitItem::Fn(method) = item {
if is_async_method(&method.sig) {
async_methods.push(method);
} else {
sync_methods.push(method);
}
}
}
let async_impl_methods = async_methods.iter().map(|method| {
let method_name = &method.sig.ident;
let inputs = &method.sig.inputs;
let output = &method.sig.output;
let param_names = method
.sig
.inputs
.iter()
.skip(1)
.map(|arg| {
if let syn::FnArg::Typed(pat_type) = arg {
if let syn::Pat::Ident(ident) = &*pat_type.pat {
&ident.ident
} else {
panic!("Unsupported parameter pattern");
}
} else {
panic!("Unsupported parameter type");
}
})
.collect::<Vec<_>>();
quote! {
pub async fn #method_name(#inputs) #output {
self.inner.#method_name(#(#param_names),*).await
}
}
});
let sync_impl_methods = sync_methods.iter().map(|method| {
let method_name = &method.sig.ident;
let inputs = &method.sig.inputs;
let output = &method.sig.output;
let param_names = method
.sig
.inputs
.iter()
.skip(1)
.map(|arg| {
if let syn::FnArg::Typed(pat_type) = arg {
if let syn::Pat::Ident(ident) = &*pat_type.pat {
&ident.ident
} else {
panic!("Unsupported parameter pattern");
}
} else {
panic!("Unsupported parameter type");
}
})
.collect::<Vec<_>>();
quote! {
pub fn #method_name(#inputs) #output {
self.inner.#method_name(#(#param_names),*)
}
}
});
let expanded = quote! {
#input
#[derive(Clone)]
pub struct #handle_name {
inner: std::sync::Arc<dyn #impl_trait_name + Send + Sync>,
}
unsafe impl Send for #handle_name {}
unsafe impl Sync for #handle_name {}
impl #handle_name {
pub fn new(inner: impl #impl_trait_name + Send + Sync + 'static) -> Self {
Self {
inner: std::sync::Arc::new(inner),
}
}
pub fn from_boxed(inner: Box<dyn #impl_trait_name + Send + Sync>) -> Self {
Self {
inner: std::sync::Arc::from(inner),
}
}
#(#async_impl_methods)*
#(#sync_impl_methods)*
}
};
TokenStream::from(expanded)
}
fn is_async_method(sig: &Signature) -> bool {
sig.asyncness.is_some()
}