rustbus_derive 0.5.0

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

pub 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::wire::errors::MarshalError> {
                #marshal
            }
        }
    }
}
pub fn make_struct_unmarshal_impl(
    ident: &syn::Ident,
    generics: &syn::Generics,
    fields: &syn::Fields,
) -> TokenStream {
    let marshal = struct_field_unmarshal(fields);

    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(),
    };

    let mut new_generics = generics.clone();
    for lt in new_generics.lifetimes_mut() {
        bufdef.bounds.push(lt.lifetime.clone());
        lt.bounds.push(bufdef.lifetime.clone());
    }

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

    new_generics
        .params
        .insert(0, syn::GenericParam::Lifetime(bufdef));

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

    quote! {
        impl #impl_gen ::rustbus::Unmarshal<'__internal_buf, '_> for #ident #typ_gen #clause_gen {
            #[inline]
            fn unmarshal(ctx: &mut ::rustbus::wire::unmarshal::UnmarshalContext<'_,'__internal_buf>) -> Result<(usize,Self), ::rustbus::wire::errors::UnmarshalError> {
                #marshal
            }
        }
    }
}
pub 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);
    let has_sig = struct_field_has_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 has_sig(sig: &str) -> bool {
                #has_sig
            }
        }
    }
}

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())
        .collect::<Vec<_>>();
    if field_types.is_empty() {
        panic!("Signature can not be derived for empty structs!")
    }

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

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

            ::rustbus::signature::Type::Container(::rustbus::signature::Container::Struct(
                ::rustbus::signature::StructTypes::new(sigs).unwrap()
            ))
    }
}
fn struct_field_has_sigs(fields: &syn::Fields) -> TokenStream {
    let field_types = fields
        .iter()
        .map(|field| field.ty.to_token_stream())
        .collect::<Vec<_>>();
    if field_types.is_empty() {
        panic!("Signature can not be derived for empty structs!")
    }

    quote! {
        if sig.starts_with('(') {
            let mut iter = ::rustbus::signature::SignatureIter::new(&sig[1..sig.len() - 1]);
            let mut accu = true;

            #(
                accu &= <#field_types as rustbus::Signature>::has_sig(iter.next().unwrap());
            )*

            accu
        } else {
            false
        }
    }
}