From 9f47c47cade14f16e527f2eb555edc5a35a2f6bd Mon Sep 17 00:00:00 2001
From: alvardes <ootvardes98@outlook.com>
Date: Fri, 1 May 2020 15:32:54 +0200
Subject: [PATCH] Add support for packed structs.

---
 serde_derive/src/internals/attr.rs | 27 +++++++++++++++++++++++++++
 serde_derive/src/ser.rs            | 20 ++++++++++++++++++--
 2 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/serde_derive/src/internals/attr.rs b/serde_derive/src/internals/attr.rs
index f80411d8..01b76422 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<syn::Path>,
+    is_packed: bool,
 }
 
 /// Styles of representing an enum.
@@ -592,6 +593,27 @@ impl Container {
             }
         }
 
+        use proc_macro2::Delimiter;
+        let is_packed = item.attrs.iter().any(|attr| {
+            match attr.style {
+                syn::AttrStyle::Outer => attr
+                        .path
+                        .get_ident()
+                        .map_or(false, |ident| *ident == "repr")
+                    && syn::parse2::<Group>(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 ")
+                        }),
+                _ => false
+            }
+        });
+
         Container {
             name: Name::from_attrs(unraw(&item.ident), ser_name, de_name, None),
             transparent: transparent.get(),
@@ -611,6 +633,7 @@ impl Container {
             identifier: decide_identifier(cx, item, field_identifier, variant_identifier),
             has_flatten: false,
             serde_path: serde_path.get(),
+            is_packed,
         }
     }
 
@@ -662,6 +685,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 37b9a8af..6163026b 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<TokenStream> {
 fn get_member(params: &Parameters, field: &Field, member: &Member) -> TokenStream {
     let self_var = &params.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))
         }