pipeworks-derive 0.1.0

Derive macros to simplify Pipeworks
Documentation
extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, LitInt, Type};

fn get_buffer_cap(input: &DeriveInput) -> LitInt {
    let default_size = 32;
    let mut buffer_cap_val = None;

    for attr in &input.attrs {
        if attr.path().is_ident("bus_ctl") {
            attr.parse_nested_meta(|meta| {
                if meta.path.is_ident("buffer_cap") {
                    let content = meta.value()?.parse::<LitInt>()?;
                    buffer_cap_val = Some(content);
                    return Ok(());
                }
                Err(meta.error("unsupported `bus_ctl` property"))
            })
            .ok();
        }
    }

    // Return the parsed buffer size or the default
    buffer_cap_val
        .unwrap_or_else(|| LitInt::new(&default_size.to_string(), proc_macro2::Span::call_site()))
}

#[proc_macro_derive(BusLocal, attributes(bus_ctl))]
pub fn bus_local_derive(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
    let buffer_cap = get_buffer_cap(&input);

    let expanded = if input.generics.params.is_empty() {
        quote! {
            impl #impl_generics pipeworks_core::reg::BusType for #name #ty_generics #where_clause {
                fn get_type_reg() -> pipeworks_core::reg::TypeReg {
                    pipeworks_core::reg::TypeReg {
                        type_name: std::any::type_name::<Self>(),
                        type_id: std::any::TypeId::of::<Self>(),
                        buffer_cap: #buffer_cap,
                        bitcode_support: None,
                    }
                }
            }

            pipeworks_core::inventory::submit! {
                use pipeworks_core::reg::BusType;
                pipeworks_core::reg::TypeRegFn(#name #ty_generics ::get_type_reg)
            }
        }
    } else {
        quote! {
            impl #impl_generics pipeworks_core::reg::BusType for #name #ty_generics #where_clause {
                fn get_type_reg() -> pipeworks_core::reg::TypeReg {
                    pipeworks_core::reg::TypeReg {
                        type_name: std::any::type_name::<Self>(),
                        type_id: std::any::TypeId::of::<Self>(),
                        buffer_cap: #buffer_cap,
                        bitcode_support: None,
                    }
                }
            }
        }
    };

    expanded.into()
}

#[proc_macro_derive(BusShared, attributes(bus_ctl))]
pub fn bus_shared_derive(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
    let buffer_cap = get_buffer_cap(&input);

    let expanded = if input.generics.params.is_empty() {
        quote! {
            impl #impl_generics pipeworks_core::reg::BusType for #name #ty_generics #where_clause {
                fn get_type_reg() -> pipeworks_core::reg::TypeReg {
                    use std::{sync::Arc, any::{Any, type_name, TypeId}};

                    fn to_bytes(type_erased: Arc<Box<dyn Any + Send + Sync + 'static>>) -> Vec<u8> {
                        let value = type_erased.downcast_ref::<#name #ty_generics>().unwrap();
                        bitcode::encode(value)
                    }

                    fn from_bytes(
                        bytes: &[u8],
                    ) -> Result<Arc<Box<dyn Any + Send + Sync + 'static>>, bitcode::Error> {
                        let value = bitcode::decode::<#name #ty_generics>(bytes)?;
                        Ok(Arc::new(Box::new(value)))
                    }

                    pipeworks_core::reg::TypeReg {
                        type_name:  type_name::<Self>(),
                        type_id: TypeId::of::<Self>(),
                        buffer_cap: #buffer_cap,
                        bitcode_support: Some((to_bytes, from_bytes)),
                    }
                }
            }

            pipeworks_core::inventory::submit! {
                use pipeworks_core::reg::BusType;
                pipeworks_core::reg::TypeRegFn(#name #ty_generics ::get_type_reg)
            }
        }
    } else {
        quote! {
            impl #impl_generics pipeworks_core::reg::BusType for #name #ty_generics #where_clause {
                fn get_type_reg() -> pipeworks_core::reg::TypeReg {
                    use std::{sync::Arc, any::{Any, type_name, TypeId}};

                    fn to_bytes(type_erased: Arc<Box<dyn Any + Send + Sync + 'static>>) -> Vec<u8> {
                        let value = type_erased.downcast_ref::<#name #ty_generics>().unwrap();
                        bitcode::encode(value)
                    }

                    fn from_bytes(
                        bytes: &[u8],
                    ) -> Result<Arc<Box<dyn Any + Send + Sync + 'static>>, bitcode::Error> {
                        let value = bitcode::decode::<#name #ty_generics>(bytes)?;
                        Ok(Arc::new(Box::new(value)))
                    }

                    pipeworks_core::reg::TypeReg {
                        type_name: type_name::<Self>(),
                        type_id: TypeId::of::<Self>(),
                        buffer_cap: #buffer_cap,
                        bitcode_support: Some((to_bytes, from_bytes)),
                    }
                }
            }
        }
    };

    expanded.into()
}

#[proc_macro]
pub fn register_generic(input: TokenStream) -> TokenStream {
    let ty = parse_macro_input!(input as Type);

    let expanded = quote! {
        use pipeworks_core::reg::BusType;
        pipeworks_core::inventory::submit! {
            pipeworks_core::reg::TypeRegFn(#ty ::get_type_reg)
        }
    };

    expanded.into()
}