cser-derive 0.1.0

Procedural macros for cser
Documentation
use alloc::vec::Vec;
use proc_macro2::TokenStream;
use quote::quote;

pub fn impl_encodable(ast: &syn::DeriveInput) -> TokenStream {
    let body = if let syn::Data::Struct(s) = &ast.data {
        s
    } else {
        panic!("#[derive(Encodable)] is only defined for structs.");
    };

    let stmts: Vec<_> = body
        .fields
        .iter()
        .enumerate()
        .map(|(index, field)| {
            let ident = field_ident(index, field);

            let id = quote! { self.#ident };

            quote! { cser::Encodable::encode(&#id, out); }
        })
        .collect();
    let name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

    let impl_block = quote! {
        impl #impl_generics cser::Encodable for #name #ty_generics #where_clause {
            fn encode(&self, out: &mut cser::Writer) {
                #(#stmts)*
            }
        }
    };

    quote! {
        const _: () = {
            extern crate cser;
            #impl_block
        };
    }
}

pub fn impl_encodable_wrapper(ast: &syn::DeriveInput) -> TokenStream {
    let body = if let syn::Data::Struct(s) = &ast.data {
        s
    } else {
        panic!("#[derive(EncodableWrapper)] is only defined for structs.");
    };

    let ident = {
        let fields: Vec<_> = body.fields.iter().collect();
        if fields.len() == 1 {
            let field = fields.first().expect("fields.len() == 1; qed");
            field_ident(0, field)
        } else {
            panic!("#[derive(EncodableWrapper)] is only defined for structs with one field.")
        }
    };

    let name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

    let impl_block = quote! {
        impl #impl_generics cser::Encodable for #name #ty_generics #where_clause {
            fn encode(&self, out: &mut cser::Writer) {
                cser::Encodable::encode(&self.#ident, out)
            }
        }
    };

    quote! {
        const _: () = {
            extern crate cser;
            #impl_block
        };
    }
}

fn field_ident(index: usize, field: &syn::Field) -> TokenStream {
    if let Some(ident) = &field.ident {
        quote! { #ident }
    } else {
        let index = syn::Index::from(index);
        quote! { #index }
    }
}