From cacfdc3a8bd95a1239bc64b43faf447244eca206 Mon Sep 17 00:00:00 2001 From: alvardes Date: Fri, 1 May 2020 15:32:54 +0200 Subject: [PATCH] Add support for packed structs. --- serde_derive/Cargo.toml | 2 +- serde_derive/src/internals/attr.rs | 25 +++++++++++++++++++++++++ serde_derive/src/ser.rs | 20 ++++++++++++++++++-- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/serde_derive/Cargo.toml b/serde_derive/Cargo.toml index 29df5df080..25a5cd4195 100644 --- a/serde_derive/Cargo.toml +++ b/serde_derive/Cargo.toml @@ -26,7 +26,7 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" quote = "1.0" -syn = { version = "1.0", features = ["visit"] } +syn = { version = "1.0", features = ["visit", "extra-traits"] } [dev-dependencies] serde = { version = "1.0", path = "../serde" } diff --git a/serde_derive/src/internals/attr.rs b/serde_derive/src/internals/attr.rs index f80411d806..ee0c3e5b72 100644 --- a/serde_derive/src/internals/attr.rs +++ b/serde_derive/src/internals/attr.rs @@ -222,6 +222,7 @@ pub struct Container { identifier: Identifier, has_flatten: bool, serde_path: Option, + is_packed: bool, } /// Styles of representing an enum. @@ -592,6 +593,25 @@ impl Container { } } + use proc_macro2::Delimiter; + let is_packed = item.attrs.iter().any(|attr| { + attr.style == syn::AttrStyle::Outer + && attr + .path + .get_ident() + .map_or(false, |ident| *ident == "repr") + && syn::parse2::(attr.tokens.clone()) + .ok() + .filter(|g| g.delimiter() == Delimiter::Parenthesis) + .map(|g| g.stream().to_string()) + .map_or(false, |repr| { + let repr = repr.trim(); + repr == "packed" + || repr.starts_with("packed(") + || repr.starts_with("packed ") + }) + }); + Container { name: Name::from_attrs(unraw(&item.ident), ser_name, de_name, None), transparent: transparent.get(), @@ -611,6 +631,7 @@ impl Container { identifier: decide_identifier(cx, item, field_identifier, variant_identifier), has_flatten: false, serde_path: serde_path.get(), + is_packed, } } @@ -662,6 +683,10 @@ impl Container { self.remote.as_ref() } + pub fn is_packed(&self) -> bool { + self.is_packed + } + pub fn identifier(&self) -> Identifier { self.identifier } diff --git a/serde_derive/src/ser.rs b/serde_derive/src/ser.rs index 37b9a8af77..6163026b67 100644 --- a/serde_derive/src/ser.rs +++ b/serde_derive/src/ser.rs @@ -87,6 +87,9 @@ struct Parameters { /// Type has a `serde(remote = "...")` attribute. is_remote: bool, + + /// Type has a repr(packed) attribute. + is_packed: bool, } impl Parameters { @@ -103,6 +106,8 @@ impl Parameters { None => cont.ident.clone().into(), }; + let is_packed = cont.attrs.is_packed(); + let generics = build_generics(cont); Parameters { @@ -110,6 +115,7 @@ impl Parameters { this, generics, is_remote, + is_packed, } } @@ -1238,9 +1244,19 @@ fn mut_if(is_mut: bool) -> Option { fn get_member(params: &Parameters, field: &Field, member: &Member) -> TokenStream { let self_var = ¶ms.self_var; match (params.is_remote, field.attrs.getter()) { - (false, None) => quote!(&#self_var.#member), + (false, None) => { + if params.is_packed { + quote!(&{let copy = #self_var.#member; copy }) + } else { + quote!(&#self_var.#member) + } + } (true, None) => { - let inner = quote!(&#self_var.#member); + let inner = if params.is_packed { + quote!(&{let copy = #self_var.#member; copy }) + } else { + quote!(&#self_var.#member) + }; let ty = field.ty; quote!(_serde::private::ser::constrain::<#ty>(#inner)) }