Repository: alexcrichton/toml-rs
Pull request #264 (2018-10-09 20:11:06)

Issue 255: Externally tagged enum deserialization
cc: #225, #222

~~The tuple variant hasn't been implemented yet, I'm having trouble figuring it out.~~
See #225 for ongoing discussion -- what should / should not be supported.

Short, the following TOML:

```toml
plain = "Plain"
plain_table = { Plain = {} }
tuple = { Tuple = { 0 = 123, 1 = true } }
struct = { Struct = { value = 123 } }
newtype = { NewType = "value" }
my_enum = [
    { Plain = {} },
    { Tuple = { 0 = 123, 1 = true } },
    { NewType = "value" },
    { Struct = { value = 123 } }
]
```

with the following types:

```rust
#[derive(Debug, Deserialize)]
struct Config {
    plain: MyEnum,
    plain_table: MyEnum,
    tuple: MyEnum,
    #[serde(rename = "struct")]
    structv: MyEnum,
    newtype: MyEnum,
    my_enum: Vec<MyEnum>,
}

#[derive(Debug, Deserialize)]
enum MyEnum {
    Plain,
    Tuple(i64, bool),
    NewType(String),
    Struct { value: i64 },
}
```

deserializes to:

```rust
Config {
    plain: Plain,
    plain_table: Plain,
    tuple: Tuple(
        123,
        true
    ),
    structv: Struct {
        value: 123
    },
    newtype: NewType(
        "value"
    ),
    my_enum: [
        Plain,
        Tuple(
            123,
            true
        ),
        NewType(
            "value"
        ),
        Struct {
            value: 123
        }
    ]
}
```

alexcrichton: Thanks for the PR! I don't personally have too many opinions about how we should map enums to toml, but I think we can probably pretty closely follow `serde_json` in this regard? Would it be possible to just clone whatever `serde_json` does for all these serializations?
azriel91: Finally got round to this, `serde_json` serializes the value like this:

<details>
<summary>json serialized</summary>

```json
{
    "plain": "Plain",
    "plain_table": "Plain",
    "tuple": {
        "Tuple": [
            -1,
            false
        ]
    },
    "struct": {
        "Struct": {
            "value": -1
        }
    },
    "newtype": {
        "NewType": "abc"
    },
    "my_enum": [
        "Plain",
        {
            "Tuple": [
                -1,
                false
            ]
        },
        {
            "NewType": "abc"
        },
        {
            "Struct": {
                "value": -1
            }
        }
    ]
}
```
</details>

The structure is similar, the differences:

* JSON array allows you to have mixed types, so
    - Tuples are represented using arrays in JSON. (mixed types are invalid in TOML, so we need to use a TOML table)
    - Array of enum variants is valid in JSON: `["Plain", { "Tuple": [ -1, false ] }]`
* In TOML it would be ergonomic to represent plain enum variants as `"String"`, but represented as a TOML table when used in an array

I'll work on getting serialization to work that way, since it's the closest we can get to JSON
azriel91: I've gotten deserialization matching the above (and tested! yay). For serialization, the tuple variant currently emits a toml array, which is invalid if you have a tuple variant with different types.

I'm not sure whether it's okay to break backwards compatibility and emit a table -- should I:

* **A:** Try and be clever and emit an array if the tuple fields are the same type, and a table if they are different types?
* **B:** Make it always emit an inline table

azriel91: I'll push serialization to a separate branch &ndash; I haven't managed to get it *correct*, let alone clean. In case you're happy with having deserialization without serialization as a partial state.
alexcrichton: Oh dear sorry about that @Aaronepower! I'm somewhat low on reviewing bandwidth for this at the moment, so some assistance in reviewing this would be greatly appreciated!
Aaronepower: @alexcrichton Sure no problem!

@azriel91 There seem to be a lot of unrelated style changes to the codebase. (Adding semicolons, changes the formatting of arguments, extra commas, etc.) Are you perhaps using rustfmt? I think removing those tidy ups would make it easier to see the changes. For example there's 940 changes in `src/de.rs`, but as far as I can tell there's actually 144 lines that aren't tidying up the code.
azriel91: Yeap, the first commit is purely rustfmt, so the relevant diff would be the ones after that.
I can push over this without that if you prefer
Aaronepower: @azriel91 If you could that would be very helpful. I've found that trying to review through individual commits is a lot of work as you have to keep track of the code from previous commits as well new code.

Patch:
diff --git a/examples/enum_external.rs b/examples/enum_external.rs
new file mode 100644
index 0000000..e2ac4f8
--- /dev/null
+++ b/examples/enum_external.rs
@@ -0,0 +1,46 @@
+//! An example showing off the usage of `Deserialize` to automatically decode
+//! TOML into a Rust `struct`, with enums.
+
+#![deny(warnings)]
+
+extern crate toml;
+#[macro_use]
+extern crate serde_derive;
+
+/// This is what we're going to decode into.
+#[derive(Debug, Deserialize)]
+struct Config {
+    plain: MyEnum,
+    plain_table: MyEnum,
+    tuple: MyEnum,
+    #[serde(rename = "struct")]
+    structv: MyEnum,
+    newtype: MyEnum,
+    my_enum: Vec<MyEnum>,
+}
+
+#[derive(Debug, Deserialize)]
+enum MyEnum {
+    Plain,
+    Tuple(i64, bool),
+    NewType(String),
+    Struct { value: i64 },
+}
+
+fn main() {
+    let toml_str = r#"
+    plain = "Plain"
+    plain_table = { Plain = {} }
+    tuple = { Tuple = { 0 = 123, 1 = true } }
+    struct = { Struct = { value = 123 } }
+    newtype = { NewType = "value" }
+    my_enum = [
+        { Plain = {} },
+        { Tuple = { 0 = 123, 1 = true } },
+        { NewType = "value" },
+        { Struct = { value = 123 } }
+    ]"#;
+
+    let decoded: Config = toml::from_str(toml_str).unwrap();
+    println!("{:#?}", decoded);
+}
diff --git a/src/de.rs b/src/de.rs
index 491ae47..9b58606 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -159,12 +159,35 @@ enum ErrorKind {
     /// type.
     Custom,
 
-    /// A struct was expected but something else was found
-    ExpectedString,
+    /// A tuple with a certain number of elements was expected but something
+    /// else was found.
+    ExpectedTuple(usize),
+
+    /// Expected table keys to be in increasing tuple index order, but something
+    /// else was found.
+    ExpectedTupleIndex {
+        /// Expected index.
+        expected: usize,
+        /// Key that was specified.
+        found: String,
+    },
+
+    /// An empty table was expected but entries were found
+    ExpectedEmptyTable,
 
     /// Dotted key attempted to extend something that is not a table.
     DottedKeyInvalidType,
 
+    /// An unexpected key was encountered.
+    ///
+    /// Used when deserializing a struct with a limited set of fields.
+    UnexpectedKeys {
+        /// The unexpected keys.
+        keys: Vec<String>,
+        /// Keys that may be specified.
+        available: &'static [&'static str],
+    },
+
     #[doc(hidden)]
     __Nonexhaustive,
 }
@@ -182,47 +205,8 @@ impl<'de, 'b> de::Deserializer<'de> for &'b mut Deserializer<'de> {
     fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
         where V: de::Visitor<'de>,
     {
-        let mut tables = Vec::new();
-        let mut cur_table = Table {
-            at: 0,
-            header: Vec::new(),
-            values: None,
-            array: false,
-        };
 
-        while let Some(line) = self.line()? {
-            match line {
-                Line::Table { at, mut header, array } => {
-                    if !cur_table.header.is_empty() || cur_table.values.is_some() {
-                        tables.push(cur_table);
-                    }
-                    cur_table = Table {
-                        at: at,
-                        header: Vec::new(),
-                        values: Some(Vec::new()),
-                        array: array,
-                    };
-                    loop {
-                        let part = header.next().map_err(|e| {
-                            self.token_error(e)
-                        });
-                        match part? {
-                            Some(part) => cur_table.header.push(part),
-                            None => break,
-                        }
-                    }
-                }
-                Line::KeyValue(key, value) => {
-                    if cur_table.values.is_none() {
-                        cur_table.values = Some(Vec::new());
-                    }
-                    self.add_dotted_key(key, value, cur_table.values.as_mut().unwrap())?;
-                }
-            }
-        }
-        if !cur_table.header.is_empty() || cur_table.values.is_some() {
-            tables.push(cur_table);
-        }
+        let mut tables = self.tables()?;
 
         visitor.visit_map(MapVisitor {
             values: Vec::new().into_iter(),
@@ -237,6 +221,7 @@ impl<'de, 'b> de::Deserializer<'de> for &'b mut Deserializer<'de> {
         })
     }
 
+    // Called when the type to deserialize is an enum, as opposed to a field in the type.
     fn deserialize_enum<V>(
         self,
         _name: &'static str,
@@ -245,15 +230,34 @@ impl<'de, 'b> de::Deserializer<'de> for &'b mut Deserializer<'de> {
     ) -> 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))
+        let (value, name) = self.string_or_table()?;
+        match value.e {
+            E::String(val) => visitor.visit_enum(val.into_deserializer()),
+            E::InlineTable(values) => {
+                if values.len() != 1 {
+                    Err(Error::from_kind(ErrorKind::Wanted {
+                        expected: "exactly 1 element",
+                        found: if values.is_empty() {
+                            "zero elements"
+                        } else {
+                            "more than 1 element"
+                        },
+                    }))
+                } else {
+                    visitor.visit_enum(InlineTableDeserializer {
+                        values: values.into_iter(),
+                        next_value: None,
+                    })
+                }
             }
-        } else {
-            Err(Error::from_kind(ErrorKind::UnexpectedEof))
+            E::DottedTable(_) => visitor.visit_enum(DottedTableDeserializer {
+                name: name.expect("Expected table header to be passed."),
+                value: value,
+            }),
+            e @ _ => Err(Error::from_kind(ErrorKind::Wanted {
+                expected: "string or table",
+                found: e.type_name(),
+            })),
         }
     }
 
@@ -497,14 +501,21 @@ impl<'de> de::Deserializer<'de> for StrDeserializer<'de> {
 
 struct ValueDeserializer<'a> {
     value: Value<'a>,
+    validate_struct_keys: bool,
 }
 
 impl<'a> ValueDeserializer<'a> {
     fn new(value: Value<'a>) -> ValueDeserializer<'a> {
         ValueDeserializer {
             value: value,
+            validate_struct_keys: false,
         }
     }
+
+    fn with_struct_key_validation(mut self) -> Self {
+        self.validate_struct_keys = true;
+        self
+    }
 }
 
 impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> {
@@ -553,6 +564,31 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> {
             }
         }
 
+        if self.validate_struct_keys {
+            match &self.value.e {
+                &E::InlineTable(ref values) | &E::DottedTable(ref values) => {
+                    let extra_fields = values.iter()
+                        .filter_map(|key_value| {
+                            let (ref key, ref _val) = *key_value;
+                            if !fields.contains(&&(**key)) {
+                                Some(key.clone())
+                            } else {
+                                None
+                            }
+                        })
+                        .collect::<Vec<Cow<'de, str>>>();
+
+                    if !extra_fields.is_empty() {
+                        return Err(Error::from_kind(ErrorKind::UnexpectedKeys {
+                            keys: extra_fields.iter().map(|k| k.to_string()).collect::<Vec<_>>(),
+                            available: fields,
+                        }));
+                    }
+                }
+                _ => {}
+            }
+        }
+
         if name == spanned::NAME && fields == &[spanned::START, spanned::END, spanned::VALUE] {
             let start = self.value.start;
             let end = self.value.end;
@@ -585,7 +621,27 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> {
     {
         match self.value.e {
             E::String(val) => visitor.visit_enum(val.into_deserializer()),
-            _ => Err(Error::from_kind(ErrorKind::ExpectedString))
+            E::InlineTable(values) => {
+                if values.len() != 1 {
+                    Err(Error::from_kind(ErrorKind::Wanted {
+                        expected: "exactly 1 element",
+                        found: if values.is_empty() {
+                            "zero elements"
+                        } else {
+                            "more than 1 element"
+                        },
+                    }))
+                } else {
+                    visitor.visit_enum(InlineTableDeserializer {
+                        values: values.into_iter(),
+                        next_value: None,
+                    })
+                }
+            }
+            e @ _ => Err(Error::from_kind(ErrorKind::Wanted {
+                expected: "string or inline table",
+                found: e.type_name(),
+            })),
         }
     }
 
@@ -697,6 +753,25 @@ impl<'de> de::Deserializer<'de> for DatetimeFieldDeserializer {
     }
 }
 
+struct DottedTableDeserializer<'a> {
+    name: Cow<'a, str>,
+    value: Value<'a>,
+}
+
+impl<'de> de::EnumAccess<'de> for DottedTableDeserializer<'de> {
+    type Error = Error;
+    type Variant = TableEnumDeserializer<'de>;
+
+    fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
+    where
+        V: de::DeserializeSeed<'de>,
+    {
+        let (name, value) = (self.name, self.value);
+        seed.deserialize(StrDeserializer::new(name))
+            .map(|val| (val, TableEnumDeserializer { value: value }))
+    }
+}
+
 struct InlineTableDeserializer<'a> {
     values: vec::IntoIter<(Cow<'a, str>, Value<'a>)>,
     next_value: Option<Value<'a>>,
@@ -724,6 +799,124 @@ impl<'de> de::MapAccess<'de> for InlineTableDeserializer<'de> {
     }
 }
 
+impl<'de> de::EnumAccess<'de> for InlineTableDeserializer<'de> {
+    type Error = Error;
+    type Variant = TableEnumDeserializer<'de>;
+
+    fn variant_seed<V>(mut self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
+    where
+        V: de::DeserializeSeed<'de>,
+    {
+        let (key, value) = match self.values.next() {
+            Some(pair) => pair,
+            None => {
+                return Err(Error::from_kind(ErrorKind::Wanted {
+                    expected: "table with exactly 1 entry",
+                    found: "empty table",
+                }))
+            }
+        };
+
+        seed.deserialize(StrDeserializer::new(key))
+            .map(|val| (val, TableEnumDeserializer { value: value }))
+    }
+}
+
+/// Deserializes table values into enum variants.
+struct TableEnumDeserializer<'a> {
+    value: Value<'a>,
+}
+
+impl<'de> de::VariantAccess<'de> for TableEnumDeserializer<'de> {
+    type Error = Error;
+
+    fn unit_variant(self) -> Result<(), Self::Error> {
+        match self.value.e {
+            E::InlineTable(values) | E::DottedTable(values) => {
+                if values.len() == 0 {
+                    Ok(())
+                } else {
+                    Err(Error::from_kind(ErrorKind::ExpectedEmptyTable))
+                }
+            }
+            e @ _ => Err(Error::from_kind(ErrorKind::Wanted {
+                expected: "table",
+                found: e.type_name(),
+            })),
+        }
+    }
+
+    fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error>
+    where
+        T: de::DeserializeSeed<'de>,
+    {
+        seed.deserialize(ValueDeserializer::new(self.value))
+    }
+
+    fn tuple_variant<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        match self.value.e {
+            E::InlineTable(values) | E::DottedTable(values) => {
+                let tuple_values = values
+                    .into_iter()
+                    .enumerate()
+                    .map(|(index, (key, value))| match key.parse::<usize>() {
+                        Ok(key_index) if key_index == index => Ok(value),
+                        Ok(_) | Err(_) => Err(Error::from_kind(ErrorKind::ExpectedTupleIndex {
+                            expected: index,
+                            found: key.to_string(),
+                        })),
+                    })
+                    // Fold all values into a `Vec`, or return the first error.
+                    .fold(Ok(Vec::with_capacity(len)), |result, value_result| {
+                        result.and_then(move |mut tuple_values| match value_result {
+                            Ok(value) => {
+                                tuple_values.push(value);
+                                Ok(tuple_values)
+                            }
+                            // `Result<de::Value, Self::Error>` to `Result<Vec<_>, Self::Error>`
+                            Err(e) => Err(e),
+                        })
+                    })?;
+
+                if tuple_values.len() == len {
+                    de::Deserializer::deserialize_seq(
+                        ValueDeserializer::new(Value {
+                            e: E::Array(tuple_values),
+                            start: self.value.start,
+                            end: self.value.end,
+                        }),
+                        visitor,
+                    )
+                } else {
+                    Err(Error::from_kind(ErrorKind::ExpectedTuple(len)))
+                }
+            }
+            e @ _ => Err(Error::from_kind(ErrorKind::Wanted {
+                expected: "table",
+                found: e.type_name(),
+            })),
+        }
+    }
+
+    fn struct_variant<V>(
+        self,
+        fields: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        de::Deserializer::deserialize_struct(
+            ValueDeserializer::new(self.value).with_struct_key_validation(),
+            "", // TODO: this should be the variant name
+            fields,
+            visitor,
+        )
+    }
+}
 
 impl<'a> Deserializer<'a> {
     /// Creates a new deserializer which will be deserializing the string
@@ -754,6 +947,53 @@ impl<'a> Deserializer<'a> {
         self.require_newline_after_table = require;
     }
 
+    fn tables(&mut self) -> Result<Vec<Table<'a>>, Error> {
+        let mut tables = Vec::new();
+        let mut cur_table = Table {
+            at: 0,
+            header: Vec::new(),
+            values: None,
+            array: false,
+        };
+
+        while let Some(line) = self.line()? {
+            match line {
+                Line::Table {
+                    at,
+                    mut header,
+                    array,
+                } => {
+                    if !cur_table.header.is_empty() || cur_table.values.is_some() {
+                        tables.push(cur_table);
+                    }
+                    cur_table = Table {
+                        at: at,
+                        header: Vec::new(),
+                        values: Some(Vec::new()),
+                        array: array,
+                    };
+                    loop {
+                        let part = header.next().map_err(|e| self.token_error(e));
+                        match part? {
+                            Some(part) => cur_table.header.push(part),
+                            None => break,
+                        }
+                    }
+                }
+                Line::KeyValue(key, value) => {
+                    if cur_table.values.is_none() {
+                        cur_table.values = Some(Vec::new());
+                    }
+                    self.add_dotted_key(key, value, cur_table.values.as_mut().unwrap())?;
+                }
+            }
+        }
+        if !cur_table.header.is_empty() || cur_table.values.is_some() {
+            tables.push(cur_table);
+        }
+        Ok(tables)
+    }
+
     fn line(&mut self) -> Result<Option<Line<'a>>, Error> {
         loop {
             self.eat_whitespace()?;
@@ -876,7 +1116,56 @@ impl<'a> Deserializer<'a> {
         }
     }
 
-    fn number(&mut self, Span { start, end}: Span, s: &'a str) -> Result<Value<'a>, Error> {
+    /// Returns a string or table value type.
+    ///
+    /// Used to deserialize enums. Unit enums may be represented as a string or a table, all other
+    /// structures (tuple, newtype, struct) must be represented as a table.
+    fn string_or_table(&mut self) -> Result<(Value<'a>, Option<Cow<'a, str>>), Error> {
+        match self.peek()? {
+            Some((_, Token::LeftBracket)) => {
+                let tables = self.tables()?;
+                if tables.len() != 1 {
+                    return Err(Error::from_kind(ErrorKind::Wanted {
+                        expected: "exactly 1 table",
+                        found: if tables.is_empty() {
+                            "zero tables"
+                        } else {
+                            "more than 1 table"
+                        },
+                    }));
+                }
+
+                let table = tables
+                    .into_iter()
+                    .next()
+                    .expect("Expected exactly one table");
+                let header = table
+                    .header
+                    .last()
+                    .expect("Expected at least one header value for table.");
+
+                let start = table.at;
+                let end = table
+                    .values
+                    .as_ref()
+                    .and_then(|values| values.last())
+                    .map(|&(_, ref val)| val.end)
+                    .unwrap_or_else(|| header.len());
+                Ok((
+                    Value {
+                        e: E::DottedTable(table.values.unwrap_or_else(Vec::new)),
+                        start: start,
+                        end: end,
+                    },
+                    Some(header.clone()),
+                ))
+            }
+            Some(_) => self.value().map(|val| (val, None)),
+            None => Err(self.eof()),
+        }
+    }
+
+    fn number(&mut self, Span { start, end }: Span, s: &'a str) -> Result<Value<'a>, Error> {
         let to_integer = |f| Value { e: E::Integer(f), start: start, end: end };
         if s.starts_with("0x") {
             self.integer(&s[2..], 16).map(to_integer)
@@ -1171,6 +1460,18 @@ impl<'a> Deserializer<'a> {
         Ok(result)
     }
 
+    /// Stores a value in the appropriate hierachical structure positioned based on the dotted key.
+    ///
+    /// Given the following definition: `multi.part.key = "value"`, `multi` and `part` are
+    /// intermediate parts which are mapped to the relevant fields in the deserialized type's data
+    /// hierarchy.
+    ///
+    /// # Parameters
+    ///
+    /// * `key_parts`: Each segment of the dotted key, e.g. `part.one` maps to
+    ///                `vec![Cow::Borrowed("part"), Cow::Borrowed("one")].`
+    /// * `value`: The parsed value.
+    /// * `values`: The `Vec` to store the value in.
     fn add_dotted_key(
         &self,
         mut key_parts: Vec<Cow<'a, str>>,
@@ -1192,12 +1493,12 @@ impl<'a> Deserializer<'a> {
             None => {}
         }
         // The start/end value is somewhat misleading here.
-        let inline_table = Value {
+        let table_values = Value {
             e: E::DottedTable(Vec::new()),
             start: value.start,
             end: value.end,
         };
-        values.push((key, inline_table));
+        values.push((key, table_values));
         let last_i = values.len() - 1;
         if let (_, Value { e: E::DottedTable(ref mut v), .. }) = values[last_i] {
             self.add_dotted_key(key_parts, value, v)?;
@@ -1385,8 +1686,23 @@ impl fmt::Display for Error {
             ErrorKind::EmptyTableKey => "empty table key found".fmt(f)?,
             ErrorKind::MultilineStringKey => "multiline strings are not allowed for key".fmt(f)?,
             ErrorKind::Custom => self.inner.message.fmt(f)?,
-            ErrorKind::ExpectedString => "expected string".fmt(f)?,
-            ErrorKind::DottedKeyInvalidType => "dotted key attempted to extend non-table type".fmt(f)?,
+            ErrorKind::ExpectedTuple(l) => write!(f, "expected table with length {}", l)?,
+            ErrorKind::ExpectedTupleIndex {
+                expected,
+                ref found,
+            } => write!(f, "expected table key `{}`, but was `{}`", expected, found)?,
+            ErrorKind::ExpectedEmptyTable => "expected empty table".fmt(f)?,
+            ErrorKind::DottedKeyInvalidType => {
+                "dotted key attempted to extend non-table type".fmt(f)?
+            }
+            ErrorKind::UnexpectedKeys { ref keys, available } => {
+                write!(
+                    f,
+                    "unexpected keys in table: `{:?}`, available keys: `{:?}`",
+                    keys,
+                    available
+                )?
+            }
             ErrorKind::__Nonexhaustive => panic!(),
         }
 
@@ -1430,8 +1746,11 @@ impl error::Error for Error {
             ErrorKind::EmptyTableKey => "empty table key found",
             ErrorKind::MultilineStringKey => "invalid multiline string for key",
             ErrorKind::Custom => "a custom error",
-            ErrorKind::ExpectedString => "expected string",
+            ErrorKind::ExpectedTuple(_) => "expected table length",
+            ErrorKind::ExpectedTupleIndex { .. } => "expected table key",
+            ErrorKind::ExpectedEmptyTable => "expected empty table",
             ErrorKind::DottedKeyInvalidType => "dotted key invalid type",
+            ErrorKind::UnexpectedKeys { .. } => "unexpected keys in table",
             ErrorKind::__Nonexhaustive => panic!(),
         }
     }
@@ -1510,6 +1829,21 @@ enum E<'a> {
     DottedTable(Vec<(Cow<'a, str>, Value<'a>)>),
 }
 
+impl<'a> E<'a> {
+    fn type_name(&self) -> &'static str {
+        match *self {
+            E::String(..) => "string",
+            E::Integer(..) => "integer",
+            E::Float(..) => "float",
+            E::Boolean(..) => "boolean",
+            E::Datetime(..) => "datetime",
+            E::Array(..) => "array",
+            E::InlineTable(..) => "inline table",
+            E::DottedTable(..) => "dotted table",
+        }
+    }
+}
+
 impl<'a> Value<'a> {
     fn same_type(&self, other: &Value<'a>) -> bool {
         match (&self.e, &other.e) {
diff --git a/src/ser.rs b/src/ser.rs
index e1fe1a2..9f36f59 100644
--- a/src/ser.rs
+++ b/src/ser.rs
@@ -1420,11 +1420,11 @@ impl ser::Serializer for StringExtractor {
         Err(Error::KeyNotString)
     }
 
-    fn serialize_newtype_struct<T: ?Sized>(self, _name: &'static str, value: &T)
+    fn serialize_newtype_struct<T: ?Sized>(self, _name: &'static str, _value: &T)
                                            -> Result<String, Self::Error>
         where T: ser::Serialize,
     {
-        value.serialize(self)
+        Err(Error::KeyNotString)
     }
 
     fn serialize_newtype_variant<T: ?Sized>(self,
diff --git a/src/tokens.rs b/src/tokens.rs
index 382c1ec..15c3b41 100644
--- a/src/tokens.rs
+++ b/src/tokens.rs
@@ -364,24 +364,7 @@ impl<'a> Tokenizer<'a> {
                             let len = if c == 'u' {4} else {8};
                             val.push(me.hex(start, i, len)?);
                         }
-                        Some((i, c @ ' ')) |
-                        Some((i, c @ '\t')) |
-                        Some((i, c @ '\n')) if multi => {
-                            if c != '\n' {
-                                while let Some((_, ch)) = me.chars.clone().next() {
-                                    match ch {
-                                        ' ' | '\t' => {
-                                            me.chars.next();
-                                            continue
-                                        },
-                                        '\n' => {
-                                            me.chars.next();
-                                            break
-                                        },
-                                        _ => return Err(Error::InvalidEscape(i, c)),
-                                    }
-                                }
-                            }
+                        Some((_, '\n')) if multi => {
                             while let Some((_, ch)) = me.chars.clone().next() {
                                 match ch {
                                     ' ' | '\t' | '\n' => {
diff --git a/src/value.rs b/src/value.rs
index 2e42dc3..64f4555 100644
--- a/src/value.rs
+++ b/src/value.rs
@@ -675,9 +675,9 @@ impl ser::Serializer for Serializer {
     type Error = ::ser::Error;
 
     type SerializeSeq = SerializeVec;
-    type SerializeTuple = SerializeVec;
-    type SerializeTupleStruct = SerializeVec;
-    type SerializeTupleVariant = SerializeVec;
+    type SerializeTuple = ser::Impossible<Value, ::ser::Error>;
+    type SerializeTupleStruct = ser::Impossible<Value, ::ser::Error>;
+    type SerializeTupleVariant = ser::Impossible<Value, ::ser::Error>;
     type SerializeMap = SerializeMap;
     type SerializeStruct = SerializeMap;
     type SerializeStructVariant = ser::Impossible<Value, ::ser::Error>;
@@ -800,23 +800,23 @@ impl ser::Serializer for Serializer {
         })
     }
 
-    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, ::ser::Error> {
-        self.serialize_seq(Some(len))
+    fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, ::ser::Error> {
+        Err(::ser::Error::UnsupportedType)
     }
 
-    fn serialize_tuple_struct(self, _name: &'static str, len: usize)
+    fn serialize_tuple_struct(self, _name: &'static str, _len: usize)
                               -> Result<Self::SerializeTupleStruct, ::ser::Error> {
-        self.serialize_seq(Some(len))
+        Err(::ser::Error::UnsupportedType)
     }
 
     fn serialize_tuple_variant(self,
                                _name: &'static str,
                                _variant_index: u32,
                                _variant: &'static str,
-                               len: usize)
+                               _len: usize)
                                -> Result<Self::SerializeTupleVariant, ::ser::Error>
     {
-        self.serialize_seq(Some(len))
+        Err(::ser::Error::UnsupportedType)
     }
 
     fn serialize_map(self, _len: Option<usize>)
@@ -869,51 +869,6 @@ impl ser::SerializeSeq for SerializeVec {
     }
 }
 
-impl ser::SerializeTuple for SerializeVec {
-    type Ok = Value;
-    type Error = ::ser::Error;
-
-    fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), ::ser::Error>
-        where T: ser::Serialize
-    {
-        ser::SerializeSeq::serialize_element(self, value)
-    }
-
-    fn end(self) -> Result<Value, ::ser::Error> {
-        ser::SerializeSeq::end(self)
-    }
-}
-
-impl ser::SerializeTupleStruct for SerializeVec {
-    type Ok = Value;
-    type Error = ::ser::Error;
-
-    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), ::ser::Error>
-        where T: ser::Serialize
-    {
-        ser::SerializeSeq::serialize_element(self, value)
-    }
-
-    fn end(self) -> Result<Value, ::ser::Error> {
-        ser::SerializeSeq::end(self)
-    }
-}
-
-impl ser::SerializeTupleVariant for SerializeVec {
-    type Ok = Value;
-    type Error = ::ser::Error;
-
-    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), ::ser::Error>
-        where T: ser::Serialize
-    {
-        ser::SerializeSeq::serialize_element(self, value)
-    }
-
-    fn end(self) -> Result<Value, ::ser::Error> {
-        ser::SerializeSeq::end(self)
-    }
-}
-
 impl ser::SerializeMap for SerializeMap {
     type Ok = Value;
     type Error = ::ser::Error;


Test patch:
diff --git a/test-suite/tests/invalid/string-bad-line-ending-escape.toml b/test-suite/tests/invalid/string-bad-line-ending-escape.toml
deleted file mode 100644
index 32e2c48..0000000
--- a/test-suite/tests/invalid/string-bad-line-ending-escape.toml
+++ /dev/null
@@ -1,3 +0,0 @@
-invalid-escape = """\
-    This string has a non whitespace-character after the line ending escape. \  a
-"""
diff --git a/test-suite/tests/serde.rs b/test-suite/tests/serde.rs
index c1b6d78..b97937f 100644
--- a/test-suite/tests/serde.rs
+++ b/test-suite/tests/serde.rs
@@ -580,63 +580,15 @@ fn table_structs_empty() {
 
 #[test]
 fn fixed_size_array() {
-    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
+    #[derive(Serialize, PartialEq, Eq, Deserialize, Debug)]
     struct Entity {
         pos: [i32; 2]
     }
-
-    equivalent! {
-        Entity { pos: [1, 2] },
-        Table(map! {
-            pos: Array(vec![
-                Integer(1),
-                Integer(2),
-            ])
-        }),
-    }
-}
-
-#[test]
-fn homogeneous_tuple() {
-    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
-    struct Collection {
-        elems: (i64, i64, i64),
-    }
-
-    equivalent! {
-        Collection { elems: (0, 1, 2) },
-        Table(map! {
-            elems: Array(vec![
-                Integer(0),
-                Integer(1),
-                Integer(2),
-            ])
-        }),
-    }
-}
-
-#[test]
-fn homogeneous_tuple_struct() {
-    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
-    struct Object(Vec<String>, Vec<String>, Vec<String>);
-
-    equivalent! {
-        map! {
-            obj: Object(vec!["foo".to_string()], vec![], vec!["bar".to_string(), "baz".to_string()])
-        },
-        Table(map! {
-            obj: Array(vec![
-                Array(vec![
-                    Value::String("foo".to_string()),
-                ]),
-                Array(vec![]),
-                Array(vec![
-                    Value::String("bar".to_string()),
-                    Value::String("baz".to_string()),
-                ]),
-            ])
-        }),
-    }
+    let text = "pos = [1, 2]\n";
+    let value: Entity = toml::from_str(text).unwrap();
+    let expected = Entity { pos: [1, 2] };
+    assert_eq!(value, expected);
+    assert_eq!(toml::to_string(&value).unwrap(), text);
 }
 
 #[test]
diff --git a/test-suite/tests/valid/multiline-string.json b/test-suite/tests/valid/multiline-string.json
index 3223bae..075bf50 100644
--- a/test-suite/tests/valid/multiline-string.json
+++ b/test-suite/tests/valid/multiline-string.json
@@ -15,10 +15,6 @@
         "type": "string",
         "value": ""
     },
-    "multiline_empty_five": {
-        "type": "string",
-        "value": ""
-    },
     "equivalent_one": {
         "type": "string",
         "value": "The quick brown fox jumps over the lazy dog."
@@ -30,9 +26,5 @@
     "equivalent_three": {
         "type": "string",
         "value": "The quick brown fox jumps over the lazy dog."
-    },
-    "equivalent_four": {
-        "type": "string",
-        "value": "The quick brown fox jumps over the lazy dog."
     }
 }
diff --git a/test-suite/tests/valid/multiline-string.toml b/test-suite/tests/valid/multiline-string.toml
index 2c4237f..15b1143 100644
--- a/test-suite/tests/valid/multiline-string.toml
+++ b/test-suite/tests/valid/multiline-string.toml
@@ -7,11 +7,6 @@ multiline_empty_four = """\
    \
    \
    """
-multiline_empty_five = """\
-   \ 
-   \	
-   \ 	
-   """
 
 equivalent_one = "The quick brown fox jumps over the lazy dog."
 equivalent_two = """
@@ -26,9 +21,3 @@ equivalent_three = """\
        fox jumps over \
        the lazy dog.\
        """
-
-equivalent_four = """\
-       The quick brown \ 
-       fox jumps over \				
-       the lazy dog.\	
-       """
diff --git a/tests/enum_external_deserialize.rs b/tests/enum_external_deserialize.rs
new file mode 100644
index 0000000..3c38580
--- /dev/null
+++ b/tests/enum_external_deserialize.rs
@@ -0,0 +1,238 @@
+#[macro_use]
+extern crate serde_derive;
+extern crate toml;
+
+#[derive(Debug, Deserialize, PartialEq)]
+enum TheEnum {
+    Plain,
+    Tuple(i64, bool),
+    NewType(String),
+    Struct { value: i64 },
+}
+
+#[derive(Debug, Deserialize, PartialEq)]
+struct Val {
+    val: TheEnum,
+}
+
+#[derive(Debug, Deserialize, PartialEq)]
+struct Multi {
+    enums: Vec<TheEnum>,
+}
+
+#[test]
+fn invalid_variant_returns_error_with_good_message_string() {
+    let error = toml::from_str::<TheEnum>("\"NonExistent\"").unwrap_err();
+
+    assert_eq!(
+        error.to_string(),
+        "unknown variant `NonExistent`, expected one of `Plain`, `Tuple`, `NewType`, `Struct`"
+    );
+}
+
+#[test]
+fn invalid_variant_returns_error_with_good_message_inline_table() {
+    let error = toml::from_str::<TheEnum>("{ NonExistent = {} }").unwrap_err();
+    assert_eq!(
+        error.to_string(),
+        "unknown variant `NonExistent`, expected one of `Plain`, `Tuple`, `NewType`, `Struct`"
+    );
+}
+
+#[test]
+fn extra_field_returns_expected_empty_table_error() {
+    let error = toml::from_str::<TheEnum>("{ Plain = { extra_field = 404 } }").unwrap_err();
+
+    assert_eq!(error.to_string(), "expected empty table");
+}
+
+#[test]
+fn extra_field_returns_expected_empty_table_error_struct_variant() {
+    let error = toml::from_str::<TheEnum>("{ Struct = { value = 123, extra_0 = 0, extra_1 = 1 } }")
+        .unwrap_err();
+
+    assert_eq!(
+        error.to_string(),
+        r#"unexpected keys in table: `["extra_0", "extra_1"]`, available keys: `["value"]`"#
+    );
+}
+
+mod enum_unit {
+    use super::*;
+
+    #[test]
+    fn from_str() {
+        assert_eq!(TheEnum::Plain, toml::from_str("\"Plain\"").unwrap());
+    }
+
+    #[test]
+    fn from_inline_table() {
+        assert_eq!(TheEnum::Plain, toml::from_str("{ Plain = {} }").unwrap());
+        assert_eq!(
+            Val {
+                val: TheEnum::Plain
+            },
+            toml::from_str("val = { Plain = {} }").unwrap()
+        );
+    }
+
+    #[test]
+    fn from_dotted_table() {
+        assert_eq!(TheEnum::Plain, toml::from_str("[Plain]\n").unwrap());
+    }
+}
+
+mod enum_tuple {
+    use super::*;
+
+    #[test]
+    fn from_inline_table() {
+        assert_eq!(
+            TheEnum::Tuple(-123, true),
+            toml::from_str("{ Tuple = { 0 = -123, 1 = true } }").unwrap()
+        );
+        assert_eq!(
+            Val {
+                val: TheEnum::Tuple(-123, true)
+            },
+            toml::from_str("val = { Tuple = { 0 = -123, 1 = true } }").unwrap()
+        );
+    }
+
+    #[test]
+    fn from_dotted_table() {
+        assert_eq!(
+            TheEnum::Tuple(-123, true),
+            toml::from_str(
+                r#"[Tuple]
+                0 = -123
+                1 = true
+                "#
+            )
+            .unwrap()
+        );
+    }
+}
+
+mod enum_newtype {
+    use super::*;
+
+    #[test]
+    fn from_inline_table() {
+        assert_eq!(
+            TheEnum::NewType("value".to_string()),
+            toml::from_str(r#"{ NewType = "value" }"#).unwrap()
+        );
+        assert_eq!(
+            Val {
+                val: TheEnum::NewType("value".to_string()),
+            },
+            toml::from_str(r#"val = { NewType = "value" }"#).unwrap()
+        );
+    }
+
+    #[test]
+    #[ignore = "Unimplemented: https://github.com/alexcrichton/toml-rs/pull/264#issuecomment-431707209"]
+    fn from_dotted_table() {
+        assert_eq!(
+            TheEnum::NewType("value".to_string()),
+            toml::from_str(r#"NewType = "value""#).unwrap()
+        );
+        assert_eq!(
+            Val {
+                val: TheEnum::NewType("value".to_string()),
+            },
+            toml::from_str(
+                r#"[val]
+                NewType = "value"
+                "#
+            )
+            .unwrap()
+        );
+    }
+}
+
+mod enum_struct {
+    use super::*;
+
+    #[test]
+    fn from_inline_table() {
+        assert_eq!(
+            TheEnum::Struct { value: -123 },
+            toml::from_str("{ Struct = { value = -123 } }").unwrap()
+        );
+        assert_eq!(
+            Val {
+                val: TheEnum::Struct { value: -123 }
+            },
+            toml::from_str("val = { Struct = { value = -123 } }").unwrap()
+        );
+    }
+
+    #[test]
+    fn from_dotted_table() {
+        assert_eq!(
+            TheEnum::Struct { value: -123 },
+            toml::from_str(
+                r#"[Struct]
+                value = -123
+                "#
+            )
+            .unwrap()
+        );
+    }
+}
+
+mod enum_array {
+    use super::*;
+
+    #[test]
+    fn from_inline_tables() {
+        let toml_str = r#"
+            enums = [
+                { Plain = {} },
+                { Tuple = { 0 = -123, 1 = true } },
+                { NewType = "value" },
+                { Struct = { value = -123 } }
+            ]"#;
+        assert_eq!(
+            Multi {
+                enums: vec![
+                    TheEnum::Plain,
+                    TheEnum::Tuple(-123, true),
+                    TheEnum::NewType("value".to_string()),
+                    TheEnum::Struct { value: -123 },
+                ]
+            },
+            toml::from_str(toml_str).unwrap()
+        );
+    }
+
+    #[test]
+    #[ignore = "Unimplemented: https://github.com/alexcrichton/toml-rs/pull/264#issuecomment-431707209"]
+    fn from_dotted_table() {
+        let toml_str = r#"[[enums]]
+            Plain = {}
+
+            [[enums]]
+            Tuple = { 0 = -123, 1 = true }
+
+            [[enums]]
+            NewType = "value"
+
+            [[enums]]
+            Struct = { value = -123 }
+            "#;
+        assert_eq!(
+            Multi {
+                enums: vec![
+                    TheEnum::Plain,
+                    TheEnum::Tuple(-123, true),
+                    TheEnum::NewType("value".to_string()),
+                    TheEnum::Struct { value: -123 },
+                ]
+            },
+            toml::from_str(toml_str).unwrap()
+        );
+    }
+}