diff options
Diffstat (limited to 'src/de/table_enum.rs')
-rw-r--r-- | src/de/table_enum.rs | 160 |
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, + ) + } +} |