Skip to content

Commit

Permalink
Merge pull request #608 from epage/enum
Browse files Browse the repository at this point in the history
fix(serde): Support variants
  • Loading branch information
epage committed Sep 13, 2023
2 parents 3f3e832 + 58a7101 commit f3e120f
Show file tree
Hide file tree
Showing 5 changed files with 346 additions and 29 deletions.
103 changes: 95 additions & 8 deletions crates/toml/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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> {
Expand All @@ -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))
}
}

Expand Down Expand Up @@ -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))
}
}
125 changes: 116 additions & 9 deletions crates/toml/tests/testsuite/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
}};
}

Expand Down Expand Up @@ -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() {
Expand Down
46 changes: 46 additions & 0 deletions crates/toml_edit/src/de/table_enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit f3e120f

Please sign in to comment.