diff options
-rw-r--r-- | src/de.rs | 246 | ||||
-rw-r--r-- | tests/enum_external_deserialize.rs | 153 |
2 files changed, 328 insertions, 71 deletions
@@ -159,9 +159,6 @@ 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), @@ -198,47 +195,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(), @@ -253,6 +211,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, @@ -261,15 +220,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(), + })), } } @@ -601,7 +579,7 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> { { match self.value.e { E::String(val) => visitor.visit_enum(val.into_deserializer()), - E::InlineTable(values) | E::DottedTable(values) => { + E::InlineTable(values) => { if values.len() != 1 { Err(Error::from_kind(ErrorKind::Wanted { expected: "exactly 1 element", @@ -619,7 +597,7 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> { } } e @ _ => Err(Error::from_kind(ErrorKind::Wanted { - expected: "string or table", + expected: "string or inline table", found: e.type_name(), })), } @@ -733,6 +711,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>>, @@ -762,7 +759,7 @@ impl<'de> de::MapAccess<'de> for InlineTableDeserializer<'de> { impl<'de> de::EnumAccess<'de> for InlineTableDeserializer<'de> { type Error = Error; - type Variant = Self; + type Variant = TableEnumDeserializer<'de>; fn variant_seed<V>(mut self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error> where @@ -777,18 +774,22 @@ impl<'de> de::EnumAccess<'de> for InlineTableDeserializer<'de> { })) } }; - self.next_value = Some(value); seed.deserialize(StrDeserializer::new(key)) - .map(|val| (val, self)) + .map(|val| (val, TableEnumDeserializer { value: value })) } } -impl<'de> de::VariantAccess<'de> for InlineTableDeserializer<'de> { +/// 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.next_value.expect("Expected value").e { + match self.value.e { E::InlineTable(values) | E::DottedTable(values) => { if values.len() == 0 { Ok(()) @@ -807,17 +808,14 @@ impl<'de> de::VariantAccess<'de> for InlineTableDeserializer<'de> { where T: de::DeserializeSeed<'de>, { - seed.deserialize(ValueDeserializer::new( - self.next_value.expect("Expected value"), - )) + 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>, { - let next_value = self.next_value.expect("Expected value"); - match next_value.e { + match self.value.e { E::InlineTable(values) | E::DottedTable(values) => { let tuple_values = values .into_iter() @@ -845,8 +843,8 @@ impl<'de> de::VariantAccess<'de> for InlineTableDeserializer<'de> { de::Deserializer::deserialize_seq( ValueDeserializer::new(Value { e: E::Array(tuple_values), - start: next_value.start, - end: next_value.end, + start: self.value.start, + end: self.value.end, }), visitor, ) @@ -870,7 +868,7 @@ impl<'de> de::VariantAccess<'de> for InlineTableDeserializer<'de> { V: de::Visitor<'de>, { de::Deserializer::deserialize_struct( - ValueDeserializer::new(self.next_value.expect("Expected value")), + ValueDeserializer::new(self.value), "", // TODO: this should be the variant name fields, visitor, @@ -907,6 +905,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()?; @@ -1029,7 +1074,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) @@ -1324,6 +1418,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>>, @@ -1345,12 +1451,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)?; @@ -1538,7 +1644,6 @@ 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::ExpectedTuple(l) => write!(f, "expected table with length {}", l)?, ErrorKind::ExpectedTupleIndex { expected, @@ -1591,7 +1696,6 @@ 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", diff --git a/tests/enum_external_deserialize.rs b/tests/enum_external_deserialize.rs new file mode 100644 index 0000000..76e7a4c --- /dev/null +++ b/tests/enum_external_deserialize.rs @@ -0,0 +1,153 @@ +#[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>, +} + +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() + ); + } +} + +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() + ); + } +} |