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

Issue:
`Vec`s containing `Option` types are removed from output
If a vector contains an option type, and any of its entries are `None`, the vector is completely removed from the output. I believe this is because `toml` tries not to output anything for `None` values, but this chokes when trying to insert a gap with nothing in it.

If you create an enum with the same structure as `Option`, `toml` uses a different format that correctly handles "gaps" (shown in the below example); I believe this should be used for vectors of options.

[Example on Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=640b11e3171204502e51f656fd6e53f1)


Discussion / hints:
The issue seems to be [here](https://github.com/toml-rs/toml/blob/main/crates/toml_edit/src/ser/array.rs#L28)

The `?` bubbles all errors up, including `UnsupportedNone`, despite it having a special meaning. This causes the entire array to be removed. At the very least, `UnsupportedNone` should be transformed into another error:

```rust
let value = value.serialize(super::ValueSerializer {}).map_err(|e| {
    if let Error::UnsupportedNone = e {
        Error::UnsupportedType(Some("None is unsupported in vectors"))
    } else {
        e
    }
})?;
```

But supporting `None` in Vectors seems worth it :)

Fix patch:
diff --git a/crates/toml_edit/src/ser/map.rs b/crates/toml_edit/src/ser/map.rs
index d743e3d5..951a5cb9 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};
+use super::{Error, KeySerializer, ValueSerializer};
 
 #[doc(hidden)]
 pub enum SerializeMap {
@@ -174,7 +174,8 @@ impl serde::ser::SerializeMap for SerializeInlineTable {
     where
         T: serde::ser::Serialize,
     {
-        let res = value.serialize(super::ValueSerializer {});
+        let mut value_serializer = MapValueSerializer::new();
+        let res = value.serialize(&mut value_serializer);
         match res {
             Ok(item) => {
                 let key = self.key.take().unwrap();
@@ -185,7 +186,7 @@ impl serde::ser::SerializeMap for SerializeInlineTable {
                 self.items.insert(key, kv);
             }
             Err(e) => {
-                if e != Error::UnsupportedNone {
+                if !(e == Error::UnsupportedNone && value_serializer.is_none) {
                     return Err(e);
                 }
             }
@@ -210,7 +211,8 @@ impl serde::ser::SerializeStruct for SerializeInlineTable {
     where
         T: serde::ser::Serialize,
     {
-        let res = value.serialize(super::ValueSerializer {});
+        let mut value_serializer = MapValueSerializer::new();
+        let res = value.serialize(&mut value_serializer);
         match res {
             Ok(item) => {
                 let kv = crate::table::TableKeyValue::new(
@@ -220,7 +222,7 @@ impl serde::ser::SerializeStruct for SerializeInlineTable {
                 self.items.insert(crate::InternalString::from(key), kv);
             }
             Err(e) => {
-                if e != Error::UnsupportedNone {
+                if !(e == Error::UnsupportedNone && value_serializer.is_none) {
                     return Err(e);
                 }
             }
@@ -403,3 +405,183 @@ impl serde::ser::Serializer for DatetimeFieldSerializer {
         Err(Error::DateInvalid)
     }
 }
+
+#[derive(Default)]
+struct MapValueSerializer {
+    is_none: bool,
+}
+
+impl MapValueSerializer {
+    fn new() -> Self {
+        Self { is_none: false }
+    }
+}
+
+impl serde::ser::Serializer for &mut MapValueSerializer {
+    type Ok = crate::Value;
+    type Error = Error;
+    type SerializeSeq = super::SerializeValueArray;
+    type SerializeTuple = super::SerializeValueArray;
+    type SerializeTupleStruct = super::SerializeValueArray;
+    type SerializeTupleVariant = super::SerializeValueArray;
+    type SerializeMap = super::SerializeMap;
+    type SerializeStruct = super::SerializeMap;
+    type SerializeStructVariant = serde::ser::Impossible<Self::Ok, Self::Error>;
+
+    fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
+        ValueSerializer::new().serialize_bool(v)
+    }
+
+    fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
+        ValueSerializer::new().serialize_i8(v)
+    }
+
+    fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
+        ValueSerializer::new().serialize_i16(v)
+    }
+
+    fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
+        ValueSerializer::new().serialize_i32(v)
+    }
+
+    fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
+        ValueSerializer::new().serialize_i64(v)
+    }
+
+    fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
+        ValueSerializer::new().serialize_u8(v)
+    }
+
+    fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
+        ValueSerializer::new().serialize_u16(v)
+    }
+
+    fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
+        ValueSerializer::new().serialize_u32(v)
+    }
+
+    fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
+        ValueSerializer::new().serialize_u64(v)
+    }
+
+    fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
+        ValueSerializer::new().serialize_f32(v)
+    }
+
+    fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
+        ValueSerializer::new().serialize_f64(v)
+    }
+
+    fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
+        ValueSerializer::new().serialize_char(v)
+    }
+
+    fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
+        ValueSerializer::new().serialize_str(v)
+    }
+
+    fn serialize_bytes(self, value: &[u8]) -> Result<Self::Ok, Self::Error> {
+        ValueSerializer::new().serialize_bytes(value)
+    }
+
+    fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
+        self.is_none = true;
+        Err(Error::UnsupportedNone)
+    }
+
+    fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
+    where
+        T: serde::ser::Serialize,
+    {
+        ValueSerializer::new().serialize_some(value)
+    }
+
+    fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
+        ValueSerializer::new().serialize_unit()
+    }
+
+    fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
+        ValueSerializer::new().serialize_unit_struct(name)
+    }
+
+    fn serialize_unit_variant(
+        self,
+        name: &'static str,
+        variant_index: u32,
+        variant: &'static str,
+    ) -> Result<Self::Ok, Self::Error> {
+        ValueSerializer::new().serialize_unit_variant(name, variant_index, variant)
+    }
+
+    fn serialize_newtype_struct<T: ?Sized>(
+        self,
+        name: &'static str,
+        value: &T,
+    ) -> Result<Self::Ok, Self::Error>
+    where
+        T: serde::ser::Serialize,
+    {
+        ValueSerializer::new().serialize_newtype_struct(name, value)
+    }
+
+    fn serialize_newtype_variant<T: ?Sized>(
+        self,
+        name: &'static str,
+        variant_index: u32,
+        variant: &'static str,
+        value: &T,
+    ) -> Result<Self::Ok, Self::Error>
+    where
+        T: serde::ser::Serialize,
+    {
+        ValueSerializer::new().serialize_newtype_variant(name, variant_index, variant, value)
+    }
+
+    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
+        ValueSerializer::new().serialize_seq(len)
+    }
+
+    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
+        ValueSerializer::new().serialize_tuple(len)
+    }
+
+    fn serialize_tuple_struct(
+        self,
+        name: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
+        ValueSerializer::new().serialize_tuple_struct(name, len)
+    }
+
+    fn serialize_tuple_variant(
+        self,
+        name: &'static str,
+        variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleVariant, Self::Error> {
+        ValueSerializer::new().serialize_tuple_variant(name, variant_index, variant, len)
+    }
+
+    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
+        ValueSerializer::new().serialize_map(len)
+    }
+
+    fn serialize_struct(
+        self,
+        name: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeStruct, Self::Error> {
+        ValueSerializer::new().serialize_struct(name, len)
+    }
+
+    fn serialize_struct_variant(
+        self,
+        name: &'static str,
+        variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeStructVariant, Self::Error> {
+        ValueSerializer::new().serialize_struct_variant(name, variant_index, variant, len)
+    }
+}


Test patch:
diff --git a/crates/toml/tests/testsuite/serde.rs b/crates/toml/tests/testsuite/serde.rs
index 6e927f73..1d2fe4f0 100644
--- a/crates/toml/tests/testsuite/serde.rs
+++ b/crates/toml/tests/testsuite/serde.rs
@@ -1094,3 +1094,24 @@ fn datetime_offset_issue_496() {
     let output = toml.to_string();
     snapbox::assert_eq(original, output);
 }
+
+#[test]
+fn serialize_array_with_none_value() {
+    #[derive(Serialize)]
+    struct Document {
+        values: Vec<Option<usize>>,
+    }
+
+    let input = Document {
+        values: vec![Some(1), Some(2), Some(3)],
+    };
+    let expected = "values = [1, 2, 3]\n";
+    let raw = toml::to_string(&input).unwrap();
+    snapbox::assert_eq(expected, raw);
+
+    let input = Document {
+        values: vec![Some(1), None, Some(3)],
+    };
+    let err = toml::to_string(&input).unwrap_err();
+    snapbox::assert_eq("unsupported None value", err.to_string());
+}