rustbus_derive 0.1.0

derive proc-macros for the rustbus crate
Documentation
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};

#[proc_macro_derive(Marshal)]
pub fn derive_marshal(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let ast: syn::DeriveInput = syn::parse(input).unwrap();

    match ast.data {
        syn::Data::Struct(data) => {
            make_struct_marshal_impl(&ast.ident, &ast.generics, &data.fields).into()
        }
        _ => unimplemented!("Nothing but structs can be derived on right now"),
    }
}
#[proc_macro_derive(Unmarshal)]
pub fn derive_unmarshal(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let ast: syn::DeriveInput = syn::parse(input).unwrap();

    match ast.data {
        syn::Data::Struct(data) => {
            make_struct_unmarshal_impl(&ast.ident, &ast.generics, &data.fields).into()
        }
        _ => unimplemented!("Nothing but structs can be derived on right now"),
    }
}
#[proc_macro_derive(Signature)]
pub fn derive_signature(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let ast: syn::DeriveInput = syn::parse(input).unwrap();

    match ast.data {
        syn::Data::Struct(data) => {
            make_struct_signature_impl(&ast.ident, &ast.generics, &data.fields).into()
        }
        _ => unimplemented!("Nothing but structs can be derived on right now"),
    }
}

fn make_struct_marshal_impl(
    ident: &syn::Ident,
    generics: &syn::Generics,
    fields: &syn::Fields,
) -> TokenStream {
    let (impl_gen, typ_gen, clause_gen) = generics.split_for_impl();
    let marshal = struct_field_marshal(fields);

    quote! {
        impl #impl_gen ::rustbus::Marshal for #ident #typ_gen #clause_gen {
            #[inline]
            fn marshal(&self, ctx: &mut ::rustbus::wire::marshal::MarshalContext<'_,'_>) -> Result<(), ::rustbus::Error> {
                #marshal
            }
        }
    }
}
fn make_struct_unmarshal_impl(
    ident: &syn::Ident,
    generics: &syn::Generics,
    fields: &syn::Fields,
) -> TokenStream {
    let marshal = struct_field_unmarshal(fields);

    let mut rdef = syn::LifetimeDef {
        attrs: Vec::new(),
        lifetime: syn::Lifetime::new("'__internal_r", proc_macro2::Span::call_site()),
        colon_token: None,
        bounds: syn::punctuated::Punctuated::new(),
    };
    let mut bufdef = syn::LifetimeDef {
        attrs: Vec::new(),
        lifetime: syn::Lifetime::new("'__internal_buf", proc_macro2::Span::call_site()),
        colon_token: None,
        bounds: syn::punctuated::Punctuated::new(),
    };
    bufdef.bounds.push(rdef.lifetime.clone());

    let fdsdef = syn::LifetimeDef {
        attrs: Vec::new(),
        lifetime: syn::Lifetime::new("'__internal_fds", proc_macro2::Span::call_site()),
        colon_token: None,
        bounds: syn::punctuated::Punctuated::new(),
    };

    let mut new_generics = generics.clone();
    new_generics
        .lifetimes()
        .for_each(|lt| rdef.bounds.push(lt.lifetime.clone()));

    let typ_generics = new_generics.clone();
    let (_, typ_gen, _) = typ_generics.split_for_impl();

    new_generics.params = vec![
        syn::GenericParam::Lifetime(rdef),
        syn::GenericParam::Lifetime(bufdef),
        syn::GenericParam::Lifetime(fdsdef),
    ]
    .into_iter()
    .chain(new_generics.params)
    .collect();

    let (impl_gen, _, clause_gen) = new_generics.split_for_impl();

    quote! {
        impl #impl_gen ::rustbus::Unmarshal<'__internal_r, '__internal_buf, '__internal_fds> for #ident #typ_gen #clause_gen {
            #[inline]
            fn unmarshal(ctx: &mut ::rustbus::wire::unmarshal::UnmarshalContext<'__internal_fds,'__internal_buf>) -> Result<(usize,Self), ::rustbus::wire::unmarshal::Error> {
                #marshal
            }
        }
    }
}
fn make_struct_signature_impl(
    ident: &syn::Ident,
    generics: &syn::Generics,
    fields: &syn::Fields,
) -> TokenStream {
    let (impl_gen, typ_gen, clause_gen) = generics.split_for_impl();
    let signature = struct_field_sigs(fields);

    quote! {
        impl #impl_gen ::rustbus::Signature for #ident #typ_gen #clause_gen {
            #[inline]
            fn signature() -> ::rustbus::signature::Type {
                #signature
            }
            fn alignment() -> usize {
                8
            }
        }
    }
}

fn struct_field_marshal(fields: &syn::Fields) -> TokenStream {
    let field_names = fields
        .iter()
        .map(|field| field.ident.as_ref().unwrap().to_token_stream());

    quote! {
            ctx.align_to(8);
            #(
                self.#field_names.marshal(ctx)?;
            )*
            Ok(())
    }
}
fn struct_field_unmarshal(fields: &syn::Fields) -> TokenStream {
    let field_names = fields
        .iter()
        .map(|field| field.ident.as_ref().unwrap().to_token_stream());

    let field_types = fields.iter().map(|field| field.ty.to_token_stream());

    quote! {
            let start_offset = ctx.offset;
            ctx.align_to(8)?;

            let this = Self{
                #(
                    #field_names: <#field_types as ::rustbus::Unmarshal>::unmarshal(ctx)?.1,
                )*
            };
            let total_bytes = ctx.offset - start_offset;
            Ok((total_bytes, this))
    }
}
fn struct_field_sigs(fields: &syn::Fields) -> TokenStream {
    let field_types = fields.iter().map(|field| field.ty.to_token_stream());

    quote! {
            let mut sigs = vec![];

            #(
                sigs.push(<#field_types as rustbus::Signature>::signature());
            )*

            ::rustbus::signature::Type::Container(::rustbus::signature::Container::Struct(sigs))
    }
}