From 60b874308e6792a73cc00517a60bbef60a12e3cc Mon Sep 17 00:00:00 2001 From: daubaris Date: Fri, 22 Nov 2019 18:28:29 +0200 Subject: Mixed type arrays (#358) * Added support of mixed-type arrays * Add tests cases * Replaced &'static str type for type_ and created a new enum instead * Restored ArrayMixedType --- src/de.rs | 19 ------------------ src/ser.rs | 68 ++++++++++++++++++++++++++++++++------------------------------ 2 files changed, 35 insertions(+), 52 deletions(-) (limited to 'src') diff --git a/src/de.rs b/src/de.rs index ae44f20..5a0d0c7 100644 --- a/src/de.rs +++ b/src/de.rs @@ -11,7 +11,6 @@ use std::f64; use std::fmt; use std::iter; use std::marker::PhantomData; -use std::mem::discriminant; use std::str; use std::vec; @@ -147,10 +146,6 @@ enum ErrorKind { found: &'static str, }, - /// An array was decoded but the types inside of it were mixed, which is - /// disallowed by TOML. - MixedArrayType, - /// A duplicate table definition was found. DuplicateTable(String), @@ -1827,13 +1822,7 @@ impl<'a> Deserializer<'a> { if let Some(span) = self.eat_spanned(Token::RightBracket)? { return Ok((span, ret)); } - let at = self.tokens.current(); let value = self.value()?; - if let Some(last) = ret.last() { - if !value.same_type(last) { - return Err(self.error(at, ErrorKind::MixedArrayType)); - } - } ret.push(value); intermediate(self)?; if !self.eat(Token::Comma)? { @@ -2118,7 +2107,6 @@ impl fmt::Display for Error { } ErrorKind::NumberInvalid => "invalid number".fmt(f)?, ErrorKind::DateInvalid => "invalid date".fmt(f)?, - ErrorKind::MixedArrayType => "mixed types in an array".fmt(f)?, ErrorKind::DuplicateTable(ref s) => { write!(f, "redefinition of table `{}`", s)?; } @@ -2180,7 +2168,6 @@ impl error::Error for Error { ErrorKind::Wanted { .. } => "expected a token but found another", ErrorKind::NumberInvalid => "invalid number", ErrorKind::DateInvalid => "invalid date", - ErrorKind::MixedArrayType => "mixed types in an array", ErrorKind::DuplicateTable(_) => "duplicate table", ErrorKind::RedefineAsArray => "table redefined as array", ErrorKind::EmptyTableKey => "empty table key found", @@ -2283,9 +2270,3 @@ impl<'a> E<'a> { } } } - -impl<'a> Value<'a> { - fn same_type(&self, other: &Value<'a>) -> bool { - discriminant(&self.e) == discriminant(&other.e) - } -} diff --git a/src/ser.rs b/src/ser.rs index 0734803..d81f75e 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -124,8 +124,8 @@ pub enum Error { #[doc(hidden)] KeyNewline, - /// Arrays in TOML must have a homogenous type, but a heterogeneous array - /// was emitted. + /// An array had to be homogenous, but now it is allowed to be heterogenous. + #[doc(hidden)] ArrayMixedType, /// All values in a TOML table must be emitted before further tables are @@ -201,6 +201,12 @@ pub struct Serializer<'a> { settings: Rc, } +#[derive(Debug, Copy, Clone)] +enum ArrayState { + Started, + StartedAsATable, +} + #[derive(Debug, Clone)] enum State<'a> { Table { @@ -212,7 +218,7 @@ enum State<'a> { Array { parent: &'a State<'a>, first: &'a Cell, - type_: &'a Cell>, + type_: &'a Cell>, len: Option, }, End, @@ -222,7 +228,7 @@ enum State<'a> { pub struct SerializeSeq<'a, 'b> { ser: &'b mut Serializer<'a>, first: Cell, - type_: Cell>, + type_: Cell>, len: Option, } @@ -417,7 +423,7 @@ impl<'a> Serializer<'a> { self } - fn display(&mut self, t: T, type_: &'static str) -> Result<(), Error> { + fn display(&mut self, t: T, type_: ArrayState) -> Result<(), Error> { self.emit_key(type_)?; write!(self.dst, "{}", t).map_err(ser::Error::custom)?; if let State::Table { .. } = self.state { @@ -426,7 +432,7 @@ impl<'a> Serializer<'a> { Ok(()) } - fn emit_key(&mut self, type_: &'static str) -> Result<(), Error> { + fn emit_key(&mut self, type_: ArrayState) -> Result<(), Error> { self.array_type(type_)?; let state = self.state.clone(); self._emit_key(&state) @@ -491,16 +497,12 @@ impl<'a> Serializer<'a> { Ok(()) } - fn array_type(&mut self, type_: &'static str) -> Result<(), Error> { + fn array_type(&mut self, type_: ArrayState) -> Result<(), Error> { let prev = match self.state { State::Array { type_, .. } => type_, _ => return Ok(()), }; - if let Some(prev) = prev.get() { - if prev != type_ { - return Err(Error::ArrayMixedType); - } - } else { + if let None = prev.get() { prev.set(Some(type_)); } Ok(()) @@ -747,7 +749,7 @@ impl<'a> Serializer<'a> { macro_rules! serialize_float { ($this:expr, $v:expr) => {{ - $this.emit_key("float")?; + $this.emit_key(ArrayState::Started)?; if ($v.is_nan() || $v == 0.0) && $v.is_sign_negative() { write!($this.dst, "-").map_err(ser::Error::custom)?; } @@ -778,39 +780,39 @@ impl<'a, 'b> ser::Serializer for &'b mut Serializer<'a> { type SerializeStructVariant = ser::Impossible<(), Error>; fn serialize_bool(self, v: bool) -> Result<(), Self::Error> { - self.display(v, "bool") + self.display(v, ArrayState::Started) } fn serialize_i8(self, v: i8) -> Result<(), Self::Error> { - self.display(v, "integer") + self.display(v, ArrayState::Started) } fn serialize_i16(self, v: i16) -> Result<(), Self::Error> { - self.display(v, "integer") + self.display(v, ArrayState::Started) } fn serialize_i32(self, v: i32) -> Result<(), Self::Error> { - self.display(v, "integer") + self.display(v, ArrayState::Started) } fn serialize_i64(self, v: i64) -> Result<(), Self::Error> { - self.display(v, "integer") + self.display(v, ArrayState::Started) } fn serialize_u8(self, v: u8) -> Result<(), Self::Error> { - self.display(v, "integer") + self.display(v, ArrayState::Started) } fn serialize_u16(self, v: u16) -> Result<(), Self::Error> { - self.display(v, "integer") + self.display(v, ArrayState::Started) } fn serialize_u32(self, v: u32) -> Result<(), Self::Error> { - self.display(v, "integer") + self.display(v, ArrayState::Started) } fn serialize_u64(self, v: u64) -> Result<(), Self::Error> { - self.display(v, "integer") + self.display(v, ArrayState::Started) } fn serialize_f32(self, v: f32) -> Result<(), Self::Error> { @@ -827,7 +829,7 @@ impl<'a, 'b> ser::Serializer for &'b mut Serializer<'a> { } fn serialize_str(self, value: &str) -> Result<(), Self::Error> { - self.emit_key("string")?; + self.emit_key(ArrayState::Started)?; self.emit_str(value, false)?; if let State::Table { .. } = self.state { self.dst.push_str("\n"); @@ -893,7 +895,7 @@ impl<'a, 'b> ser::Serializer for &'b mut Serializer<'a> { } fn serialize_seq(self, len: Option) -> Result { - self.array_type("array")?; + self.array_type(ArrayState::Started)?; Ok(SerializeSeq { ser: self, first: Cell::new(true), @@ -925,7 +927,7 @@ impl<'a, 'b> ser::Serializer for &'b mut Serializer<'a> { } fn serialize_map(self, _len: Option) -> Result { - self.array_type("table")?; + self.array_type(ArrayState::StartedAsATable)?; Ok(SerializeTable::Table { ser: self, key: String::new(), @@ -940,10 +942,10 @@ impl<'a, 'b> ser::Serializer for &'b mut Serializer<'a> { _len: usize, ) -> Result { if name == datetime::NAME { - self.array_type("datetime")?; + self.array_type(ArrayState::Started)?; Ok(SerializeTable::Datetime(self)) } else { - self.array_type("table")?; + self.array_type(ArrayState::StartedAsATable)?; Ok(SerializeTable::Table { ser: self, key: String::new(), @@ -988,8 +990,8 @@ impl<'a, 'b> ser::SerializeSeq for SerializeSeq<'a, 'b> { fn end(self) -> Result<(), Error> { match self.type_.get() { - Some("table") => return Ok(()), - Some(_) => match (self.len, &self.ser.settings.array) { + Some(ArrayState::StartedAsATable) => return Ok(()), + Some(ArrayState::Started) => match (self.len, &self.ser.settings.array) { (Some(0..=1), _) | (_, &None) => { self.ser.dst.push_str("]"); } @@ -1002,7 +1004,7 @@ impl<'a, 'b> ser::SerializeSeq for SerializeSeq<'a, 'b> { }, None => { assert!(self.first.get()); - self.ser.emit_key("array")?; + self.ser.emit_key(ArrayState::Started)?; self.ser.dst.push_str("[]") } } @@ -1244,7 +1246,7 @@ impl<'a, 'b> ser::Serializer for DateStrEmitter<'a, 'b> { } fn serialize_str(self, value: &str) -> Result<(), Self::Error> { - self.0.display(value, "datetime")?; + self.0.display(value, ArrayState::Started)?; Ok(()) } @@ -1528,13 +1530,13 @@ impl fmt::Display for Error { match *self { Error::UnsupportedType => "unsupported Rust type".fmt(f), Error::KeyNotString => "map key was not a string".fmt(f), - Error::ArrayMixedType => "arrays cannot have mixed types".fmt(f), Error::ValueAfterTable => "values must be emitted before tables".fmt(f), Error::DateInvalid => "a serialized date was invalid".fmt(f), Error::NumberInvalid => "a serialized number was invalid".fmt(f), Error::UnsupportedNone => "unsupported None value".fmt(f), Error::Custom(ref s) => s.fmt(f), Error::KeyNewline => unreachable!(), + Error::ArrayMixedType => unreachable!(), Error::__Nonexhaustive => panic!(), } } @@ -1545,13 +1547,13 @@ impl error::Error for Error { match *self { Error::UnsupportedType => "unsupported Rust type", Error::KeyNotString => "map key was not a string", - Error::ArrayMixedType => "arrays cannot have mixed types", Error::ValueAfterTable => "values must be emitted before tables", Error::DateInvalid => "a serialized date was invalid", Error::NumberInvalid => "a serialized number was invalid", Error::UnsupportedNone => "unsupported None value", Error::Custom(_) => "custom error", Error::KeyNewline => unreachable!(), + Error::ArrayMixedType => unreachable!(), Error::__Nonexhaustive => panic!(), } } -- cgit v1.2.3