diff -Nru rust-toml-0.4.8/Cargo.toml rust-toml-0.4.10/Cargo.toml --- rust-toml-0.4.8/Cargo.toml 1970-01-01 00:00:00.000000000 +0000 +++ rust-toml-0.4.10/Cargo.toml 1970-01-01 00:00:00.000000000 +0000 @@ -12,7 +12,7 @@ [package] name = "toml" -version = "0.4.8" +version = "0.4.10" authors = ["Alex Crichton "] description = "A native Rust encoder and decoder of TOML-formatted files and streams. Provides\nimplementations of the standard Serialize/Deserialize traits for TOML data to\nfacilitate deserializing and serializing Rust structures.\n" homepage = "https://github.com/alexcrichton/toml-rs" diff -Nru rust-toml-0.4.8/Cargo.toml.orig rust-toml-0.4.10/Cargo.toml.orig --- rust-toml-0.4.8/Cargo.toml.orig 2018-10-01 17:05:54.000000000 +0000 +++ rust-toml-0.4.10/Cargo.toml.orig 2018-12-06 18:59:46.000000000 +0000 @@ -1,6 +1,6 @@ [package] name = "toml" -version = "0.4.8" +version = "0.4.10" authors = ["Alex Crichton "] license = "MIT/Apache-2.0" readme = "README.md" diff -Nru rust-toml-0.4.8/.cargo_vcs_info.json rust-toml-0.4.10/.cargo_vcs_info.json --- rust-toml-0.4.8/.cargo_vcs_info.json 1970-01-01 00:00:00.000000000 +0000 +++ rust-toml-0.4.10/.cargo_vcs_info.json 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,5 @@ +{ + "git": { + "sha1": "1ef180d06ed4ec207c41b2595feaca84959e709e" + } +} diff -Nru rust-toml-0.4.8/debian/cargo-checksum.json rust-toml-0.4.10/debian/cargo-checksum.json --- rust-toml-0.4.8/debian/cargo-checksum.json 2018-10-04 09:49:40.000000000 +0000 +++ rust-toml-0.4.10/debian/cargo-checksum.json 2018-12-27 11:33:06.000000000 +0000 @@ -1 +1 @@ -{"package":"4a2ecc31b0351ea18b3fe11274b8db6e4d82bce861bbb22e6dbed40417902c65","files":{}} +{"package":"758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f","files":{}} diff -Nru rust-toml-0.4.8/debian/changelog rust-toml-0.4.10/debian/changelog --- rust-toml-0.4.8/debian/changelog 2018-10-04 09:49:40.000000000 +0000 +++ rust-toml-0.4.10/debian/changelog 2018-12-27 11:33:06.000000000 +0000 @@ -1,3 +1,10 @@ +rust-toml (0.4.10-1) unstable; urgency=medium + + * Team upload. + * Package toml 0.4.10 from crates.io using debcargo 2.2.9 + + -- Ximin Luo Thu, 27 Dec 2018 03:33:06 -0800 + rust-toml (0.4.8-1) unstable; urgency=medium * Team upload. diff -Nru rust-toml-0.4.8/debian/control rust-toml-0.4.10/debian/control --- rust-toml-0.4.8/debian/control 2018-10-04 09:49:40.000000000 +0000 +++ rust-toml-0.4.10/debian/control 2018-12-27 11:33:06.000000000 +0000 @@ -27,8 +27,8 @@ librust-toml-0+default-dev (= ${binary:Version}), librust-toml-0.4-dev (= ${binary:Version}), librust-toml-0.4+default-dev (= ${binary:Version}), - librust-toml-0.4.8-dev (= ${binary:Version}), - librust-toml-0.4.8+default-dev (= ${binary:Version}) + librust-toml-0.4.10-dev (= ${binary:Version}), + librust-toml-0.4.10+default-dev (= ${binary:Version}) Description: Native encoder and decoder of TOML-formatted data - Rust source code Provides implementations of the standard Serialize/Deserialize traits for TOML data to facilitate deserializing and serializing Rust structures. diff -Nru rust-toml-0.4.8/examples/enum_external.rs rust-toml-0.4.10/examples/enum_external.rs --- rust-toml-0.4.8/examples/enum_external.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-toml-0.4.10/examples/enum_external.rs 2018-12-06 18:59:42.000000000 +0000 @@ -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, +} + +#[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 -Nru rust-toml-0.4.8/README.md rust-toml-0.4.10/README.md --- rust-toml-0.4.8/README.md 2018-04-04 14:56:15.000000000 +0000 +++ rust-toml-0.4.10/README.md 2018-11-21 17:55:36.000000000 +0000 @@ -6,7 +6,7 @@ [![Documentation](https://docs.rs/toml/badge.svg)](https://docs.rs/toml) A [TOML][toml] decoder and encoder for Rust. This library is currently compliant -with the v0.4.0 version of TOML. This library will also likely continue to stay +with the v0.5.0 version of TOML. This library will also likely continue to stay up to date with the TOML specification as changes happen. [toml]: https://github.com/toml-lang/toml diff -Nru rust-toml-0.4.8/src/de.rs rust-toml-0.4.10/src/de.rs --- rust-toml-0.4.8/src/de.rs 2018-09-25 17:08:59.000000000 +0000 +++ rust-toml-0.4.10/src/de.rs 2018-12-06 18:59:42.000000000 +0000 @@ -159,12 +159,35 @@ /// 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, + /// Keys that may be specified. + available: &'static [&'static str], + }, + #[doc(hidden)] __Nonexhaustive, } @@ -182,47 +205,8 @@ fn deserialize_any(self, visitor: V) -> Result 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 @@ }) } + // Called when the type to deserialize is an enum, as opposed to a field in the type. fn deserialize_enum( self, _name: &'static str, @@ -245,15 +230,34 @@ ) -> Result 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 @@ 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 @@ } } + 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::>>(); + + if !extra_fields.is_empty() { + return Err(Error::from_kind(ErrorKind::UnexpectedKeys { + keys: extra_fields.iter().map(|k| k.to_string()).collect::>(), + 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 @@ { 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 @@ } } +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(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>, @@ -724,6 +799,124 @@ } } +impl<'de> de::EnumAccess<'de> for InlineTableDeserializer<'de> { + type Error = Error; + type Variant = TableEnumDeserializer<'de>; + + fn variant_seed(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(self, seed: T) -> Result + where + T: de::DeserializeSeed<'de>, + { + seed.deserialize(ValueDeserializer::new(self.value)) + } + + fn tuple_variant(self, len: usize, visitor: V) -> Result + 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::() { + 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` to `Result, 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( + self, + fields: &'static [&'static str], + visitor: V, + ) -> Result + 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 @@ self.require_newline_after_table = require; } + fn tables(&mut self) -> Result>, 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>, Error> { loop { self.eat_whitespace()?; @@ -876,7 +1116,56 @@ } } - fn number(&mut self, Span { start, end}: Span, s: &'a str) -> Result, 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>), 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, 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) @@ -1035,13 +1324,12 @@ let start = self.tokens.substr_offset(date); // Check for space separated date and time. - if let Some((_, Token::Whitespace(s))) = self.peek()? { - if s == " " { - self.next()?; - // Skip past the hour. - if let Some((_, Token::Keylike(_))) = self.peek()? { - self.next()?; - } + let mut lookahead = self.tokens.clone(); + if let Ok(Some((_, Token::Whitespace(" ")))) = lookahead.next() { + // Check if hour follows. + if let Ok(Some((_, Token::Keylike(_)))) = lookahead.next() { + self.next()?; // skip space + self.next()?; // skip keylike hour } } @@ -1171,6 +1459,18 @@ 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>, @@ -1192,12 +1492,12 @@ 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 +1685,23 @@ 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 +1745,11 @@ 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 +1828,21 @@ 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 -Nru rust-toml-0.4.8/src/lib.rs rust-toml-0.4.10/src/lib.rs --- rust-toml-0.4.8/src/lib.rs 2018-05-24 14:14:48.000000000 +0000 +++ rust-toml-0.4.10/src/lib.rs 2018-11-21 17:55:36.000000000 +0000 @@ -1,6 +1,6 @@ //! A [TOML]-parsing library //! -//! This library implements a [TOML] v0.4.0 compatible parser, +//! This library implements a [TOML] v0.5.0 compatible parser, //! primarily supporting the [`serde`] library for encoding/decoding //! various types in Rust. //! diff -Nru rust-toml-0.4.8/src/ser.rs rust-toml-0.4.10/src/ser.rs --- rust-toml-0.4.8/src/ser.rs 2018-10-01 17:05:50.000000000 +0000 +++ rust-toml-0.4.10/src/ser.rs 2018-11-21 17:55:36.000000000 +0000 @@ -1420,11 +1420,11 @@ Err(Error::KeyNotString) } - fn serialize_newtype_struct(self, _name: &'static str, _value: &T) + fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result where T: ser::Serialize, { - Err(Error::KeyNotString) + value.serialize(self) } fn serialize_newtype_variant(self, diff -Nru rust-toml-0.4.8/src/tokens.rs rust-toml-0.4.10/src/tokens.rs --- rust-toml-0.4.8/src/tokens.rs 2018-09-25 17:08:59.000000000 +0000 +++ rust-toml-0.4.10/src/tokens.rs 2018-11-21 17:55:36.000000000 +0000 @@ -364,7 +364,24 @@ let len = if c == 'u' {4} else {8}; val.push(me.hex(start, i, len)?); } - Some((_, '\n')) if multi => { + 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)), + } + } + } while let Some((_, ch)) = me.chars.clone().next() { match ch { ' ' | '\t' | '\n' => { diff -Nru rust-toml-0.4.8/src/value.rs rust-toml-0.4.10/src/value.rs --- rust-toml-0.4.8/src/value.rs 2018-09-25 17:08:59.000000000 +0000 +++ rust-toml-0.4.10/src/value.rs 2018-11-21 17:55:36.000000000 +0000 @@ -675,9 +675,9 @@ type Error = ::ser::Error; type SerializeSeq = SerializeVec; - type SerializeTuple = ser::Impossible; - type SerializeTupleStruct = ser::Impossible; - type SerializeTupleVariant = ser::Impossible; + type SerializeTuple = SerializeVec; + type SerializeTupleStruct = SerializeVec; + type SerializeTupleVariant = SerializeVec; type SerializeMap = SerializeMap; type SerializeStruct = SerializeMap; type SerializeStructVariant = ser::Impossible; @@ -800,23 +800,23 @@ }) } - fn serialize_tuple(self, _len: usize) -> Result { - Err(::ser::Error::UnsupportedType) + fn serialize_tuple(self, len: usize) -> Result { + self.serialize_seq(Some(len)) } - fn serialize_tuple_struct(self, _name: &'static str, _len: usize) + fn serialize_tuple_struct(self, _name: &'static str, len: usize) -> Result { - Err(::ser::Error::UnsupportedType) + self.serialize_seq(Some(len)) } fn serialize_tuple_variant(self, _name: &'static str, _variant_index: u32, _variant: &'static str, - _len: usize) + len: usize) -> Result { - Err(::ser::Error::UnsupportedType) + self.serialize_seq(Some(len)) } fn serialize_map(self, _len: Option) @@ -869,6 +869,51 @@ } } +impl ser::SerializeTuple for SerializeVec { + type Ok = Value; + type Error = ::ser::Error; + + fn serialize_element(&mut self, value: &T) -> Result<(), ::ser::Error> + where T: ser::Serialize + { + ser::SerializeSeq::serialize_element(self, value) + } + + fn end(self) -> Result { + ser::SerializeSeq::end(self) + } +} + +impl ser::SerializeTupleStruct for SerializeVec { + type Ok = Value; + type Error = ::ser::Error; + + fn serialize_field(&mut self, value: &T) -> Result<(), ::ser::Error> + where T: ser::Serialize + { + ser::SerializeSeq::serialize_element(self, value) + } + + fn end(self) -> Result { + ser::SerializeSeq::end(self) + } +} + +impl ser::SerializeTupleVariant for SerializeVec { + type Ok = Value; + type Error = ::ser::Error; + + fn serialize_field(&mut self, value: &T) -> Result<(), ::ser::Error> + where T: ser::Serialize + { + ser::SerializeSeq::serialize_element(self, value) + } + + fn end(self) -> Result { + ser::SerializeSeq::end(self) + } +} + impl ser::SerializeMap for SerializeMap { type Ok = Value; type Error = ::ser::Error; diff -Nru rust-toml-0.4.8/tests/enum_external_deserialize.rs rust-toml-0.4.10/tests/enum_external_deserialize.rs --- rust-toml-0.4.8/tests/enum_external_deserialize.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-toml-0.4.10/tests/enum_external_deserialize.rs 2018-12-06 18:59:42.000000000 +0000 @@ -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, +} + +#[test] +fn invalid_variant_returns_error_with_good_message_string() { + let error = toml::from_str::("\"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::("{ 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::("{ 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::("{ 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() + ); + } +}