diff --git a/src/descriptors/struct_desc.rs b/src/descriptors/struct_desc.rs index dbeac4d..f0984ff 100644 --- a/src/descriptors/struct_desc.rs +++ b/src/descriptors/struct_desc.rs @@ -90,7 +90,7 @@ impl Descriptor for StructDescriptor { } impl StructDescriptor { - pub fn new(input: &syn::DataStruct, ident: syn::Ident) -> Self { + pub fn new(input: &syn::DataStruct, ident: syn::Ident, packed_struct: bool) -> Self { let mut descriptor = StructDescriptor { ty: ident, version: 1, // struct start at version 1. @@ -98,17 +98,18 @@ impl StructDescriptor { }; // Fills self.fields. - descriptor.parse_struct_fields(&input.fields); + descriptor.parse_struct_fields(&input.fields, packed_struct); descriptor.version = compute_version(&descriptor.fields); descriptor } - fn parse_struct_fields(&mut self, fields: &syn::Fields) { + fn parse_struct_fields(&mut self, fields: &syn::Fields, packed_struct: bool) { match fields { syn::Fields::Named(ref named_fields) => { let pairs = named_fields.named.pairs(); for field in pairs.into_iter() { - self.fields.push(StructField::new(self.version, field)); + self.fields + .push(StructField::new(self.version, field, packed_struct)); } } _ => panic!("Only named fields are supported."), diff --git a/src/fields/struct_field.rs b/src/fields/struct_field.rs index 982a50e..4b03b74 100644 --- a/src/fields/struct_field.rs +++ b/src/fields/struct_field.rs @@ -14,6 +14,7 @@ pub(crate) struct StructField { start_version: u16, end_version: u16, attrs: HashMap<String, syn::Lit>, + packed_struct: bool, } impl Exists for StructField { @@ -36,6 +37,7 @@ impl StructField { pub fn new( base_version: u16, ast_field: syn::punctuated::Pair<&syn::Field, &syn::token::Comma>, + packed_struct: bool, ) -> Self { let attrs = parse_field_attributes(&ast_field.value().attrs); @@ -45,6 +47,7 @@ impl StructField { start_version: get_start_version(&attrs).unwrap_or(base_version), end_version: get_end_version(&attrs).unwrap_or_default(), attrs, + packed_struct, } } @@ -87,9 +90,18 @@ impl StructField { Versionize::serialize(element, writer, version_map, app_version)?; } }, - syn::Type::Path(_) => quote! { - Versionize::serialize(©_of_self.#field_ident, writer, version_map, app_version)?; - }, + syn::Type::Path(_) => { + if self.packed_struct { + quote! { + let copy = copy_of_self.#field_ident; + Versionize::serialize(©, writer, version_map, app_version)?; + } + } else { + quote! { + Versionize::serialize(©_of_self.#field_ident, writer, version_map, app_version)?; + } + } + } syn::Type::Reference(_) => quote! { copy_of_self.#field_ident.serialize(writer, version_map, app_version)?; }, diff --git a/src/lib.rs b/src/lib.rs index 3bcf6a2..3c2303a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,8 +33,9 @@ mod helpers; use common::Descriptor; use descriptors::{enum_desc::EnumDescriptor, struct_desc::StructDescriptor}; use proc_macro::TokenStream; +use proc_macro2::TokenTree; use quote::quote; -use syn::{parse_macro_input, DeriveInput}; +use syn::{parse::ParseStream, parse_macro_input, DeriveInput}; pub(crate) const ATTRIBUTE_NAME: &str = "version"; @@ -215,10 +216,26 @@ pub fn impl_versionize(input: TokenStream) -> proc_macro::TokenStream { let ident = input.ident.clone(); let generics = input.generics.clone(); - let descriptor: Box<dyn Descriptor> = match &input.data { - syn::Data::Struct(data_struct) => { - Box::new(StructDescriptor::new(&data_struct, ident.clone())) + let mut is_packed = false; + for attr in &input.attrs { + if attr.path.is_ident("repr") { + let _ = attr.parse_args_with(|input: ParseStream| { + while let Some(token) = input.parse()? { + if let TokenTree::Ident(ident) = token { + is_packed |= ident == "packed"; + } + } + Ok(()) + }); } + } + + let descriptor: Box<dyn Descriptor> = match &input.data { + syn::Data::Struct(data_struct) => Box::new(StructDescriptor::new( + &data_struct, + ident.clone(), + is_packed, + )), syn::Data::Enum(data_enum) => Box::new(EnumDescriptor::new(&data_enum, ident.clone())), syn::Data::Union(_) => { return (quote! {