Repository: toml-rs/toml (language: rust)

Issue:
Deserialization error occurs when enum member contains multiple values
The following is a very simple use case. my_box can be serialized into toml, but this toml cannot be parsed back.

``` rust
    #[derive(Debug, Serialize, Deserialize)]
    enum AB {
        A(i32, i32),
        B(String, String),
    }

    #[derive(Debug, Serialize, Deserialize)]
    struct Box {
        inner: Vec<AB>,
    }

    #[test]
    fn toml_test() {
        let my_box = Box {
            inner: vec![
                AB::A(1, 1),
                AB::B("2".to_string(), "2".to_string()),
            ],
        };

        println!("===== now serialize =====");

        // serialize my_box to a TOML string
        match toml::to_string(&my_box) {
            Ok(v) => {
                println!("ser ok!\n{}", &v);

                println!("===== now deserialize =====");

                // deserialize the TOML string back to my_box
                match toml::from_str::<Box>(&v) {
                    Ok(v) => println!("de ok!\n{:?}", v),
                    Err(e) => println!("de err!\n{}", e),
                }
            }
            Err(e) => println!("ser err!\n{}", e),
        }
    }
```

output
```
===== now serialize =====
ser ok!
inner = [[1, 1], ["2", "2"]]

===== now deserialize =====
de err!
TOML parse error at line 1, column 10
  |
1 | inner = [[1, 1], ["2", "2"]]
  |          ^^^^^^
wanted string or table
```

---

Then I wrapped the two values of each member into a tuple and it worked fine

``` rust
#[derive(Debug, Serialize, Deserialize)]
    enum AB {
        A((i32, i32)),
        B((String, String)),
    }

    #[derive(Debug, Serialize, Deserialize)]
    struct Box {
        inner: Vec<AB>,
    }

    #[test]
    fn toml_test() {
        let my_box = Box {
            inner: vec![
                AB::A((1, 1)),
                AB::B(("2".to_string(), "2".to_string())),
            ],
        };

        println!("===== now serialize =====");

        // serialize my_box to a TOML string
        match toml::to_string(&my_box) {
            Ok(v) => {
                println!("ser ok!\n{}", &v);

                println!("===== now deserialize =====");

                // deserialize the TOML string back to my_box
                match toml::from_str::<Box>(&v) {
                    Ok(v) => println!("de ok!\n{:?}", v),
                    Err(e) => println!("de err!\n{}", e),
                }
            }
            Err(e) => println!("ser err!\n{}", e),
        }
    }
```

output
```
===== now serialize =====
ser ok!
[[inner]]
A = [1, 1]

[[inner]]
B = ["2", "2"]

===== now deserialize =====
de ok!
Box { inner: [A((1, 1)), B(("2", "2"))] }
```

---

After that, I changed the enum parameters to named form, and even the serialization went wrong.

``` rust
#[derive(Debug, Serialize, Deserialize)]
enum AB {
    A { v1: i32, v2: i32 },
    B { s1: String, s2: String },
}

#[derive(Debug, Serialize, Deserialize)]
struct Box {
    inner: Vec<AB>,
}

#[test]
fn toml_test() {
    let my_box = Box {
        inner: vec![
            AB::A { v1: 1, v2: 2 },
            AB::B { s1: "a".to_string(), s2: "b".to_string() },
        ],
    };

    println!("original:\n{:?}", &my_box);

    println!("===== now serialize =====");

    // serialize my_box to a TOML string
    match toml::to_string(&my_box) {
        Ok(v) => {
            println!("ser ok!\n{}", &v);

            println!("===== now deserialize =====");

            // deserialize the TOML string back to my_box
            match toml::from_str::<Box>(&v) {
                Ok(v) => println!("de ok!\n{:?}", v),
                Err(e) => println!("de err!\n{}", e),
            }
        }
        Err(e) => println!("ser err!\n{}", e),
    }
}
```

output
```
original:
Box { inner: [A { v1: 1, v2: 2 }, B { s1: "a", s2: "b" }] }
===== now serialize =====
ser err!
unsupported AB type
```


Discussion / hints:

Fix patch:
diff --git a/crates/toml/src/value.rs b/crates/toml/src/value.rs
index 2785d9de..b98196ad 100644
--- a/crates/toml/src/value.rs
+++ b/crates/toml/src/value.rs
@@ -770,6 +770,13 @@ impl<'de> serde::de::VariantAccess<'de> for MapEnumDeserializer {
     fn unit_variant(self) -> Result<(), Self::Error> {
         use de::Error;
         match self.value {
+            Value::Array(values) => {
+                if values.is_empty() {
+                    Ok(())
+                } else {
+                    Err(Error::custom("expected empty array"))
+                }
+            }
             Value::Table(values) => {
                 if values.is_empty() {
                     Ok(())
@@ -797,6 +804,13 @@ impl<'de> serde::de::VariantAccess<'de> for MapEnumDeserializer {
     {
         use de::Error;
         match self.value {
+            Value::Array(values) => {
+                if values.len() == len {
+                    serde::de::Deserializer::deserialize_seq(values.into_deserializer(), visitor)
+                } else {
+                    Err(Error::custom(format!("expected tuple with length {}", len)))
+                }
+            }
             Value::Table(values) => {
                 let tuple_values = values
                     .into_iter()
@@ -870,10 +884,10 @@ impl ser::Serializer for ValueSerializer {
     type SerializeSeq = ValueSerializeVec;
     type SerializeTuple = ValueSerializeVec;
     type SerializeTupleStruct = ValueSerializeVec;
-    type SerializeTupleVariant = ValueSerializeVec;
+    type SerializeTupleVariant = ValueSerializeTupleVariant;
     type SerializeMap = ValueSerializeMap;
     type SerializeStruct = ValueSerializeMap;
-    type SerializeStructVariant = ser::Impossible<Value, crate::ser::Error>;
+    type SerializeStructVariant = ValueSerializeStructVariant;
 
     fn serialize_bool(self, value: bool) -> Result<Value, crate::ser::Error> {
         Ok(Value::Boolean(value))
@@ -1015,10 +1029,10 @@ impl ser::Serializer for ValueSerializer {
         self,
         _name: &'static str,
         _variant_index: u32,
-        _variant: &'static str,
+        variant: &'static str,
         len: usize,
     ) -> Result<Self::SerializeTupleVariant, crate::ser::Error> {
-        self.serialize_seq(Some(len))
+        Ok(ValueSerializeTupleVariant::tuple(variant, len))
     }
 
     fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, crate::ser::Error> {
@@ -1040,12 +1054,12 @@ impl ser::Serializer for ValueSerializer {
 
     fn serialize_struct_variant(
         self,
-        name: &'static str,
+        _name: &'static str,
         _variant_index: u32,
-        _variant: &'static str,
-        _len: usize,
+        variant: &'static str,
+        len: usize,
     ) -> Result<Self::SerializeStructVariant, crate::ser::Error> {
-        Err(crate::ser::Error::unsupported_type(Some(name)))
+        Ok(ValueSerializeStructVariant::struct_(variant, len))
     }
 }
 
@@ -1453,3 +1467,76 @@ impl<'a, 'de> de::Visitor<'de> for DatetimeOrTable<'a> {
         }
     }
 }
+
+type ValueSerializeTupleVariant = ValueSerializeVariant<ValueSerializeVec>;
+type ValueSerializeStructVariant = ValueSerializeVariant<ValueSerializeMap>;
+
+struct ValueSerializeVariant<T> {
+    variant: &'static str,
+    inner: T,
+}
+
+impl ValueSerializeVariant<ValueSerializeVec> {
+    pub(crate) fn tuple(variant: &'static str, len: usize) -> Self {
+        Self {
+            variant,
+            inner: ValueSerializeVec {
+                vec: Vec::with_capacity(len),
+            },
+        }
+    }
+}
+
+impl ValueSerializeVariant<ValueSerializeMap> {
+    pub(crate) fn struct_(variant: &'static str, len: usize) -> Self {
+        Self {
+            variant,
+            inner: ValueSerializeMap {
+                ser: SerializeMap {
+                    map: Table::with_capacity(len),
+                    next_key: None,
+                },
+            },
+        }
+    }
+}
+
+impl serde::ser::SerializeTupleVariant for ValueSerializeVariant<ValueSerializeVec> {
+    type Ok = crate::Value;
+    type Error = crate::ser::Error;
+
+    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
+    where
+        T: serde::ser::Serialize,
+    {
+        serde::ser::SerializeSeq::serialize_element(&mut self.inner, value)
+    }
+
+    fn end(self) -> Result<Self::Ok, Self::Error> {
+        let inner = serde::ser::SerializeSeq::end(self.inner)?;
+        let mut table = Table::new();
+        table.insert(self.variant.to_owned(), inner);
+        Ok(Value::Table(table))
+    }
+}
+
+impl serde::ser::SerializeStructVariant for ValueSerializeVariant<ValueSerializeMap> {
+    type Ok = crate::Value;
+    type Error = crate::ser::Error;
+
+    #[inline]
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
+    where
+        T: serde::ser::Serialize + ?Sized,
+    {
+        serde::ser::SerializeStruct::serialize_field(&mut self.inner, key, value)
+    }
+
+    #[inline]
+    fn end(self) -> Result<Self::Ok, Self::Error> {
+        let inner = serde::ser::SerializeStruct::end(self.inner)?;
+        let mut table = Table::new();
+        table.insert(self.variant.to_owned(), inner);
+        Ok(Value::Table(table))
+    }
+}
diff --git a/crates/toml_edit/src/de/table_enum.rs b/crates/toml_edit/src/de/table_enum.rs
index 197ad6ea..328834bf 100644
--- a/crates/toml_edit/src/de/table_enum.rs
+++ b/crates/toml_edit/src/de/table_enum.rs
@@ -16,6 +16,20 @@ impl<'de> serde::de::VariantAccess<'de> for TableEnumDeserializer {
 
     fn unit_variant(self) -> Result<(), Self::Error> {
         match self.value {
+            crate::Item::ArrayOfTables(values) => {
+                if values.is_empty() {
+                    Ok(())
+                } else {
+                    Err(Error::custom("expected empty array", values.span()))
+                }
+            }
+            crate::Item::Value(crate::Value::Array(values)) => {
+                if values.is_empty() {
+                    Ok(())
+                } else {
+                    Err(Error::custom("expected empty table", values.span()))
+                }
+            }
             crate::Item::Table(values) => {
                 if values.is_empty() {
                     Ok(())
@@ -49,6 +63,38 @@ impl<'de> serde::de::VariantAccess<'de> for TableEnumDeserializer {
         V: serde::de::Visitor<'de>,
     {
         match self.value {
+            crate::Item::ArrayOfTables(values) => {
+                let values_span = values.span();
+                let tuple_values = values.values.into_iter().collect::<Vec<_>>();
+
+                if tuple_values.len() == len {
+                    serde::de::Deserializer::deserialize_seq(
+                        super::ArrayDeserializer::new(tuple_values, values_span),
+                        visitor,
+                    )
+                } else {
+                    Err(Error::custom(
+                        format!("expected tuple with length {}", len),
+                        values_span,
+                    ))
+                }
+            }
+            crate::Item::Value(crate::Value::Array(values)) => {
+                let values_span = values.span();
+                let tuple_values = values.values.into_iter().collect::<Vec<_>>();
+
+                if tuple_values.len() == len {
+                    serde::de::Deserializer::deserialize_seq(
+                        super::ArrayDeserializer::new(tuple_values, values_span),
+                        visitor,
+                    )
+                } else {
+                    Err(Error::custom(
+                        format!("expected tuple with length {}", len),
+                        values_span,
+                    ))
+                }
+            }
             crate::Item::Table(values) => {
                 let values_span = values.span();
                 let tuple_values = values
diff --git a/crates/toml_edit/src/ser/map.rs b/crates/toml_edit/src/ser/map.rs
index 951a5cb9..47e56ba4 100644
--- a/crates/toml_edit/src/ser/map.rs
+++ b/crates/toml_edit/src/ser/map.rs
@@ -1,4 +1,4 @@
-use super::{Error, KeySerializer, ValueSerializer};
+use super::{Error, KeySerializer, SerializeValueArray, ValueSerializer};
 
 #[doc(hidden)]
 pub enum SerializeMap {
@@ -165,7 +165,6 @@ impl serde::ser::SerializeMap for SerializeInlineTable {
     where
         T: serde::ser::Serialize,
     {
-        self.key = None;
         self.key = Some(input.serialize(KeySerializer)?);
         Ok(())
     }
@@ -423,10 +422,10 @@ impl serde::ser::Serializer for &mut MapValueSerializer {
     type SerializeSeq = super::SerializeValueArray;
     type SerializeTuple = super::SerializeValueArray;
     type SerializeTupleStruct = super::SerializeValueArray;
-    type SerializeTupleVariant = super::SerializeValueArray;
+    type SerializeTupleVariant = super::SerializeTupleVariant;
     type SerializeMap = super::SerializeMap;
     type SerializeStruct = super::SerializeMap;
-    type SerializeStructVariant = serde::ser::Impossible<Self::Ok, Self::Error>;
+    type SerializeStructVariant = super::SerializeStructVariant;
 
     fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
         ValueSerializer::new().serialize_bool(v)
@@ -585,3 +584,81 @@ impl serde::ser::Serializer for &mut MapValueSerializer {
         ValueSerializer::new().serialize_struct_variant(name, variant_index, variant, len)
     }
 }
+
+pub type SerializeTupleVariant = SerializeVariant<SerializeValueArray>;
+pub type SerializeStructVariant = SerializeVariant<SerializeMap>;
+
+pub struct SerializeVariant<T> {
+    variant: &'static str,
+    inner: T,
+}
+
+impl SerializeVariant<SerializeValueArray> {
+    pub(crate) fn tuple(variant: &'static str, len: usize) -> Self {
+        Self {
+            variant,
+            inner: SerializeValueArray::with_capacity(len),
+        }
+    }
+}
+
+impl SerializeVariant<SerializeMap> {
+    pub(crate) fn struct_(variant: &'static str, len: usize) -> Self {
+        Self {
+            variant,
+            inner: SerializeMap::table_with_capacity(len),
+        }
+    }
+}
+
+impl serde::ser::SerializeTupleVariant for SerializeVariant<SerializeValueArray> {
+    type Ok = crate::Value;
+    type Error = Error;
+
+    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
+    where
+        T: serde::ser::Serialize,
+    {
+        serde::ser::SerializeSeq::serialize_element(&mut self.inner, value)
+    }
+
+    fn end(self) -> Result<Self::Ok, Self::Error> {
+        let inner = serde::ser::SerializeSeq::end(self.inner)?;
+        let mut items = crate::table::KeyValuePairs::new();
+        let kv = crate::table::TableKeyValue::new(
+            crate::Key::new(self.variant),
+            crate::Item::Value(inner),
+        );
+        items.insert(crate::InternalString::from(self.variant), kv);
+        Ok(crate::Value::InlineTable(crate::InlineTable::with_pairs(
+            items,
+        )))
+    }
+}
+
+impl serde::ser::SerializeStructVariant for SerializeVariant<SerializeMap> {
+    type Ok = crate::Value;
+    type Error = Error;
+
+    #[inline]
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
+    where
+        T: serde::ser::Serialize + ?Sized,
+    {
+        serde::ser::SerializeStruct::serialize_field(&mut self.inner, key, value)
+    }
+
+    #[inline]
+    fn end(self) -> Result<Self::Ok, Self::Error> {
+        let inner = serde::ser::SerializeStruct::end(self.inner)?;
+        let mut items = crate::table::KeyValuePairs::new();
+        let kv = crate::table::TableKeyValue::new(
+            crate::Key::new(self.variant),
+            crate::Item::Value(inner),
+        );
+        items.insert(crate::InternalString::from(self.variant), kv);
+        Ok(crate::Value::InlineTable(crate::InlineTable::with_pairs(
+            items,
+        )))
+    }
+}
diff --git a/crates/toml_edit/src/ser/value.rs b/crates/toml_edit/src/ser/value.rs
index d29390a4..808bb900 100644
--- a/crates/toml_edit/src/ser/value.rs
+++ b/crates/toml_edit/src/ser/value.rs
@@ -60,10 +60,10 @@ impl serde::ser::Serializer for ValueSerializer {
     type SerializeSeq = super::SerializeValueArray;
     type SerializeTuple = super::SerializeValueArray;
     type SerializeTupleStruct = super::SerializeValueArray;
-    type SerializeTupleVariant = super::SerializeValueArray;
+    type SerializeTupleVariant = super::SerializeTupleVariant;
     type SerializeMap = super::SerializeMap;
     type SerializeStruct = super::SerializeMap;
-    type SerializeStructVariant = serde::ser::Impossible<Self::Ok, Self::Error>;
+    type SerializeStructVariant = super::SerializeStructVariant;
 
     fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
         Ok(v.into())
@@ -205,10 +205,10 @@ impl serde::ser::Serializer for ValueSerializer {
         self,
         _name: &'static str,
         _variant_index: u32,
-        _variant: &'static str,
+        variant: &'static str,
         len: usize,
     ) -> Result<Self::SerializeTupleVariant, Self::Error> {
-        self.serialize_seq(Some(len))
+        Ok(super::SerializeTupleVariant::tuple(variant, len))
     }
 
     fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
@@ -233,11 +233,11 @@ impl serde::ser::Serializer for ValueSerializer {
 
     fn serialize_struct_variant(
         self,
-        name: &'static str,
+        _name: &'static str,
         _variant_index: u32,
-        _variant: &'static str,
-        _len: usize,
+        variant: &'static str,
+        len: usize,
     ) -> Result<Self::SerializeStructVariant, Self::Error> {
-        Err(Error::UnsupportedType(Some(name)))
+        Ok(super::SerializeStructVariant::struct_(variant, len))
     }
 }


Test patch:
diff --git a/crates/toml/tests/testsuite/serde.rs b/crates/toml/tests/testsuite/serde.rs
index 13af51d8..82ba00b1 100644
--- a/crates/toml/tests/testsuite/serde.rs
+++ b/crates/toml/tests/testsuite/serde.rs
@@ -21,19 +21,26 @@ macro_rules! equivalent {
         let toml = $toml;
         let literal = $literal;
 
-        // In/out of Value is equivalent
-        println!("try_from");
-        assert_eq!(t!(Table::try_from(literal.clone())), toml);
-        println!("try_into");
-        assert_eq!(literal, t!(toml.clone().try_into()));
-
         // Through a string equivalent
         println!("to_string");
-        snapbox::assert_eq(t!(toml::to_string(&toml)), t!(toml::to_string(&literal)));
+        snapbox::assert_eq(t!(toml::to_string(&literal)), t!(toml::to_string(&toml)));
         println!("literal, from_str(toml)");
         assert_eq!(literal, t!(toml::from_str(&t!(toml::to_string(&toml)))));
-        println!("toml, from_str(toml)");
-        assert_eq!(toml, t!(toml::from_str(&t!(toml::to_string(&toml)))));
+        println!("toml, from_str(literal)");
+        assert_eq!(toml, t!(toml::from_str(&t!(toml::to_string(&literal)))));
+
+        // In/out of Value is equivalent
+        println!("Table::try_from(literal)");
+        assert_eq!(toml, t!(Table::try_from(literal.clone())));
+        println!("Value::try_from(literal)");
+        assert_eq!(
+            Value::Table(toml.clone()),
+            t!(Value::try_from(literal.clone()))
+        );
+        println!("toml.try_into()");
+        assert_eq!(literal, t!(toml.clone().try_into()));
+        println!("Value::Table(toml).try_into()");
+        assert_eq!(literal, t!(Value::Table(toml.clone()).try_into()));
     }};
 }
 
@@ -376,6 +383,106 @@ fn parse_enum_string() {
     }
 }
 
+#[test]
+fn parse_tuple_variant() {
+    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+    struct Document {
+        inner: Vec<Enum>,
+    }
+
+    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+    enum Enum {
+        Int(i32, i32),
+        String(String, String),
+    }
+
+    let input = Document {
+        inner: vec![
+            Enum::Int(1, 1),
+            Enum::String("2".to_owned(), "2".to_owned()),
+        ],
+    };
+    let expected = "[[inner]]
+Int = [1, 1]
+
+[[inner]]
+String = [\"2\", \"2\"]
+";
+    let raw = toml::to_string(&input).unwrap();
+    snapbox::assert_eq(expected, raw);
+
+    equivalent! {
+        Document {
+            inner: vec![
+                Enum::Int(1, 1),
+                Enum::String("2".to_owned(), "2".to_owned()),
+            ],
+        },
+        map! {
+            inner: vec![
+                map! { Int: [1, 1] },
+                map! { String: ["2".to_owned(), "2".to_owned()] },
+            ]
+        },
+    }
+}
+
+#[test]
+fn parse_struct_variant() {
+    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+    struct Document {
+        inner: Vec<Enum>,
+    }
+
+    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+    enum Enum {
+        Int { first: i32, second: i32 },
+        String { first: String, second: String },
+    }
+
+    let input = Document {
+        inner: vec![
+            Enum::Int {
+                first: 1,
+                second: 1,
+            },
+            Enum::String {
+                first: "2".to_owned(),
+                second: "2".to_owned(),
+            },
+        ],
+    };
+    let expected = "[[inner]]
+
+[inner.Int]
+first = 1
+second = 1
+
+[[inner]]
+
+[inner.String]
+first = \"2\"
+second = \"2\"
+";
+    let raw = toml::to_string(&input).unwrap();
+    snapbox::assert_eq(expected, raw);
+
+    equivalent! {
+        Document {
+            inner: vec![
+                Enum::Int { first: 1, second: 1 },
+                Enum::String { first: "2".to_owned(), second: "2".to_owned() },
+            ],
+        },
+        map! {
+            inner: vec![
+                map! { Int: map! { first: 1, second: 1 } },
+                map! { String: map! { first: "2".to_owned(), second: "2".to_owned() } },
+            ]
+        },
+    }
+}
+
 #[test]
 #[cfg(feature = "preserve_order")]
 fn map_key_unit_variants() {