summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNadrieril2020-05-10 22:09:43 +0100
committerNadrieril2020-10-28 22:52:41 +0000
commite070270c3f1f10d46281ed7751ff95e15092e7f4 (patch)
tree46c22c75f209c5112030f3db6609ed64ff4888d3
parent5f3ca811f09dcf6f09fb9b60fcd2664d06762f39 (diff)
Implement serialization
-rw-r--r--CHANGELOG.md1
-rw-r--r--serde_dhall/src/deserialize.rs16
-rw-r--r--serde_dhall/src/error.rs11
-rw-r--r--serde_dhall/src/lib.rs6
-rw-r--r--serde_dhall/src/options/de.rs (renamed from serde_dhall/src/options.rs)12
-rw-r--r--serde_dhall/src/options/mod.rs2
-rw-r--r--serde_dhall/src/options/ser.rs67
-rw-r--r--serde_dhall/src/serialize.rs312
-rw-r--r--serde_dhall/src/static_type.rs60
-rw-r--r--serde_dhall/src/value.rs106
-rw-r--r--serde_dhall/tests/de.rs59
-rw-r--r--serde_dhall/tests/serde.rs145
12 files changed, 725 insertions, 72 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5329eca..0fe7814 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
#### [Unreleased]
+- Implement serialization (https://github.com/Nadrieril/dhall-rust/issues/164)
- BREAKING CHANGE: use u64/i64 instead of usize/isize in `NumKind`
#### [0.7.5] - 2020-10-28
diff --git a/serde_dhall/src/deserialize.rs b/serde_dhall/src/deserialize.rs
index 12b4703..0e518ea 100644
--- a/serde_dhall/src/deserialize.rs
+++ b/serde_dhall/src/deserialize.rs
@@ -47,8 +47,6 @@ pub trait FromDhall: Sealed + Sized {
impl<T> Sealed for T where T: serde::de::DeserializeOwned {}
-struct Deserializer<'a>(Cow<'a, SimpleValue>);
-
/// Deserialize a Rust value from a Dhall [`SimpleValue`].
///
/// # Example
@@ -109,6 +107,8 @@ where
}
}
+struct Deserializer<'a>(Cow<'a, SimpleValue>);
+
impl<'de: 'a, 'a> serde::de::IntoDeserializer<'de, Error> for Deserializer<'a> {
type Deserializer = Deserializer<'a>;
fn into_deserializer(self) -> Self::Deserializer {
@@ -171,9 +171,19 @@ impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> {
}
}
+ fn deserialize_unit<V>(self, visitor: V) -> crate::Result<V::Value>
+ where
+ V: serde::de::Visitor<'de>,
+ {
+ match self.0.as_ref() {
+ SimpleValue::Record(m) if m.is_empty() => visitor.visit_unit(),
+ _ => self.deserialize_any(visitor),
+ }
+ }
+
serde::forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
- bytes byte_buf option unit unit_struct newtype_struct seq
+ bytes byte_buf option unit_struct newtype_struct seq
tuple_struct map struct enum identifier ignored_any
}
}
diff --git a/serde_dhall/src/error.rs b/serde_dhall/src/error.rs
index 896e8b9..3309fe4 100644
--- a/serde_dhall/src/error.rs
+++ b/serde_dhall/src/error.rs
@@ -11,6 +11,7 @@ pub struct Error(pub(crate) ErrorKind);
pub(crate) enum ErrorKind {
Dhall(DhallError),
Deserialize(String),
+ Serialize(String),
}
impl From<ErrorKind> for Error {
@@ -24,6 +25,7 @@ impl std::fmt::Display for Error {
match &self.0 {
ErrorKind::Dhall(err) => write!(f, "{}", err),
ErrorKind::Deserialize(err) => write!(f, "{}", err),
+ ErrorKind::Serialize(err) => write!(f, "{}", err),
}
}
}
@@ -38,3 +40,12 @@ impl serde::de::Error for Error {
ErrorKind::Deserialize(msg.to_string()).into()
}
}
+
+impl serde::ser::Error for Error {
+ fn custom<T>(msg: T) -> Self
+ where
+ T: std::fmt::Display,
+ {
+ ErrorKind::Serialize(msg.to_string()).into()
+ }
+}
diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs
index 8291f74..e236e37 100644
--- a/serde_dhall/src/lib.rs
+++ b/serde_dhall/src/lib.rs
@@ -169,6 +169,7 @@ mod test_readme {
mod deserialize;
mod error;
mod options;
+mod serialize;
mod static_type;
/// Dhall values
mod value;
@@ -176,10 +177,11 @@ mod value;
#[doc(hidden)]
pub use dhall_proc_macros::StaticType;
-pub(crate) use deserialize::Sealed;
pub use deserialize::{from_simple_value, FromDhall};
pub(crate) use error::ErrorKind;
pub use error::{Error, Result};
-pub use options::{from_file, from_str, Deserializer};
+pub use options::de::{from_file, from_str, Deserializer};
+pub use options::ser::{serialize, Serializer};
+pub use serialize::ToDhall;
pub use static_type::StaticType;
pub use value::{NumKind, SimpleType, SimpleValue, Value};
diff --git a/serde_dhall/src/options.rs b/serde_dhall/src/options/de.rs
index 7b27114..8ff794d 100644
--- a/serde_dhall/src/options.rs
+++ b/serde_dhall/src/options/de.rs
@@ -19,20 +19,20 @@ pub struct ManualAnnot<'ty>(&'ty SimpleType);
#[derive(Debug, Clone, Copy)]
pub struct StaticAnnot;
-pub trait HasAnnot<A> {
+pub trait OptionalAnnot<A> {
fn get_annot(a: &A) -> Option<SimpleType>;
}
-impl<T> HasAnnot<NoAnnot> for T {
+impl<T> OptionalAnnot<NoAnnot> for T {
fn get_annot(_: &NoAnnot) -> Option<SimpleType> {
None
}
}
-impl<'ty, T> HasAnnot<ManualAnnot<'ty>> for T {
+impl<'ty, T> OptionalAnnot<ManualAnnot<'ty>> for T {
fn get_annot(a: &ManualAnnot<'ty>) -> Option<SimpleType> {
Some(a.0.clone())
}
}
-impl<T: StaticType> HasAnnot<StaticAnnot> for T {
+impl<T: StaticType> OptionalAnnot<StaticAnnot> for T {
fn get_annot(_: &StaticAnnot) -> Option<SimpleType> {
Some(T::static_type())
}
@@ -252,7 +252,7 @@ impl<'a, A> Deserializer<'a, A> {
fn _parse<T>(&self) -> dhall::error::Result<Value>
where
- T: HasAnnot<A>,
+ T: OptionalAnnot<A>,
{
let parsed = match &self.source {
Source::Str(s) => Parsed::parse_str(s)?,
@@ -287,7 +287,7 @@ impl<'a, A> Deserializer<'a, A> {
/// [`StaticType`]: trait.StaticType.html
pub fn parse<T>(&self) -> Result<T>
where
- T: FromDhall + HasAnnot<A>,
+ T: FromDhall + OptionalAnnot<A>,
{
let val = self
._parse::<T>()
diff --git a/serde_dhall/src/options/mod.rs b/serde_dhall/src/options/mod.rs
new file mode 100644
index 0000000..384f318
--- /dev/null
+++ b/serde_dhall/src/options/mod.rs
@@ -0,0 +1,2 @@
+pub(crate) mod de;
+pub(crate) mod ser;
diff --git a/serde_dhall/src/options/ser.rs b/serde_dhall/src/options/ser.rs
new file mode 100644
index 0000000..026dd21
--- /dev/null
+++ b/serde_dhall/src/options/ser.rs
@@ -0,0 +1,67 @@
+use crate::{Result, SimpleType, StaticType, ToDhall};
+
+#[derive(Debug, Clone, Copy)]
+pub struct NoAnnot;
+#[derive(Debug, Clone, Copy)]
+pub struct ManualAnnot<'ty>(&'ty SimpleType);
+#[derive(Debug, Clone, Copy)]
+pub struct StaticAnnot;
+
+pub trait RequiredAnnot<A> {
+ fn get_annot(a: &A) -> SimpleType;
+}
+impl<'ty, T> RequiredAnnot<ManualAnnot<'ty>> for T {
+ fn get_annot(a: &ManualAnnot<'ty>) -> SimpleType {
+ a.0.clone()
+ }
+}
+impl<T: StaticType> RequiredAnnot<StaticAnnot> for T {
+ fn get_annot(_: &StaticAnnot) -> SimpleType {
+ T::static_type()
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Serializer<'a, T, A> {
+ data: &'a T,
+ annot: A,
+}
+
+impl<'a, T> Serializer<'a, T, NoAnnot> {
+ pub fn type_annotation<'ty>(
+ self,
+ ty: &'ty SimpleType,
+ ) -> Serializer<'a, T, ManualAnnot<'ty>> {
+ Serializer {
+ annot: ManualAnnot(ty),
+ data: self.data,
+ }
+ }
+
+ pub fn static_type_annotation(self) -> Serializer<'a, T, StaticAnnot> {
+ Serializer {
+ annot: StaticAnnot,
+ data: self.data,
+ }
+ }
+}
+
+impl<'a, T, A> Serializer<'a, T, A> {
+ pub fn to_string(&self) -> Result<String>
+ where
+ T: ToDhall + RequiredAnnot<A>,
+ {
+ let val = self.data.to_dhall(&T::get_annot(&self.annot))?;
+ Ok(val.to_string())
+ }
+}
+
+pub fn serialize<'a, T>(data: &'a T) -> Serializer<'a, T, NoAnnot>
+where
+ T: ToDhall,
+{
+ Serializer {
+ data,
+ annot: NoAnnot,
+ }
+}
diff --git a/serde_dhall/src/serialize.rs b/serde_dhall/src/serialize.rs
new file mode 100644
index 0000000..dd3e426
--- /dev/null
+++ b/serde_dhall/src/serialize.rs
@@ -0,0 +1,312 @@
+use serde::ser;
+use std::collections::BTreeMap;
+
+use dhall::syntax::NumKind;
+
+use crate::value::SimpleValue;
+use crate::{Error, ErrorKind, Result, SimpleType, Value};
+use SimpleValue::*;
+
+pub trait Sealed {}
+
+pub trait ToDhall: Sealed {
+ #[doc(hidden)]
+ fn to_dhall(&self, ty: &SimpleType) -> Result<Value>;
+}
+
+impl<T> Sealed for T where T: ser::Serialize {}
+
+impl<T> ToDhall for T
+where
+ T: ser::Serialize,
+{
+ fn to_dhall(&self, ty: &SimpleType) -> Result<Value> {
+ let sval: SimpleValue = self.serialize(Serializer)?;
+ sval.into_value(ty)
+ }
+}
+
+#[derive(Default, Clone, Copy)]
+struct Serializer;
+
+impl ser::Serializer for Serializer {
+ type Ok = SimpleValue;
+ type Error = Error;
+
+ type SerializeSeq = SeqSerializer;
+ type SerializeTuple = TupleSerializer;
+ type SerializeTupleStruct = ser::Impossible<Self::Ok, Self::Error>;
+ type SerializeTupleVariant = ser::Impossible<Self::Ok, Self::Error>;
+ type SerializeMap = MapSerializer;
+ type SerializeStruct = StructSerializer;
+ type SerializeStructVariant = ser::Impossible<Self::Ok, Self::Error>;
+
+ fn serialize_bool(self, v: bool) -> Result<Self::Ok> {
+ Ok(Num(NumKind::Bool(v)))
+ }
+
+ fn serialize_i8(self, v: i8) -> Result<Self::Ok> {
+ self.serialize_i64(i64::from(v))
+ }
+ fn serialize_i16(self, v: i16) -> Result<Self::Ok> {
+ self.serialize_i64(i64::from(v))
+ }
+ fn serialize_i32(self, v: i32) -> Result<Self::Ok> {
+ self.serialize_i64(i64::from(v))
+ }
+ fn serialize_i64(self, v: i64) -> Result<Self::Ok> {
+ Ok(Num(NumKind::Integer(v)))
+ }
+
+ fn serialize_u8(self, v: u8) -> Result<Self::Ok> {
+ self.serialize_u64(u64::from(v))
+ }
+ fn serialize_u16(self, v: u16) -> Result<Self::Ok> {
+ self.serialize_u64(u64::from(v))
+ }
+ fn serialize_u32(self, v: u32) -> Result<Self::Ok> {
+ self.serialize_u64(u64::from(v))
+ }
+ fn serialize_u64(self, v: u64) -> Result<Self::Ok> {
+ Ok(Num(NumKind::Natural(v)))
+ }
+
+ fn serialize_f32(self, v: f32) -> Result<Self::Ok> {
+ self.serialize_f64(f64::from(v))
+ }
+ fn serialize_f64(self, v: f64) -> Result<Self::Ok> {
+ Ok(Num(NumKind::Double(v.into())))
+ }
+
+ fn serialize_char(self, v: char) -> Result<Self::Ok> {
+ self.serialize_str(&v.to_string())
+ }
+ fn serialize_str(self, v: &str) -> Result<Self::Ok> {
+ Ok(Text(v.to_owned()))
+ }
+
+ fn serialize_bytes(self, _v: &[u8]) -> Result<Self::Ok> {
+ Err(ErrorKind::Serialize(
+ "Unsupported data for serialization: byte array".to_owned(),
+ )
+ .into())
+ }
+
+ fn serialize_none(self) -> Result<Self::Ok> {
+ Ok(Optional(None))
+ }
+ fn serialize_some<T>(self, v: &T) -> Result<Self::Ok>
+ where
+ T: ?Sized + ser::Serialize,
+ {
+ Ok(Optional(Some(Box::new(v.serialize(self)?))))
+ }
+
+ fn serialize_unit(self) -> Result<Self::Ok> {
+ Ok(Record(Default::default()))
+ }
+
+ fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok> {
+ self.serialize_unit()
+ }
+ fn serialize_newtype_struct<T>(
+ self,
+ _name: &'static str,
+ _value: &T,
+ ) -> Result<Self::Ok>
+ where
+ T: ?Sized + ser::Serialize,
+ {
+ Err(ErrorKind::Serialize(
+ "Unsupported data for serialization: newtype struct".to_owned(),
+ )
+ .into())
+ }
+ fn serialize_struct(
+ self,
+ _name: &'static str,
+ _len: usize,
+ ) -> Result<Self::SerializeStruct> {
+ Ok(StructSerializer::default())
+ }
+
+ fn serialize_unit_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ variant: &'static str,
+ ) -> Result<Self::Ok> {
+ Ok(Union(variant.to_owned(), None))
+ }
+ fn serialize_newtype_variant<T>(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ variant: &'static str,
+ value: &T,
+ ) -> Result<Self::Ok>
+ where
+ T: ?Sized + ser::Serialize,
+ {
+ let value = value.serialize(self)?;
+ Ok(Union(variant.to_owned(), Some(Box::new(value))))
+ }
+ fn serialize_tuple_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ _variant: &'static str,
+ _len: usize,
+ ) -> Result<Self::SerializeTupleVariant> {
+ Err(ErrorKind::Serialize(
+ "Unsupported data for serialization: tuple variant".to_owned(),
+ )
+ .into())
+ }
+ fn serialize_struct_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ _variant: &'static str,
+ _len: usize,
+ ) -> Result<Self::SerializeStructVariant> {
+ Err(ErrorKind::Serialize(
+ "Unsupported data for serialization: struct variant".to_owned(),
+ )
+ .into())
+ }
+
+ fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple> {
+ Ok(TupleSerializer::default())
+ }
+ fn serialize_tuple_struct(
+ self,
+ _name: &'static str,
+ _len: usize,
+ ) -> Result<Self::SerializeTupleStruct> {
+ Err(ErrorKind::Serialize(
+ "Unsupported data for serialization: tuple struct".to_owned(),
+ )
+ .into())
+ }
+
+ fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
+ Ok(SeqSerializer::default())
+ }
+
+ fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
+ Ok(MapSerializer::default())
+ }
+}
+
+#[derive(Default)]
+struct SeqSerializer(Vec<SimpleValue>);
+
+impl ser::SerializeSeq for SeqSerializer {
+ type Ok = SimpleValue;
+ type Error = Error;
+
+ fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+ where
+ T: ?Sized + ser::Serialize,
+ {
+ self.0.push(value.serialize(Serializer)?);
+ Ok(())
+ }
+
+ fn end(self) -> Result<Self::Ok> {
+ Ok(List(self.0))
+ }
+}
+
+#[derive(Default)]
+struct TupleSerializer(Vec<SimpleValue>);
+
+impl ser::SerializeTuple for TupleSerializer {
+ type Ok = SimpleValue;
+ type Error = Error;
+
+ fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+ where
+ T: ?Sized + ser::Serialize,
+ {
+ self.0.push(value.serialize(Serializer)?);
+ Ok(())
+ }
+
+ fn end(self) -> Result<Self::Ok> {
+ Ok(Record(
+ self.0
+ .into_iter()
+ .enumerate()
+ .map(|(i, x)| (format!("_{}", i + 1), x))
+ .collect(),
+ ))
+ }
+}
+
+#[derive(Default)]
+struct MapSerializer {
+ map: BTreeMap<String, SimpleValue>,
+ key: Option<String>,
+ val: Option<SimpleValue>,
+}
+
+impl ser::SerializeMap for MapSerializer {
+ type Ok = SimpleValue;
+ type Error = Error;
+
+ fn serialize_key<T>(&mut self, key: &T) -> Result<()>
+ where
+ T: ?Sized + ser::Serialize,
+ {
+ let key = match key.serialize(Serializer)? {
+ Text(key) => key,
+ _ => return Err(<Error as ser::Error>::custom("not a string")),
+ };
+ if let Some(val) = self.val.take() {
+ self.map.insert(key, val);
+ } else {
+ self.key = Some(key);
+ }
+ Ok(())
+ }
+
+ fn serialize_value<T>(&mut self, val: &T) -> Result<()>
+ where
+ T: ?Sized + ser::Serialize,
+ {
+ let val: SimpleValue = val.serialize(Serializer)?;
+ if let Some(key) = self.key.take() {
+ self.map.insert(key, val);
+ } else {
+ self.val = Some(val);
+ }
+ Ok(())
+ }
+
+ fn end(self) -> Result<Self::Ok> {
+ Ok(Record(self.map))
+ }
+}
+
+#[derive(Default)]
+struct StructSerializer(BTreeMap<String, SimpleValue>);
+
+impl ser::SerializeStruct for StructSerializer {
+ type Ok = SimpleValue;
+ type Error = Error;
+
+ fn serialize_field<T>(&mut self, key: &'static str, val: &T) -> Result<()>
+ where
+ T: ?Sized + ser::Serialize,
+ {
+ let val: SimpleValue = val.serialize(Serializer)?;
+ self.0.insert(key.into(), val);
+ Ok(())
+ }
+
+ fn end(self) -> Result<Self::Ok> {
+ Ok(Record(self.0))
+ }
+}
diff --git a/serde_dhall/src/static_type.rs b/serde_dhall/src/static_type.rs
index 3c5da18..7e6c27c 100644
--- a/serde_dhall/src/static_type.rs
+++ b/serde_dhall/src/static_type.rs
@@ -103,6 +103,26 @@ derive_builtin!(i32, Integer);
derive_builtin!(f64, Double);
derive_builtin!(f32, Double);
derive_builtin!(String, Text);
+derive_builtin!(&str, Text);
+
+impl StaticType for () {
+ fn static_type() -> SimpleType {
+ SimpleType::Record(vec![].into_iter().collect())
+ }
+}
+
+impl<A> StaticType for (A,)
+where
+ A: StaticType,
+{
+ fn static_type() -> SimpleType {
+ SimpleType::Record(
+ vec![("_1".to_owned(), A::static_type())]
+ .into_iter()
+ .collect(),
+ )
+ }
+}
impl<A, B> StaticType for (A, B)
where
@@ -121,6 +141,46 @@ where
}
}
+impl<A, B, C> StaticType for (A, B, C)
+where
+ A: StaticType,
+ B: StaticType,
+ C: StaticType,
+{
+ fn static_type() -> SimpleType {
+ SimpleType::Record(
+ vec![
+ ("_1".to_owned(), A::static_type()),
+ ("_2".to_owned(), B::static_type()),
+ ("_3".to_owned(), C::static_type()),
+ ]
+ .into_iter()
+ .collect(),
+ )
+ }
+}
+
+impl<A, B, C, D> StaticType for (A, B, C, D)
+where
+ A: StaticType,
+ B: StaticType,
+ C: StaticType,
+ D: StaticType,
+{
+ fn static_type() -> SimpleType {
+ SimpleType::Record(
+ vec![
+ ("_1".to_owned(), A::static_type()),
+ ("_2".to_owned(), B::static_type()),
+ ("_3".to_owned(), C::static_type()),
+ ("_4".to_owned(), D::static_type()),
+ ]
+ .into_iter()
+ .collect(),
+ )
+ }
+}
+
impl<T, E> StaticType for std::result::Result<T, E>
where
T: StaticType,
diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs
index d7116d9..ca05658 100644
--- a/serde_dhall/src/value.rs
+++ b/serde_dhall/src/value.rs
@@ -6,7 +6,7 @@ use dhall::semantics::{Hir, HirKind, Nir, NirKind};
pub use dhall::syntax::NumKind;
use dhall::syntax::{Expr, ExprKind, Span};
-use crate::{Error, ErrorKind, FromDhall, Result, Sealed};
+use crate::{Error, ErrorKind, FromDhall, Result, ToDhall};
#[doc(hidden)]
/// An arbitrary Dhall value.
@@ -292,6 +292,94 @@ impl SimpleValue {
_ => return None,
})
}
+
+ fn to_hir(&self, ty: &SimpleType) -> Result<Hir> {
+ use SimpleType as T;
+ use SimpleValue as V;
+ let hir = |k| Hir::new(HirKind::Expr(k), Span::Artificial);
+ let type_error = |msg| Error(ErrorKind::Serialize(msg));
+ let kind = match (self, ty) {
+ (V::Num(num @ NumKind::Bool(_)), T::Bool)
+ | (V::Num(num @ NumKind::Natural(_)), T::Natural)
+ | (V::Num(num @ NumKind::Integer(_)), T::Integer)
+ | (V::Num(num @ NumKind::Double(_)), T::Double) => {
+ ExprKind::Num(num.clone())
+ }
+ (V::Text(v), T::Text) => ExprKind::TextLit(v.clone().into()),
+ (V::Optional(None), T::Optional(t)) => ExprKind::Op(OpKind::App(
+ hir(ExprKind::Builtin(Builtin::OptionalNone)),
+ t.to_hir(),
+ )),
+ (V::Optional(Some(v)), T::Optional(t)) => {
+ ExprKind::SomeLit(v.to_hir(t)?)
+ }
+ (V::List(v), T::List(t)) if v.is_empty() => {
+ ExprKind::EmptyListLit(hir(ExprKind::Op(OpKind::App(
+ hir(ExprKind::Builtin(Builtin::List)),
+ t.to_hir(),
+ ))))
+ }
+ (V::List(v), T::List(t)) => ExprKind::NEListLit(
+ v.iter().map(|v| v.to_hir(t)).collect::<Result<_>>()?,
+ ),
+ (V::Record(v), T::Record(t)) => ExprKind::RecordLit(
+ v.iter()
+ .map(|(k, v)| match t.get(k) {
+ Some(t) => Ok((k.clone().into(), v.to_hir(t)?)),
+ None => Err(type_error(format!(
+ "expected a value of type {}, found {:?}",
+ ty.to_hir().to_expr(Default::default()),
+ self
+ ))),
+ })
+ .collect::<Result<_>>()?,
+ ),
+ (V::Union(variant, Some(v)), T::Union(t)) => match t.get(variant) {
+ Some(Some(variant_t)) => ExprKind::Op(OpKind::App(
+ hir(ExprKind::Op(OpKind::Field(
+ ty.to_hir(),
+ variant.clone().into(),
+ ))),
+ v.to_hir(variant_t)?,
+ )),
+ _ => {
+ return Err(type_error(format!(
+ "expected a value of type {}, found {:?}",
+ ty.to_hir().to_expr(Default::default()),
+ self
+ )))
+ }
+ },
+ (V::Union(variant, None), T::Union(t)) => match t.get(variant) {
+ Some(None) => ExprKind::Op(OpKind::Field(
+ ty.to_hir(),
+ variant.clone().into(),
+ )),
+ _ => {
+ return Err(type_error(format!(
+ "expected a value of type {}, found {:?}",
+ ty.to_hir().to_expr(Default::default()),
+ self
+ )))
+ }
+ },
+ _ => {
+ return Err(type_error(format!(
+ "expected a value of type {}, found {:?}",
+ ty.to_hir().to_expr(Default::default()),
+ self
+ )))
+ }
+ };
+ Ok(hir(kind))
+ }
+ pub(crate) fn into_value(self, ty: &SimpleType) -> Result<Value> {
+ Ok(Value {
+ hir: self.to_hir(ty)?,
+ as_simple_val: Some(self),
+ as_simple_ty: None,
+ })
+ }
}
impl SimpleType {
@@ -376,8 +464,10 @@ impl SimpleType {
}
}
-impl Sealed for Value {}
-impl Sealed for SimpleType {}
+impl crate::deserialize::Sealed for Value {}
+impl crate::deserialize::Sealed for SimpleType {}
+impl crate::serialize::Sealed for Value {}
+impl crate::serialize::Sealed for SimpleValue {}
impl FromDhall for Value {
fn from_dhall(v: &Value) -> Result<Self> {
@@ -394,6 +484,16 @@ impl FromDhall for SimpleType {
})
}
}
+impl ToDhall for Value {
+ fn to_dhall(&self, _ty: &SimpleType) -> Result<Value> {
+ Ok(self.clone())
+ }
+}
+impl ToDhall for SimpleValue {
+ fn to_dhall(&self, ty: &SimpleType) -> Result<Value> {
+ self.clone().into_value(ty)
+ }
+}
impl Eq for Value {}
impl PartialEq for Value {
diff --git a/serde_dhall/tests/de.rs b/serde_dhall/tests/de.rs
index 3abaad2..0b43de2 100644
--- a/serde_dhall/tests/de.rs
+++ b/serde_dhall/tests/de.rs
@@ -1,62 +1,5 @@
use serde::Deserialize;
-use serde_dhall::{from_str, FromDhall, NumKind, SimpleValue, StaticType};
-
-#[test]
-fn test_de_typed() {
- fn parse<T: FromDhall + StaticType>(s: &str) -> T {
- from_str(s).static_type_annotation().parse().unwrap()
- }
-
- assert_eq!(parse::<bool>("True"), true);
-
- assert_eq!(parse::<u64>("1"), 1);
- assert_eq!(parse::<u32>("1"), 1);
- assert_eq!(parse::<usize>("1"), 1);
-
- assert_eq!(parse::<i64>("+1"), 1);
- assert_eq!(parse::<i32>("+1"), 1);
- assert_eq!(parse::<isize>("+1"), 1);
-
- assert_eq!(parse::<f64>("1.0"), 1.0);
- assert_eq!(parse::<f32>("1.0"), 1.0);
-
- assert_eq!(parse::<String>(r#""foo""#), "foo".to_owned());
- assert_eq!(parse::<Vec<u64>>("[] : List Natural"), <Vec<u64>>::new());
- assert_eq!(parse::<Vec<u64>>("[1, 2]"), vec![1, 2]);
- assert_eq!(parse::<Option<u64>>("None Natural"), None);
- assert_eq!(parse::<Option<u64>>("Some 1"), Some(1));
-
- assert_eq!(
- parse::<(u64, String)>(r#"{ _1 = 1, _2 = "foo" }"#),
- (1, "foo".to_owned())
- );
-
- #[derive(Debug, PartialEq, Eq, Deserialize, StaticType)]
- struct Foo {
- x: u64,
- y: i64,
- }
- assert_eq!(parse::<Foo>("{ x = 1, y = -2 }"), Foo { x: 1, y: -2 });
-
- #[derive(Debug, PartialEq, Eq, Deserialize, StaticType)]
- enum Bar {
- X(u64),
- Y(i64),
- }
- assert_eq!(parse::<Bar>("< X: Natural | Y: Integer >.X 1"), Bar::X(1));
-
- #[derive(Debug, PartialEq, Eq, Deserialize, StaticType)]
- enum Baz {
- X,
- Y(i64),
- }
- assert_eq!(parse::<Baz>("< X | Y: Integer >.X"), Baz::X);
-
- assert!(from_str("< X | Y: Integer >.Y")
- .static_type_annotation()
- .parse::<Baz>()
- .is_err());
-}
+use serde_dhall::{from_str, FromDhall, NumKind, SimpleValue};
#[test]
fn test_de_untyped() {
diff --git a/serde_dhall/tests/serde.rs b/serde_dhall/tests/serde.rs
new file mode 100644
index 0000000..39f2f79
--- /dev/null
+++ b/serde_dhall/tests/serde.rs
@@ -0,0 +1,145 @@
+mod serde {
+ use serde::{Deserialize, Serialize};
+ use serde_dhall::{from_str, serialize, FromDhall, StaticType, ToDhall};
+
+ fn assert_de<T>(s: &str, x: T)
+ where
+ T: FromDhall + StaticType + PartialEq + std::fmt::Debug,
+ {
+ assert_eq!(
+ from_str(s)
+ .static_type_annotation()
+ .parse::<T>()
+ .map_err(|e| e.to_string()),
+ Ok(x)
+ );
+ }
+ fn assert_ser<T>(s: &str, x: T)
+ where
+ T: ToDhall + StaticType + PartialEq + std::fmt::Debug,
+ {
+ assert_eq!(
+ serialize(&x)
+ .static_type_annotation()
+ .to_string()
+ .map_err(|e| e.to_string()),
+ Ok(s.to_string())
+ );
+ }
+ fn assert_serde<T>(s: &str, x: T)
+ where
+ T: ToDhall
+ + FromDhall
+ + StaticType
+ + PartialEq
+ + std::fmt::Debug
+ + Clone,
+ {
+ assert_de(s, x.clone());
+ assert_ser(s, x);
+ }
+
+ #[test]
+ fn numbers() {
+ assert_serde("True", true);
+
+ assert_serde("1", 1u64);
+ assert_serde("1", 1u32);
+ assert_serde("1", 1usize);
+
+ assert_serde("+1", 1i64);
+ assert_serde("+1", 1i32);
+ assert_serde("+1", 1isize);
+
+ assert_serde("1.0", 1.0f64);
+ assert_serde("1.0", 1.0f32);
+ }
+
+ #[test]
+ fn text() {
+ assert_serde(r#""foo""#, "foo".to_owned());
+ assert_ser(r#""foo""#, "foo");
+ }
+
+ #[test]
+ fn list() {
+ assert_serde("[] : List Natural", <Vec<u64>>::new());
+ assert_serde("[] : List Text", <Vec<String>>::new());
+ assert_serde(
+ r#"["foo", "bar"]"#,
+ vec!["foo".to_owned(), "bar".to_owned()],
+ );
+ assert_ser(r#"["foo", "bar"]"#, vec!["foo", "bar"]);
+ assert_serde::<Vec<u64>>("[1, 2]", vec![1, 2]);
+ }
+
+ #[test]
+ fn optional() {
+ assert_serde::<Option<u64>>("None Natural", None);
+ assert_serde::<Option<String>>("None Text", None);
+ assert_serde("Some 1", Some(1u64));
+ }
+
+ #[test]
+ fn tuple() {
+ assert_serde::<()>(r#"{=}"#, ());
+ assert_serde::<(u64, String)>(
+ r#"{ _1 = 1, _2 = "foo" }"#,
+ (1, "foo".to_owned()),
+ );
+ assert_serde::<(u64, u64, u64, u64)>(
+ r#"{ _1 = 1, _2 = 2, _3 = 3, _4 = 4 }"#,
+ (1, 2, 3, 4),
+ );
+ }
+
+ #[test]
+ fn structs() {
+ // #[derive(
+ // Debug, Clone, PartialEq, Eq, Deserialize, Serialize, StaticType,
+ // )]
+ // struct Foo;
+ // assert_serde::<Foo>("{=}", Foo);
+
+ // #[derive(
+ // Debug, Clone, PartialEq, Eq, Deserialize, Serialize, StaticType,
+ // )]
+ // struct Bar(u64);
+ // assert_serde::<Bar>("{ _1 = 1 }", Bar (1));
+
+ #[derive(
+ Debug, Clone, PartialEq, Eq, Deserialize, Serialize, StaticType,
+ )]
+ struct Baz {
+ x: u64,
+ y: i64,
+ }
+ assert_serde::<Baz>("{ x = 1, y = -2 }", Baz { x: 1, y: -2 });
+ }
+
+ #[test]
+ fn enums() {
+ #[derive(
+ Debug, Clone, PartialEq, Eq, Deserialize, Serialize, StaticType,
+ )]
+ enum Foo {
+ X(u64),
+ Y(i64),
+ }
+ assert_serde::<Foo>("< X: Natural | Y: Integer >.X 1", Foo::X(1));
+
+ #[derive(
+ Debug, Clone, PartialEq, Eq, Deserialize, Serialize, StaticType,
+ )]
+ enum Bar {
+ X,
+ Y(i64),
+ }
+ assert_serde::<Bar>("< X | Y: Integer >.X", Bar::X);
+
+ assert!(from_str("< X | Y: Integer >.Y")
+ .static_type_annotation()
+ .parse::<Bar>()
+ .is_err());
+ }
+}