Repository: 3Hren/msgpack-rust (Rust)

Issue:
Struct-like enum variants don't round-trip when using `with_struct_map`
When using an struct-like enum with `with_struct_map` or `with_struct_tuple`, decoding the encoded enum fails. For example, this test:

```rust
use serde::Serialize;

#[derive(Serialize, Deserialize, Debug, PartialEq)]
enum Enum {
    A {
        data: u32
    },
}

let expected = Enum::A { data: 42 };
let mut buf = Vec::new();
expected.serialize(&mut Serializer::new(&mut buf).with_struct_map()).unwrap();

let mut de = Deserializer::new(&buf[..]);

assert_eq!(expected, Deserialize::deserialize(&mut de).unwrap());
```

fails with

```
thread 'round_struct_like_enum_with_struct_map' panicked at 'called `Result::unwrap()` on an `Err` value: TypeMismatch(FixArray(2))', src/libcore/result.rs:999:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
```


I think this is due to `Serializer` encoding enum struct variants a map `{idx => data}`:

https://github.com/3Hren/msgpack-rust/blob/88c46511cc9bb9cabce8b53d46a0062b7b88a9a0/rmp-serde/src/encode.rs#L461-L468

While the ext serializers encode enum variants as an array `[idx, data]`:

https://github.com/3Hren/msgpack-rust/blob/88c46511cc9bb9cabce8b53d46a0062b7b88a9a0/rmp-serde/src/ext.rs#L215-L220

https://github.com/3Hren/msgpack-rust/blob/88c46511cc9bb9cabce8b53d46a0062b7b88a9a0/rmp-serde/src/ext.rs#L465-L470


Discussion:

Patch:
diff --git a/rmp-serde/src/ext.rs b/rmp-serde/src/ext.rs
index 18180394..e3915ce9 100644
--- a/rmp-serde/src/ext.rs
+++ b/rmp-serde/src/ext.rs
@@ -213,7 +213,8 @@ where
 
     #[inline]
     fn serialize_struct_variant(self, _name: &'static str, variant_index: u32, _variant: &'static str, len: usize) -> Result<Self::SerializeStructVariant, Self::Error> {
-        encode::write_array_len(self.se.get_mut(), 2)?;
+        // encode as a map from variant idx to a sequence of its attributed data, like: {idx => [v1,...,vN]}
+        encode::write_map_len(&mut self.se.get_mut(), 1)?;
         self.se.serialize_u32(variant_index)?;
         encode::write_map_len(self.se.get_mut(), len as u32)?;
         Ok(self)
@@ -463,7 +464,8 @@ where
 
     #[inline]
     fn serialize_struct_variant(self, _name: &'static str, variant_index: u32, _variant: &'static str, len: usize) -> Result<Self::SerializeStructVariant, Self::Error> {
-        encode::write_array_len(&mut self.se.get_mut(), 2)?;
+        // encode as a map from variant idx to a sequence of its attributed data, like: {idx => [v1,...,vN]}
+        encode::write_map_len(&mut self.se.get_mut(), 1)?;
         self.se.serialize_u32(variant_index)?;
         encode::write_array_len(self.se.get_mut(), len as u32)?;
         Ok(self)


Test patch:
diff --git a/rmp-serde/tests/encode_derive.rs b/rmp-serde/tests/encode_derive.rs
index dbabf0a2..29c0bbe8 100644
--- a/rmp-serde/tests/encode_derive.rs
+++ b/rmp-serde/tests/encode_derive.rs
@@ -164,8 +164,8 @@ fn serialize_struct_variant_as_map() {
         .with_struct_map();
     Enum::V1 { f1: 42 }.serialize(&mut se).unwrap();
 
-    // Expect: [0, {"f1": 42}].
-    assert_eq!(vec![0x92, 0x00, 0x81, 0xa2, 0x66, 0x31, 0x2a], se.into_inner());
+    // Expect: {0 => {"f1": 42}}.
+    assert_eq!(vec![0x81, 0x00, 0x81, 0xa2, 0x66, 0x31, 0x2a], se.into_inner());
 }
 
 #[test]
diff --git a/rmp-serde/tests/round.rs b/rmp-serde/tests/round.rs
index a99b2861..35979537 100644
--- a/rmp-serde/tests/round.rs
+++ b/rmp-serde/tests/round.rs
@@ -8,6 +8,7 @@ use std::borrow::Cow;
 use std::io::Cursor;
 
 use serde::{Deserialize, Serialize};
+use rmps::encode::Ext;
 use rmps::{Deserializer, Serializer};
 
 #[test]
@@ -80,6 +81,64 @@ fn round_trip_option_cow() {
     assert_eq!(expected, Deserialize::deserialize(&mut de).unwrap());
 }
 
+#[test]
+fn round_struct_like_enum() {
+    use serde::Serialize;
+
+    #[derive(Serialize, Deserialize, Debug, PartialEq)]
+    enum Enum {
+        A { data: u32 },
+    }
+
+    let expected = Enum::A { data: 42 };
+    let mut buf = Vec::new();
+    expected.serialize(&mut Serializer::new(&mut buf)).unwrap();
+
+    let mut de = Deserializer::new(&buf[..]);
+
+    assert_eq!(expected, Deserialize::deserialize(&mut de).unwrap());
+}
+
+#[test]
+fn round_struct_like_enum_with_struct_map() {
+    use serde::Serialize;
+
+    #[derive(Serialize, Deserialize, Debug, PartialEq)]
+    enum Enum {
+        A { data: u32 },
+    }
+
+    let expected = Enum::A { data: 42 };
+    let mut buf = Vec::new();
+    expected
+        .serialize(&mut Serializer::new(&mut buf).with_struct_map())
+        .unwrap();
+
+    let mut de = Deserializer::new(&buf[..]);
+
+    assert_eq!(expected, Deserialize::deserialize(&mut de).unwrap());
+}
+
+#[test]
+fn round_struct_like_enum_with_struct_tuple() {
+    use serde::Serialize;
+
+    #[derive(Serialize, Deserialize, Debug, PartialEq)]
+    enum Enum {
+        A { data: u32 },
+    }
+
+    let expected = Enum::A { data: 42 };
+    let mut buf = Vec::new();
+    expected
+        .serialize(&mut Serializer::new(&mut buf).with_struct_tuple())
+        .unwrap();
+
+    let mut de = Deserializer::new(&buf[..]);
+
+    assert_eq!(expected, Deserialize::deserialize(&mut de).unwrap());
+}
+
 #[test]
 fn round_enum_with_newtype_struct() {
     use serde::Serialize;