aboutsummaryrefslogtreecommitdiff
path: root/src/de/table_enum.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/de/table_enum.rs')
-rw-r--r--src/de/table_enum.rs160
1 files changed, 160 insertions, 0 deletions
diff --git a/src/de/table_enum.rs b/src/de/table_enum.rs
new file mode 100644
index 0000000..197ad6e
--- /dev/null
+++ b/src/de/table_enum.rs
@@ -0,0 +1,160 @@
+use crate::de::Error;
+
+/// Deserializes table values into enum variants.
+pub(crate) struct TableEnumDeserializer {
+ value: crate::Item,
+}
+
+impl TableEnumDeserializer {
+ pub(crate) fn new(value: crate::Item) -> Self {
+ TableEnumDeserializer { value }
+ }
+}
+
+impl<'de> serde::de::VariantAccess<'de> for TableEnumDeserializer {
+ type Error = Error;
+
+ fn unit_variant(self) -> Result<(), Self::Error> {
+ match self.value {
+ crate::Item::Table(values) => {
+ if values.is_empty() {
+ Ok(())
+ } else {
+ Err(Error::custom("expected empty table", values.span()))
+ }
+ }
+ crate::Item::Value(crate::Value::InlineTable(values)) => {
+ if values.is_empty() {
+ Ok(())
+ } else {
+ Err(Error::custom("expected empty table", values.span()))
+ }
+ }
+ e => Err(Error::custom(
+ format!("expected table, found {}", e.type_name()),
+ e.span(),
+ )),
+ }
+ }
+
+ fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error>
+ where
+ T: serde::de::DeserializeSeed<'de>,
+ {
+ seed.deserialize(super::ValueDeserializer::new(self.value))
+ }
+
+ fn tuple_variant<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
+ where
+ V: serde::de::Visitor<'de>,
+ {
+ match self.value {
+ crate::Item::Table(values) => {
+ let values_span = values.span();
+ let tuple_values = values
+ .items
+ .into_iter()
+ .enumerate()
+ .map(
+ |(index, (_, value))| match value.key.get().parse::<usize>() {
+ Ok(key_index) if key_index == index => Ok(value.value),
+ Ok(_) | Err(_) => Err(Error::custom(
+ format!(
+ "expected table key `{}`, but was `{}`",
+ index,
+ value.key.get()
+ ),
+ value.key.span(),
+ )),
+ },
+ )
+ // 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 {
+ serde::de::Deserializer::deserialize_seq(
+ super::ArrayDeserializer::new(tuple_values, values_span),
+ visitor,
+ )
+ } else {
+ Err(Error::custom(
+ format!("expected tuple with length {}", len),
+ values_span,
+ ))
+ }
+ }
+ crate::Item::Value(crate::Value::InlineTable(values)) => {
+ let values_span = values.span();
+ let tuple_values = values
+ .items
+ .into_iter()
+ .enumerate()
+ .map(
+ |(index, (_, value))| match value.key.get().parse::<usize>() {
+ Ok(key_index) if key_index == index => Ok(value.value),
+ Ok(_) | Err(_) => Err(Error::custom(
+ format!(
+ "expected table key `{}`, but was `{}`",
+ index,
+ value.key.get()
+ ),
+ value.key.span(),
+ )),
+ },
+ )
+ // 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 {
+ serde::de::Deserializer::deserialize_seq(
+ super::ArrayDeserializer::new(tuple_values, values_span),
+ visitor,
+ )
+ } else {
+ Err(Error::custom(
+ format!("expected tuple with length {}", len),
+ values_span,
+ ))
+ }
+ }
+ e => Err(Error::custom(
+ format!("expected table, found {}", e.type_name()),
+ e.span(),
+ )),
+ }
+ }
+
+ fn struct_variant<V>(
+ self,
+ fields: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value, Self::Error>
+ where
+ V: serde::de::Visitor<'de>,
+ {
+ serde::de::Deserializer::deserialize_struct(
+ super::ValueDeserializer::new(self.value).with_struct_key_validation(),
+ "", // TODO: this should be the variant name
+ fields,
+ visitor,
+ )
+ }
+}