diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/de.rs | 448 | 
1 files changed, 391 insertions, 57 deletions
| @@ -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) @@ -1170,6 +1459,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>>, @@ -1191,12 +1492,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)?; @@ -1384,8 +1685,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!(),          } @@ -1429,8 +1745,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!(),          }      } @@ -1509,6 +1828,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) { |