use quote::{format_ident, quote, quote_spanned};
use syn::{DataEnum, DataStruct, Fields, Ident, Index};
use crate::attr::parse_field_attributes;
pub fn to_bytes_struct(_struct_name: &Ident, data: DataStruct) -> proc_macro2::TokenStream {
let mut fields = vec![];
for (idx, field) in data.fields.iter().enumerate() {
let attributes = parse_field_attributes!(&field.attrs);
if !attributes.is_skip() {
let variant_idx = Index::from(idx);
let field_ref = field
.ident
.as_ref()
.map_or_else(|| quote!(&self.#variant_idx), |ident| quote!(&self.#ident));
let q = if let Some(map_func) = attributes.map_to_bytes() {
quote! {
{
let value_out = #map_func(#field_ref).map_err(|err| nuts_bytes::Error::Custom(err.into()))?;
ToBytes::to_bytes(&value_out, target)?
}
}
} else {
quote! {
ToBytes::to_bytes(#field_ref, target)?
}
};
fields.push(q);
}
}
quote! {
let mut n = 0;
#(n += #fields;)*
Ok(n)
}
}
pub fn to_bytes_enum(enum_name: &Ident, data: DataEnum) -> proc_macro2::TokenStream {
if data.variants.is_empty() {
let span = enum_name.span();
return quote_spanned! {
span => compile_error!("zero-variant enums cannot be instantiated")
};
}
let variants = data
.variants
.iter()
.take(u32::MAX as usize)
.enumerate()
.map(|(idx, variant)| {
let variant_idx = Index::from(idx);
let variant_name = &variant.ident;
let left_arm_args = variant.fields.iter().enumerate().map(|(idx, field)| {
let ident = field.ident.as_ref().map_or_else(
|| format_ident!("f{}", Index::from(idx)),
|ident| ident.clone(),
);
quote!(#ident)
});
let left_arm = match &variant.fields {
Fields::Named(_) => {
quote!( #enum_name::#variant_name { #(#left_arm_args),* } )
}
Fields::Unnamed(_) => {
quote!( #enum_name::#variant_name ( #(#left_arm_args),* ) )
}
Fields::Unit => quote!( #enum_name::#variant_name ),
};
let right_arm_fields = variant.fields.iter().enumerate().map(|(idx, field)| {
let ident = field.ident.as_ref().map_or_else(
|| format_ident!("f{}", Index::from(idx)),
|ident| ident.clone(),
);
quote!(ToBytes::to_bytes(#ident, target)?)
});
let right_arm = quote! {
{
let mut m = 0;
m += ToBytes::to_bytes(&(#variant_idx as u32), target)?;
#(m += #right_arm_fields;)*
m
}
};
quote! {
#left_arm => #right_arm
}
});
quote! {
let n = match self {
#(#variants,)*
};
Ok(n)
}
}
pub fn to_bytes_union(union_name: &Ident) -> proc_macro2::TokenStream {
let span = union_name.span();
quote_spanned! {
span => compile_error!("the union type is currently not supported")
}
}