Implement deserialization for unit and struct variants.

Issue #225
This commit is contained in:
Azriel Hoh 2018-10-10 08:59:46 +13:00
parent f07ba88de4
commit f5d14af5a6
2 changed files with 140 additions and 1 deletions

40
examples/enum_external.rs Normal file
View file

@ -0,0 +1,40 @@
//! An example showing off the usage of `Deserialize` to automatically decode
//! TOML into a Rust `struct`, with enums.
#![deny(warnings)]
extern crate toml;
#[macro_use]
extern crate serde_derive;
/// This is what we're going to decode into.
#[derive(Debug, Deserialize)]
struct Config {
plain: MyEnum,
// tuple: MyEnum,
#[serde(rename = "struct")]
structv: MyEnum,
my_enum: Vec<MyEnum>,
}
#[derive(Debug, Deserialize)]
enum MyEnum {
Plain,
Tuple(i64, bool),
Struct { value: i64 },
}
fn main() {
let toml_str = r#"
plain = "Plain"
# tuple = { 0 = 123, 1 = true }
struct = { Struct = { value = 123 } }
my_enum = [
{ Plain = {} },
# { Tuple = { 0 = 123, 1 = true } },
{ Struct = { value = 123 } }
]"#;
let decoded: Config = toml::from_str(toml_str).unwrap();
println!("{:#?}", decoded);
}

101
src/de.rs
View file

@ -585,7 +585,27 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> {
{ {
match self.value.e { match self.value.e {
E::String(val) => visitor.visit_enum(val.into_deserializer()), E::String(val) => visitor.visit_enum(val.into_deserializer()),
_ => Err(Error::from_kind(ErrorKind::ExpectedString)) E::InlineTable(values) | E::DottedTable(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 table",
found: e.type_name(),
})),
} }
} }
@ -724,6 +744,70 @@ impl<'de> de::MapAccess<'de> for InlineTableDeserializer<'de> {
} }
} }
impl<'de> de::EnumAccess<'de> for InlineTableDeserializer<'de> {
type Error = Error;
type Variant = Self;
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 map",
}))
}
};
self.next_value = Some(value);
seed.deserialize(StrDeserializer::new(key))
.map(|val| (val, self))
}
}
impl<'de> de::VariantAccess<'de> for InlineTableDeserializer<'de> {
type Error = Error;
fn unit_variant(self) -> Result<(), Self::Error> {
// TODO: Error handling if there are entries
Ok(())
}
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error>
where
T: de::DeserializeSeed<'de>,
{
seed.deserialize(ValueDeserializer::new(
self.next_value.expect("Expected value"),
))
}
fn tuple_variant<V>(self, _len: usize, _visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
unimplemented!()
}
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.next_value.expect("Expected value")),
"", // TODO: this should be the variant name
fields,
visitor,
)
}
}
impl<'a> Deserializer<'a> { impl<'a> Deserializer<'a> {
/// Creates a new deserializer which will be deserializing the string /// Creates a new deserializer which will be deserializing the string
@ -1510,6 +1594,21 @@ enum E<'a> {
DottedTable(Vec<(Cow<'a, str>, Value<'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> { impl<'a> Value<'a> {
fn same_type(&self, other: &Value<'a>) -> bool { fn same_type(&self, other: &Value<'a>) -> bool {
match (&self.e, &other.e) { match (&self.e, &other.e) {