web-workers-derive 0.1.0

Derive macro for web-workers MessageSend (zero-copy transferable)
Documentation
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Fields};

#[proc_macro_derive(MessageSend)]
pub fn derive_message_send(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = input.ident;
    let generics = input.generics;
    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

    let expanded = match input.data {
        Data::Struct(data) => match data.fields {
            Fields::Named(fields) => {
                let field_names: Vec<_> = fields.named.iter().map(|f| f.ident.as_ref().unwrap()).collect();
                let field_tys: Vec<_> = fields.named.iter().map(|f| &f.ty).collect();
                quote! {
                    impl #impl_generics ::web_workers::web::message::MessageSend for #name #ty_generics #where_clause {
                        type Send = (<(#(#field_tys,)*) as ::web_workers::web::message::MessageSend>::Send,);
                        fn send<E: ::std::iter::Extend<::wasm_bindgen::JsValue>>(self, transfer: &mut E) -> ::web_workers::web::message::RawMessage<Self::Send> {
                            let #name { #(#field_names,)* } = self;
                            let inner = (#(#field_names,)*);
                            let msg = <(#(#field_tys,)*) as ::web_workers::web::message::MessageSend>::send(inner, transfer);
                            ::web_workers::web::message::RawMessage { serialize: msg.serialize, send: msg.send.map(|s| (s,)) }
                        }
                        fn receive(serialized: Option<::wasm_bindgen::JsValue>, sent: Option<Self::Send>) -> Self {
                            let (inner_send,) = sent.expect("expected Send for struct");
                            let inner = <(#(#field_tys,)*) as ::web_workers::web::message::MessageSend>::receive(serialized, Some(inner_send));
                            let (#(#field_names,)*) = inner;
                            Self { #(#field_names,)* }
                        }
                    }
                }
            }
            Fields::Unnamed(fields) => {
                let field_tys: Vec<_> = fields.unnamed.iter().map(|f| &f.ty).collect();
                let n = field_tys.len();
                let idents: Vec<_> = (0..n).map(|i| quote::format_ident!("__field{}", i)).collect();
                quote! {
                    impl #impl_generics ::web_workers::web::message::MessageSend for #name #ty_generics #where_clause {
                        type Send = (<(#(#field_tys,)*) as ::web_workers::web::message::MessageSend>::Send,);
                        fn send<E: ::std::iter::Extend<::wasm_bindgen::JsValue>>(self, transfer: &mut E) -> ::web_workers::web::message::RawMessage<Self::Send> {
                            let #name(#(#idents,)*) = self;
                            let inner = (#(#idents,)*);
                            let msg = <(#(#field_tys,)*) as ::web_workers::web::message::MessageSend>::send(inner, transfer);
                            ::web_workers::web::message::RawMessage { serialize: msg.serialize, send: msg.send.map(|s| (s,)) }
                        }
                        fn receive(serialized: Option<::wasm_bindgen::JsValue>, sent: Option<Self::Send>) -> Self {
                            let (inner_send,) = sent.expect("expected Send for tuple struct");
                            let inner = <(#(#field_tys,)*) as ::web_workers::web::message::MessageSend>::receive(serialized, Some(inner_send));
                            let (#(#idents,)*) = inner;
                            Self(#(#idents,)*)
                        }
                    }
                }
            }
            Fields::Unit => quote! {
                impl #impl_generics ::web_workers::web::message::MessageSend for #name #ty_generics #where_clause {
                    type Send = ();
                    fn send<E: ::std::iter::Extend<::wasm_bindgen::JsValue>>(self, _: &mut E) -> ::web_workers::web::message::RawMessage<Self::Send> {
                        ::web_workers::web::message::RawMessage { serialize: None, send: None }
                    }
                    fn receive(_serialized: Option<::wasm_bindgen::JsValue>, _sent: Option<Self::Send>) -> Self { Self }
                }
            },
        },
        Data::Enum(_) => {
            return syn::Error::new_spanned(name, "MessageSend derive for enums not yet supported - implement manually via tuple delegation").to_compile_error().into();
        }
        Data::Union(_) => {
            return syn::Error::new_spanned(name, "MessageSend derive does not support unions").to_compile_error().into();
        }
    };

    expanded.into()
}