summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNadrieril2020-10-27 23:33:26 +0000
committerNadrieril2020-10-27 23:33:26 +0000
commit77258af83dfe93293ad854ccb401d1ce7453edfc (patch)
treea349d40ab3b9f5bee0ebbe2d3da4a87f50d90ae8
parentf84609a06372eedcf4727ceb91430b82c99e6039 (diff)
Make `SimpleValue` deserializable within other types
Fixes https://github.com/Nadrieril/dhall-rust/issues/184
-rw-r--r--CHANGELOG.md2
-rw-r--r--dhall/src/syntax/ast/expr.rs1
-rw-r--r--serde_dhall/src/deserialize.rs119
-rw-r--r--serde_dhall/src/value.rs11
-rw-r--r--serde_dhall/tests/de.rs34
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<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