use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, Fields};
#[proc_macro_derive(StructPacker)]
pub fn struct_packer_macro(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = input.ident;
let fields = match input.data {
syn::Data::Struct(s) => match s.fields {
Fields::Named(fields) => fields.named,
Fields::Unnamed(fields) => fields.unnamed,
Fields::Unit => panic!("Unit structs are not supported"),
},
_ => panic!("StructPacker can only be derived for structs"),
};
let size_fields = fields.iter().map(|f| {
let field_name = &f.ident;
quote! {
_size += self.#field_name.size();
}
});
let pack_fields = fields.iter().map(|f| {
let field_name = &f.ident;
quote! {
self.#field_name.pack(enc);
}
});
let unpack_fields = fields.iter().map(|f| {
let field_name = &f.ident;
quote! {
dec.unpack(&mut self.#field_name);
}
});
let expanded = quote! {
impl Packer for #name {
fn size(&self) -> usize {
let mut _size: usize = 0;
#(#size_fields)*
_size
}
fn pack(&self, enc: &mut Encoder) -> usize {
let pos = enc.get_size();
#(#pack_fields)*
enc.get_size() - pos
}
fn unpack(&mut self, data: &[u8]) -> usize {
let mut dec = Decoder::new(data);
#(#unpack_fields)*
dec.get_pos()
}
}
};
TokenStream::from(expanded)
}
#[proc_macro_derive(EnumPacker)]
pub fn enum_packer_macro(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let gen = match input.data {
syn::Data::Enum(data_enum) => {
let size_variants = data_enum.variants.iter().enumerate().map(|(_i, variant)| {
let variant_ident = &variant.ident;
match &variant.fields {
Fields::Unnamed(fields) => {
if fields.unnamed.len() != 1 {
panic!("Each variant must have exactly one field implementing the Packer trait.");
}
quote! {
#name::#variant_ident(x) => { _size = 1 + x.size(); }
}
},
_ => panic!("Only unnamed fields are supported"),
}
});
let pack_variants = data_enum.variants.iter().enumerate().map(|(i, variant)| {
let variant_ident = &variant.ident;
quote! {
#name::#variant_ident(x) => {
let mut i: u8 = #i as u8;
i.pack(enc);
x.pack(enc);
}
}
});
let unpack_variants = data_enum.variants.iter().enumerate().map(|(i, variant)| {
let variant_ident = &variant.ident;
let variant_type = &variant.fields;
let variant_default = match variant_type {
Fields::Unnamed(fields) => {
let ty = &fields.unnamed.first().unwrap().ty;
quote! {
let mut v: #ty = Default::default();
dec.unpack(&mut v);
*self = #name::#variant_ident(v);
}
},
_ => panic!("Only unnamed fields are supported"),
};
quote! {
#i => {
#variant_default
}
}
});
let default_variant = &data_enum.variants[0];
let default_variant_ident = &default_variant.ident;
quote! {
impl Default for #name {
#[doc = r""]
#[inline]
fn default() -> Self {
#name::#default_variant_ident(Default::default())
}
}
impl ::antelope::serializer::Packer for #name {
fn size(&self) -> usize {
let mut _size: usize = 0;
match self {
#( #size_variants ),*
}
_size
}
fn pack(&self, enc: &mut ::antelope::chain::Encoder) -> usize {
let pos = enc.get_size();
match self {
#( #pack_variants ),*
}
enc.get_size() - pos
}
fn unpack<'a>(&mut self, data: &'a [u8]) -> usize {
let mut dec = ::antelope::chain::Decoder::new(data);
let mut variant_type_index: u8 = 0;
dec.unpack(&mut variant_type_index);
let variant_type_index = variant_type_index as usize;
match variant_type_index {
#( #unpack_variants ),*
_ => { panic!("bad variant index!"); }
}
dec.get_pos()
}
}
}
},
_ => panic!("EnumPacker can only be derived for enums"),
};
TokenStream::from(gen)
}