diff options
author | Nadrieril | 2020-10-27 23:33:26 +0000 |
---|---|---|
committer | Nadrieril | 2020-10-27 23:33:26 +0000 |
commit | 77258af83dfe93293ad854ccb401d1ce7453edfc (patch) | |
tree | a349d40ab3b9f5bee0ebbe2d3da4a87f50d90ae8 /serde_dhall | |
parent | f84609a06372eedcf4727ceb91430b82c99e6039 (diff) |
Make `SimpleValue` deserializable within other types
Fixes https://github.com/Nadrieril/dhall-rust/issues/184
Diffstat (limited to 'serde_dhall')
-rw-r--r-- | serde_dhall/src/deserialize.rs | 119 | ||||
-rw-r--r-- | serde_dhall/src/value.rs | 11 | ||||
-rw-r--r-- | serde_dhall/tests/de.rs | 34 |
3 files changed, 145 insertions, 19 deletions
diff --git a/serde_dhall/src/deserialize.rs b/serde_dhall/src/deserialize.rs index 6d3aab8..1206033 100644 --- a/serde_dhall/src/deserialize.rs +++ b/serde_dhall/src/deserialize.rs @@ -1,12 +1,17 @@ +use std::borrow::Cow; +use std::collections::BTreeMap; +use std::convert::TryInto as _; +use std::fmt; + use serde::de::value::{ MapAccessDeserializer, MapDeserializer, SeqDeserializer, }; -use std::borrow::Cow; +use serde::de::VariantAccess as _; use dhall::syntax::NumKind; use crate::value::SimpleValue; -use crate::{Error, ErrorKind, Result, Value}; +use crate::{Error, ErrorKind, Value}; pub trait Sealed {} @@ -38,7 +43,7 @@ pub trait Sealed {} /// [serde]: https://serde.rs pub trait FromDhall: Sealed + Sized { #[doc(hidden)] - fn from_dhall(v: &Value) -> Result<Self>; + fn from_dhall(v: &Value) -> crate::Result<Self>; } impl<T> Sealed for T where T: serde::de::DeserializeOwned {} @@ -83,7 +88,7 @@ struct Deserializer<'a>(Cow<'a, SimpleValue>); /// ``` /// /// [`SimpleValue`]: enum.SimpleValue.html -pub fn from_simple_value<T>(v: SimpleValue) -> Result<T> +pub fn from_simple_value<T>(v: SimpleValue) -> crate::Result<T> where T: serde::de::DeserializeOwned, { @@ -94,7 +99,7 @@ impl<T> FromDhall for T where T: serde::de::DeserializeOwned, { - fn from_dhall(v: &Value) -> Result<Self> { + fn from_dhall(v: &Value) -> crate::Result<Self> { let sval = v.to_simple_value().ok_or_else(|| { Error(ErrorKind::Deserialize(format!( "this cannot be deserialized into the serde data model: {}", @@ -115,7 +120,7 @@ impl<'de: 'a, 'a> serde::de::IntoDeserializer<'de, Error> for Deserializer<'a> { impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { type Error = Error; - fn deserialize_any<V>(self, visitor: V) -> Result<V::Value> + fn deserialize_any<V>(self, visitor: V) -> crate::Result<V::Value> where V: serde::de::Visitor<'de>, { @@ -167,7 +172,11 @@ impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { } } - fn deserialize_tuple<V>(self, _: usize, visitor: V) -> Result<V::Value> + fn deserialize_tuple<V>( + self, + _: usize, + visitor: V, + ) -> crate::Result<V::Value> where V: serde::de::Visitor<'de>, { @@ -186,3 +195,99 @@ impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { tuple_struct map struct enum identifier ignored_any } } + +struct SimpleValueVisitor; + +impl<'de> serde::de::Visitor<'de> for SimpleValueVisitor { + type Value = SimpleValue; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("any valid Dhall value") + } + + fn visit_bool<E>(self, value: bool) -> Result<SimpleValue, E> { + Ok(SimpleValue::Num(NumKind::Bool(value))) + } + + fn visit_i64<E>(self, value: i64) -> Result<SimpleValue, E> { + // TODO: use `i64` instead of `isize` in `NumKind`. + Ok(SimpleValue::Num(NumKind::Integer( + value.try_into().unwrap(), + ))) + } + + fn visit_u64<E>(self, value: u64) -> Result<SimpleValue, E> { + // TODO: use `u64` instead of `usize` in `NumKind`. + Ok(SimpleValue::Num(NumKind::Natural( + value.try_into().unwrap(), + ))) + } + + fn visit_f64<E>(self, value: f64) -> Result<SimpleValue, E> { + Ok(SimpleValue::Num(NumKind::Double(value.into()))) + } + + fn visit_str<E>(self, value: &str) -> Result<SimpleValue, E> { + Ok(SimpleValue::Text(String::from(value))) + } + + fn visit_string<E>(self, value: String) -> Result<SimpleValue, E> { + Ok(SimpleValue::Text(value)) + } + + fn visit_none<E>(self) -> Result<SimpleValue, E> { + Ok(SimpleValue::Optional(None)) + } + + fn visit_some<D>(self, val: D) -> Result<SimpleValue, D::Error> + where + D: serde::Deserializer<'de>, + { + let val = val.deserialize_any(SimpleValueVisitor)?; + Ok(SimpleValue::Optional(Some(Box::new(val)))) + } + + fn visit_enum<V>(self, visitor: V) -> Result<SimpleValue, V::Error> + where + V: serde::de::EnumAccess<'de>, + { + let (name, variant): (String, _) = visitor.variant()?; + // Serde does not allow me to check what kind of variant it is. This will work for dhall + // values, because there are only two possible kinds of cvariants, but doesn't work in + // general. Given that the `serde_value` crate ignores enums, I assume this is not fixable + // :(. + let val = variant.newtype_variant().ok(); + Ok(SimpleValue::Union(name, val)) + } + + fn visit_seq<V>(self, mut visitor: V) -> Result<SimpleValue, V::Error> + where + V: serde::de::SeqAccess<'de>, + { + let mut vec = Vec::new(); + while let Some(elem) = visitor.next_element()? { + vec.push(elem); + } + Ok(SimpleValue::List(vec)) + } + + fn visit_map<V>(self, mut visitor: V) -> Result<SimpleValue, V::Error> + where + V: serde::de::MapAccess<'de>, + { + let mut record = BTreeMap::default(); + while let Some((key, value)) = visitor.next_entry()? { + record.insert(key, value); + } + Ok(SimpleValue::Record(record)) + } +} + +impl<'de> serde::de::Deserialize<'de> for SimpleValue { + fn deserialize<D>(deserializer: D) -> Result<SimpleValue, D::Error> + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_any(SimpleValueVisitor) + } +} diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs index 79ba75a..d7116d9 100644 --- a/serde_dhall/src/value.rs +++ b/serde_dhall/src/value.rs @@ -377,7 +377,6 @@ impl SimpleType { } impl Sealed for Value {} -impl Sealed for SimpleValue {} impl Sealed for SimpleType {} impl FromDhall for Value { @@ -385,16 +384,6 @@ impl FromDhall for Value { Ok(v.clone()) } } -impl FromDhall for SimpleValue { - fn from_dhall(v: &Value) -> Result<Self> { - v.to_simple_value().ok_or_else(|| { - Error(ErrorKind::Deserialize(format!( - "this cannot be deserialized into a simple type: {}", - v - ))) - }) - } -} impl FromDhall for SimpleType { fn from_dhall(v: &Value) -> Result<Self> { v.to_simple_type().ok_or_else(|| { diff --git a/serde_dhall/tests/de.rs b/serde_dhall/tests/de.rs index a418563..549a753 100644 --- a/serde_dhall/tests/de.rs +++ b/serde_dhall/tests/de.rs @@ -1,5 +1,5 @@ use serde::Deserialize; -use serde_dhall::{from_str, FromDhall, StaticType}; +use serde_dhall::{from_str, FromDhall, NumKind, SimpleValue, StaticType}; #[test] fn test_de_typed() { @@ -116,5 +116,37 @@ fn test_de_untyped() { assert!(from_str("List/length [True, 42]").parse::<bool>().is_err()); } +#[test] +fn test_de_simplevalue() { + let bool_true = SimpleValue::Num(NumKind::Bool(true)); + // https://github.com/Nadrieril/dhall-rust/issues/184 + assert_eq!( + from_str("[ True ]").parse::<Vec<SimpleValue>>().unwrap(), + vec![bool_true.clone()] + ); + + assert_eq!( + from_str("< Foo >.Foo").parse::<SimpleValue>().unwrap(), + SimpleValue::Union("Foo".into(), None) + ); + assert_eq!( + from_str("< Foo: Bool >.Foo True") + .parse::<SimpleValue>() + .unwrap(), + SimpleValue::Union("Foo".into(), Some(Box::new(bool_true.clone()))) + ); + + #[derive(Debug, PartialEq, Eq, Deserialize)] + struct Foo { + foo: SimpleValue, + } + assert_eq!( + from_str("{ foo = True }").parse::<Foo>().unwrap(), + Foo { + foo: bool_true.clone() + }, + ); +} + // TODO: test various builder configurations // In particular test cloning and reusing builder |