Repository: alexcrichton/toml-rs (rust)

Issue:
Deserializing enums
I want to deserialize enum from string, I've seen many people already filed this issue but I am looking for quick solution.

```Rust
#[derive(Deserialize)]
enum Type {
    One,
    Two,
}
```

The error I'm getting:

```Rust
Error {
    inner: ErrorInner {
        kind: Custom,
        line: None,
        col: 0,
        message: "invalid type: string \"One\", expected enum Type",
        key: ["markets"],
    },
}
```

Is it possible to deserialize enum like that? Is there any quick solution to this without rewriting the parser?
I suppose it requires a lot of work if it hasn't been done for such a long time, or maybe it just wasn't worth the time or maybe there is a way to do this without too much effort?

Pull request description:
Allow to deserialize/serialize into enums
Close #164

It currently doesn't work for deserialization and I have no idea where it fails.

```
thread 'parse_enum_string' panicked at 'toml.clone().try_into() failed with invalid type: string "desc", expected enum Sort for key `a`', tests/serde.rs:344
```

Some of the changes are probably not needed/not optimal, I was following the serde docs and serde-json repo trying to make it work first.

Patch:
diff --git a/src/de.rs b/src/de.rs
index 4578c9a..223d492 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -11,6 +11,7 @@ use std::str;
 use std::vec;
 
 use serde::de;
+use serde::de::IntoDeserializer;
 
 use tokens::{Tokenizer, Token, Error as TokenError};
 use datetime::{SERDE_STRUCT_FIELD_NAME, SERDE_STRUCT_NAME};
@@ -121,6 +122,9 @@ enum ErrorKind {
     /// type.
     Custom,
 
+    /// A struct was expected but something else was found
+    ExpectedString,
+
     #[doc(hidden)]
     __Nonexhaustive,
 }
@@ -145,6 +149,7 @@ impl<'de, 'b> de::Deserializer<'de> for &'b mut Deserializer<'de> {
             values: None,
             array: false,
         };
+
         while let Some(line) = self.line()? {
             match line {
                 Line::Table { at, mut header, array } => {
@@ -192,9 +197,30 @@ impl<'de, 'b> de::Deserializer<'de> for &'b mut Deserializer<'de> {
         })
     }
 
+    fn deserialize_enum<V>(
+        self,
+        _name: &'static str,
+        _variants: &'static [&'static str],
+        visitor: V
+    ) -> Result<V::Value, Error>
+        where V: de::Visitor<'de>
+    {
+        if let Some(next) = self.next()? {
+            match next {
+                Token::String { val, .. } => {
+                    visitor.visit_enum(val.into_deserializer())
+                },
+                _ => Err(Error::from_kind(ErrorKind::ExpectedString))
+            }
+        } else {
+            Err(Error::from_kind(ErrorKind::UnexpectedEof))
+        }
+    }
+
+
     forward_to_deserialize_any! {
         bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
-        bytes byte_buf map struct unit enum newtype_struct
+        bytes byte_buf map struct unit newtype_struct
         ignored_any unit_struct tuple_struct tuple option identifier
     }
 }
@@ -489,10 +515,24 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> {
         visitor.visit_some(self)
     }
 
+    fn deserialize_enum<V>(
+        self,
+        _name: &'static str,
+        _variants: &'static [&'static str],
+        visitor: V
+    ) -> Result<V::Value, Error>
+        where V: de::Visitor<'de>
+    {
+        match self.value {
+            Value::String(val) => visitor.visit_enum(val.into_deserializer()),
+            _ => Err(Error::from_kind(ErrorKind::ExpectedString))
+        }
+    }
+
     forward_to_deserialize_any! {
         bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
         bytes byte_buf map unit newtype_struct identifier
-        ignored_any unit_struct tuple_struct tuple enum
+        ignored_any unit_struct tuple_struct tuple
     }
 }
 
@@ -574,6 +614,7 @@ impl<'de> de::MapAccess<'de> for InlineTableDeserializer<'de> {
     }
 }
 
+
 impl<'a> Deserializer<'a> {
     /// Creates a new deserializer which will be deserializing the string
     /// provided.
@@ -1092,6 +1133,7 @@ impl fmt::Display for Error {
             ErrorKind::RedefineAsArray => "table redefined as array".fmt(f)?,
             ErrorKind::EmptyTableKey => "empty table key found".fmt(f)?,
             ErrorKind::Custom => self.inner.message.fmt(f)?,
+            ErrorKind::ExpectedString => "expected string".fmt(f)?,
             ErrorKind::__Nonexhaustive => panic!(),
         }
 
@@ -1134,6 +1176,7 @@ impl error::Error for Error {
             ErrorKind::RedefineAsArray => "table redefined as array",
             ErrorKind::EmptyTableKey => "empty table key found",
             ErrorKind::Custom => "a custom error",
+            ErrorKind::ExpectedString => "expected string",
             ErrorKind::__Nonexhaustive => panic!(),
         }
     }
@@ -1193,6 +1236,7 @@ impl<'a> Header<'a> {
     }
 }
 
+#[derive(Debug)]
 enum Value<'a> {
     Integer(i64),
     Float(f64),
diff --git a/src/ser.rs b/src/ser.rs
index e4b62f7..4896f8a 100644
--- a/src/ser.rs
+++ b/src/ser.rs
@@ -421,9 +421,9 @@ impl<'a, 'b> ser::Serializer for &'b mut Serializer<'a> {
     fn serialize_unit_variant(self,
                               _name: &'static str,
                               _variant_index: u32,
-                              _variant: &'static str)
+                              variant: &'static str)
                               -> Result<(), Self::Error> {
-        Err(Error::UnsupportedType)
+        self.serialize_str(variant)
     }
 
     fn serialize_newtype_struct<T: ?Sized>(self, _name: &'static str, value: &T)
diff --git a/src/value.rs b/src/value.rs
index 7c2b3eb..dcc3567 100644
--- a/src/value.rs
+++ b/src/value.rs
@@ -8,6 +8,7 @@ use std::vec;
 
 use serde::ser;
 use serde::de;
+use serde::de::IntoDeserializer;
 
 pub use datetime::{Datetime, DatetimeParseError};
 use datetime::{DatetimeFromString, SERDE_STRUCT_FIELD_NAME};
@@ -505,6 +506,22 @@ impl<'de> de::Deserializer<'de> for Value {
         }
     }
 
+    #[inline]
+    fn deserialize_enum<V>(
+        self,
+        _name: &str,
+        _variants: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value, ::de::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        match self {
+            Value::String(variant) => visitor.visit_enum(variant.into_deserializer()),
+            _ => Err(de::Error::invalid_type(de::Unexpected::UnitVariant, &"string only")),
+        }
+    }
+
     // `None` is interpreted as a missing field so be sure to implement `Some`
     // as a present field.
     fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, ::de::Error>
@@ -516,7 +533,7 @@ impl<'de> de::Deserializer<'de> for Value {
     forward_to_deserialize_any! {
         bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit seq
         bytes byte_buf map unit_struct tuple_struct struct
-        tuple ignored_any enum newtype_struct identifier
+        tuple ignored_any newtype_struct identifier
     }
 }
 
@@ -694,7 +711,7 @@ impl ser::Serializer for Serializer {
                               _variant_index: u32,
                               _variant: &'static str)
                               -> Result<Value, ::ser::Error> {
-        Err(::ser::Error::UnsupportedType)
+        self.serialize_str(_variant)
     }
 
     fn serialize_newtype_struct<T: ?Sized>(self,


Test patch:
diff --git a/tests/serde.rs b/tests/serde.rs
index bfbdc6f..0f4c37a 100644
--- a/tests/serde.rs
+++ b/tests/serde.rs
@@ -329,6 +329,25 @@ fn parse_enum() {
     }
 }
 
+#[test]
+fn parse_enum_string() {
+    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
+    struct Foo { a: Sort }
+
+    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
+    #[serde(rename_all = "lowercase")]
+    enum Sort {
+        Asc,
+        Desc,
+    }
+
+    equivalent! {
+        Foo { a: Sort::Desc },
+        Table(map! { a: Value::String("desc".to_string()) }),
+    }
+
+}
+
 // #[test]
 // fn unused_fields() {
 //     #[derive(Serialize, Deserialize, PartialEq, Debug)]