1
0
Fork 0
mirror of https://codeberg.org/icewind/bitbuffer.git synced 2026-06-04 09:04:05 +02:00

rewrite derive macro

This commit is contained in:
Robin Appelman 2023-09-12 22:30:45 +02:00
commit 3852f09dd5
21 changed files with 1548 additions and 840 deletions

View file

@ -0,0 +1,63 @@
use crate::params::{EnumParam, VariantBody, VariantBodyType};
use crate::write::field::write_enum_variant;
use proc_macro2::TokenStream;
use quote::quote_spanned;
use syn::Path;
pub fn derive_encode_enum(params: &EnumParam) -> TokenStream {
let discriminant_bits = params.discriminant_bits;
let repr = params.discriminant_repr();
let ident = params.ident.clone();
let span = params.span();
let discriminant_value = params
.variants
.iter()
.zip(params.write_discriminant_tokens())
.map(|(variant, discriminant_token)| {
let span = variant.span();
let variant_name = &variant.variant_name;
match variant.body.body_type() {
VariantBodyType::Unit => quote_spanned! {span =>
#ident::#variant_name => #discriminant_token
},
VariantBodyType::Unnamed => {
quote_spanned! { span =>
#ident::#variant_name(_) => #discriminant_token
}
}
VariantBodyType::Named => {
quote_spanned! { span =>
#ident::#variant_name{..} => #discriminant_token
}
}
}
});
let write_inner = params.variants.iter().map(|variant| {
let span = variant.span();
let mut path = Path::from(ident.clone());
path.segments.push(variant.variant_name.clone().into());
match &variant.body {
VariantBody::Unit => {
quote_spanned! {span =>
#path => {},
}
}
VariantBody::Fields(fields) => write_enum_variant(path, &fields, span),
}
});
quote_spanned! {span=>
let discriminant:#repr = match &self {
#(#discriminant_value),*
};
#[allow(clippy::unnecessary_cast)]
__stream.write_int(discriminant, #discriminant_bits as usize)?;
match &self {
#(#write_inner)*
}
Ok(())
}
}

View file

@ -0,0 +1,81 @@
use crate::params::FieldParam;
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote_spanned;
use syn::Path;
pub fn write_struct(fields: &[FieldParam], span: Span) -> TokenStream {
let expand = fields
.iter()
.enumerate()
.zip(names(fields))
.map(|((index, field), name)| {
let member = field.member(index as u32);
let size_field = match (field.is_int(), field.field_name.as_ref()) {
(true, Some(name)) => Some(quote_spanned! { field.span() =>
#[allow(unused_variables)]
let #name = self.#member;
}),
_ => None,
};
quote_spanned! { field.span() =>
#size_field
#[allow(unused_variables)]
let #name = &self.#member;
}
});
let writes = writes(fields);
quote_spanned! {span=>
#(#expand)*
#(#writes)*
}
}
fn names(fields: &[FieldParam]) -> impl Iterator<Item = Ident> + '_ {
fields
.iter()
.enumerate()
.map(|(index, field)| Ident::new(&format!("__field_{}", index), field.span()))
}
fn writes(fields: &[FieldParam]) -> impl Iterator<Item = TokenStream> + '_ {
let names = names(fields);
fields.iter().zip(names).map(|(field, name)| {
let align = &field.align.write();
let span = field.span();
match &field.size {
Some(size) => {
quote_spanned! { span =>
{
#align
let _size: usize = #size;
__stream.write_sized(#name, _size)?;
}
}
}
None => {
quote_spanned! { span =>
{
#align
__stream.write(#name)?;
}
}
}
}
})
}
pub fn write_enum_variant(variant: Path, fields: &[FieldParam], span: Span) -> TokenStream {
let names = names(fields);
let named = fields.iter().any(|f| f.field_name.is_some());
let writes = writes(fields);
if named {
quote_spanned!(span => #variant{#(#names,)*} => {
#(#writes;)*
})
} else {
quote_spanned!(span => #variant(#(#names,)*) => {
#(#writes;)*
})
}
}

View file

@ -0,0 +1,68 @@
pub mod r#enum;
pub mod field;
pub mod r#struct;
use self::r#enum::derive_encode_enum;
use self::r#struct::derive_encode_struct;
use crate::params::{InputInnerParams, InputParams};
use crate::Derivable;
use proc_macro2::TokenStream;
use quote::quote;
use syn::Result;
fn encode_impl(params: &InputParams) -> Result<TokenStream> {
Ok(match &params.inner {
InputInnerParams::Struct(inner) => derive_encode_struct(inner),
InputInnerParams::Enum(inner) => derive_encode_enum(inner),
})
}
pub struct Write;
impl Derivable for Write {
type Params = InputParams;
fn derive(params: Self::Params) -> Result<TokenStream> {
let (impl_generics, ty_generics, where_clause) = params.generics_for_impl();
let encode = encode_impl(&params)?;
let endianness = params.endianness();
let name = params.ident.clone();
let align = params.align.write();
Ok(quote! {
impl #impl_generics ::bitbuffer::BitWrite<#endianness> for #name #ty_generics #where_clause {
#[allow(unused_braces)]
fn write(&self, __stream: &mut ::bitbuffer::BitWriteStream<#endianness>) -> ::bitbuffer::Result<()> {
#align
#encode
}
}
})
}
}
pub struct WriteSized;
impl Derivable for WriteSized {
type Params = InputParams;
fn derive(params: Self::Params) -> Result<TokenStream> {
let (impl_generics, ty_generics, where_clause) = params.generics_for_impl();
let encode = encode_impl(&params)?;
let endianness = params.endianness();
let name = params.ident.clone();
let align = params.align.write();
Ok(quote! {
impl #impl_generics ::bitbuffer::BitWriteSized<#endianness> for #name #ty_generics #where_clause {
#[allow(unused_braces)]
fn write_sized(&self, __stream: &mut ::bitbuffer::BitWriteStream<#endianness>, input_size: usize) -> ::bitbuffer::Result<()> {
#align
#encode
}
}
})
}
}

View file

@ -0,0 +1,13 @@
use crate::params::StructParam;
use crate::write::field::write_struct;
use proc_macro2::TokenStream;
use quote::quote;
pub fn derive_encode_struct(params: &StructParam) -> TokenStream {
let body = write_struct(&params.fields, params.span());
quote!(
#body
Ok(())
)
}