aboutsummaryrefslogtreecommitdiff
path: root/src/de/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/de/mod.rs')
-rw-r--r--src/de/mod.rs289
1 files changed, 289 insertions, 0 deletions
diff --git a/src/de/mod.rs b/src/de/mod.rs
new file mode 100644
index 0000000..09ea120
--- /dev/null
+++ b/src/de/mod.rs
@@ -0,0 +1,289 @@
+//! Deserializing TOML into Rust structures.
+//!
+//! This module contains all the Serde support for deserializing TOML documents into Rust structures.
+
+use serde::de::DeserializeOwned;
+
+mod array;
+mod datetime;
+mod key;
+mod spanned;
+mod table;
+mod table_enum;
+mod value;
+
+use array::ArrayDeserializer;
+use datetime::DatetimeDeserializer;
+use key::KeyDeserializer;
+use spanned::SpannedDeserializer;
+use table::TableMapAccess;
+use table_enum::TableEnumDeserializer;
+
+pub use value::ValueDeserializer;
+
+/// Errors that can occur when deserializing a type.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Error {
+ inner: crate::TomlError,
+}
+
+impl Error {
+ pub(crate) fn custom<T>(msg: T, span: Option<std::ops::Range<usize>>) -> Self
+ where
+ T: std::fmt::Display,
+ {
+ Error {
+ inner: crate::TomlError::custom(msg.to_string(), span),
+ }
+ }
+
+ /// Add key while unwinding
+ pub fn add_key(&mut self, key: String) {
+ self.inner.add_key(key)
+ }
+
+ /// What went wrong
+ pub fn message(&self) -> &str {
+ self.inner.message()
+ }
+
+ /// The start/end index into the original document where the error occurred
+ pub fn span(&self) -> Option<std::ops::Range<usize>> {
+ self.inner.span()
+ }
+
+ pub(crate) fn set_span(&mut self, span: Option<std::ops::Range<usize>>) {
+ self.inner.set_span(span);
+ }
+}
+
+impl serde::de::Error for Error {
+ fn custom<T>(msg: T) -> Self
+ where
+ T: std::fmt::Display,
+ {
+ Error::custom(msg, None)
+ }
+}
+
+impl std::fmt::Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ self.inner.fmt(f)
+ }
+}
+
+impl From<crate::TomlError> for Error {
+ fn from(e: crate::TomlError) -> Error {
+ Self { inner: e }
+ }
+}
+
+impl From<Error> for crate::TomlError {
+ fn from(e: Error) -> crate::TomlError {
+ e.inner
+ }
+}
+
+impl std::error::Error for Error {}
+
+/// Convert a value into `T`.
+pub fn from_str<T>(s: &'_ str) -> Result<T, Error>
+where
+ T: DeserializeOwned,
+{
+ let de = s.parse::<Deserializer>()?;
+ T::deserialize(de)
+}
+
+/// Convert a value into `T`.
+pub fn from_slice<T>(s: &'_ [u8]) -> Result<T, Error>
+where
+ T: DeserializeOwned,
+{
+ let s = std::str::from_utf8(s).map_err(|e| Error::custom(e, None))?;
+ from_str(s)
+}
+
+/// Convert a document into `T`.
+pub fn from_document<T>(d: crate::Document) -> Result<T, Error>
+where
+ T: DeserializeOwned,
+{
+ let deserializer = Deserializer::new(d);
+ T::deserialize(deserializer)
+}
+
+/// Deserialization for TOML [documents][crate::Document].
+pub struct Deserializer {
+ input: crate::Document,
+}
+
+impl Deserializer {
+ /// Deserialization implementation for TOML.
+ pub fn new(input: crate::Document) -> Self {
+ Self { input }
+ }
+}
+
+impl std::str::FromStr for Deserializer {
+ type Err = Error;
+
+ /// Parses a document from a &str
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let d = crate::parser::parse_document(s).map_err(Error::from)?;
+ Ok(Self::new(d))
+ }
+}
+
+// Note: this is wrapped by `toml::de::Deserializer` and any trait methods
+// implemented here need to be wrapped there
+impl<'de> serde::Deserializer<'de> for Deserializer {
+ type Error = Error;
+
+ fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+ where
+ V: serde::de::Visitor<'de>,
+ {
+ let original = self.input.original;
+ self.input
+ .root
+ .into_deserializer()
+ .deserialize_any(visitor)
+ .map_err(|mut e: Self::Error| {
+ e.inner.set_original(original);
+ e
+ })
+ }
+
+ // `None` is interpreted as a missing field so be sure to implement `Some`
+ // as a present field.
+ fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: serde::de::Visitor<'de>,
+ {
+ let original = self.input.original;
+ self.input
+ .root
+ .into_deserializer()
+ .deserialize_option(visitor)
+ .map_err(|mut e: Self::Error| {
+ e.inner.set_original(original);
+ e
+ })
+ }
+
+ fn deserialize_newtype_struct<V>(
+ self,
+ name: &'static str,
+ visitor: V,
+ ) -> Result<V::Value, Error>
+ where
+ V: serde::de::Visitor<'de>,
+ {
+ let original = self.input.original;
+ self.input
+ .root
+ .into_deserializer()
+ .deserialize_newtype_struct(name, visitor)
+ .map_err(|mut e: Self::Error| {
+ e.inner.set_original(original);
+ e
+ })
+ }
+
+ fn deserialize_struct<V>(
+ self,
+ name: &'static str,
+ fields: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value, Error>
+ where
+ V: serde::de::Visitor<'de>,
+ {
+ let original = self.input.original;
+ self.input
+ .root
+ .into_deserializer()
+ .deserialize_struct(name, fields, visitor)
+ .map_err(|mut e: Self::Error| {
+ e.inner.set_original(original);
+ e
+ })
+ }
+
+ // 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,
+ variants: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value, Error>
+ where
+ V: serde::de::Visitor<'de>,
+ {
+ let original = self.input.original;
+ self.input
+ .root
+ .into_deserializer()
+ .deserialize_enum(name, variants, visitor)
+ .map_err(|mut e: Self::Error| {
+ e.inner.set_original(original);
+ e
+ })
+ }
+
+ serde::forward_to_deserialize_any! {
+ bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
+ bytes byte_buf map unit
+ ignored_any unit_struct tuple_struct tuple identifier
+ }
+}
+
+impl<'de> serde::de::IntoDeserializer<'de, crate::de::Error> for Deserializer {
+ type Deserializer = Deserializer;
+
+ fn into_deserializer(self) -> Self::Deserializer {
+ self
+ }
+}
+
+impl<'de> serde::de::IntoDeserializer<'de, crate::de::Error> for crate::Document {
+ type Deserializer = Deserializer;
+
+ fn into_deserializer(self) -> Self::Deserializer {
+ Deserializer::new(self)
+ }
+}
+
+pub(crate) fn validate_struct_keys(
+ table: &crate::table::KeyValuePairs,
+ fields: &'static [&'static str],
+) -> Result<(), Error> {
+ let extra_fields = table
+ .iter()
+ .filter_map(|(key, val)| {
+ if !fields.contains(&key.as_str()) {
+ Some(val.clone())
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<_>>();
+
+ if extra_fields.is_empty() {
+ Ok(())
+ } else {
+ Err(Error::custom(
+ format!(
+ "unexpected keys in table: {}, available keys: {}",
+ extra_fields
+ .iter()
+ .map(|k| k.key.get())
+ .collect::<Vec<_>>()
+ .join(", "),
+ fields.join(", "),
+ ),
+ extra_fields[0].key.span(),
+ ))
+ }
+}