From 77258af83dfe93293ad854ccb401d1ce7453edfc Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 27 Oct 2020 23:33:26 +0000 Subject: Make `SimpleValue` deserializable within other types Fixes https://github.com/Nadrieril/dhall-rust/issues/184 --- CHANGELOG.md | 2 + dhall/src/syntax/ast/expr.rs | 1 + serde_dhall/src/deserialize.rs | 119 ++++++++++++++++++++++++++++++++++++++--- serde_dhall/src/value.rs | 11 ---- serde_dhall/tests/de.rs | 34 +++++++++++- 5 files changed, 148 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b835aa1..35b87a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ #### [Unreleased] +- Make `SimpleValue` deserializable within other types (https://github.com/Nadrieril/dhall-rust/issues/184) + #### [0.7.4] - 2020-10-25 - Add new `Text/replace` builtin (https://github.com/Nadrieril/dhall-rust/pull/181) diff --git a/dhall/src/syntax/ast/expr.rs b/dhall/src/syntax/ast/expr.rs index 9d216a7..d9badb9 100644 --- a/dhall/src/syntax/ast/expr.rs +++ b/dhall/src/syntax/ast/expr.rs @@ -7,6 +7,7 @@ use crate::semantics::Universe; use crate::syntax::visitor; use crate::syntax::*; +// TODO: `usize` was a mistake. Should use `u64`. pub type Integer = isize; pub type Natural = usize; pub type Double = NaiveDouble; 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; + fn from_dhall(v: &Value) -> crate::Result; } impl 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(v: SimpleValue) -> Result +pub fn from_simple_value(v: SimpleValue) -> crate::Result where T: serde::de::DeserializeOwned, { @@ -94,7 +99,7 @@ impl FromDhall for T where T: serde::de::DeserializeOwned, { - fn from_dhall(v: &Value) -> Result { + fn from_dhall(v: &Value) -> crate::Result { 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(self, visitor: V) -> Result + fn deserialize_any(self, visitor: V) -> crate::Result where V: serde::de::Visitor<'de>, { @@ -167,7 +172,11 @@ impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { } } - fn deserialize_tuple(self, _: usize, visitor: V) -> Result + fn deserialize_tuple( + self, + _: usize, + visitor: V, + ) -> crate::Result 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(self, value: bool) -> Result { + Ok(SimpleValue::Num(NumKind::Bool(value))) + } + + fn visit_i64(self, value: i64) -> Result { + // TODO: use `i64` instead of `isize` in `NumKind`. + Ok(SimpleValue::Num(NumKind::Integer( + value.try_into().unwrap(), + ))) + } + + fn visit_u64(self, value: u64) -> Result { + // TODO: use `u64` instead of `usize` in `NumKind`. + Ok(SimpleValue::Num(NumKind::Natural( + value.try_into().unwrap(), + ))) + } + + fn visit_f64(self, value: f64) -> Result { + Ok(SimpleValue::Num(NumKind::Double(value.into()))) + } + + fn visit_str(self, value: &str) -> Result { + Ok(SimpleValue::Text(String::from(value))) + } + + fn visit_string(self, value: String) -> Result { + Ok(SimpleValue::Text(value)) + } + + fn visit_none(self) -> Result { + Ok(SimpleValue::Optional(None)) + } + + fn visit_some(self, val: D) -> Result + where + D: serde::Deserializer<'de>, + { + let val = val.deserialize_any(SimpleValueVisitor)?; + Ok(SimpleValue::Optional(Some(Box::new(val)))) + } + + fn visit_enum(self, visitor: V) -> Result + 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(self, mut visitor: V) -> Result + 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(self, mut visitor: V) -> Result + 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(deserializer: D) -> Result + 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 { - 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 { 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::().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::>().unwrap(), + vec![bool_true.clone()] + ); + + assert_eq!( + from_str("< Foo >.Foo").parse::().unwrap(), + SimpleValue::Union("Foo".into(), None) + ); + assert_eq!( + from_str("< Foo: Bool >.Foo True") + .parse::() + .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::().unwrap(), + Foo { + foo: bool_true.clone() + }, + ); +} + // TODO: test various builder configurations // In particular test cloning and reusing builder -- cgit v1.2.3