arc-handle 1.0.0

Proc macro for generating Arc-based handle wrappers for traits
Documentation
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemTrait, Signature, TraitItem};

/// Attribute macro that generates an Arc-based handle wrapper for a trait.
///
/// This macro renames the original trait to `TraitImpl` and creates a handle struct
/// with the original trait name that wraps the trait in an Arc<dyn TraitImpl + Send + Sync>
/// and provides methods that delegate to the inner trait implementation.
///
/// # Example
///
/// ```rust
/// use arc_handle::arc_handle;
/// use async_trait::async_trait;
///
/// #[arc_handle]
/// #[async_trait]
/// pub trait MyTrait {
///     async fn do_something(&self, value: i32) -> String;
///     fn get_name(&self) -> &str;
/// }
/// ```
///
/// This will generate a `MyTraitImpl` trait and a `MyTrait` handle struct.
#[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();

    // Rename the original trait to TraitImpl
    input.ident = impl_trait_name.clone();

    // Extract methods from the trait
    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);
            }
        }
    }

    // Generate async method implementations
    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;

        // Extract parameter names (skip &self)
        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
            }
        }
    });

    // Generate sync method implementations
    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;

        // Extract parameter names (skip &self)
        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! {
        // Include the renamed trait (TraitImpl)
        #input

        /// Handle wrapper for the trait that provides Arc-based sharing
        #[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 {
            /// Create a new handle from a trait implementation
            pub fn new(inner: impl #impl_trait_name + Send + Sync + 'static) -> Self {
                Self {
                    inner: std::sync::Arc::new(inner),
                }
            }

            /// Create a new handle from a boxed trait object
            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()
}