diff options
Diffstat (limited to 'serde_dhall')
-rw-r--r-- | serde_dhall/src/deserialize.rs | 16 | ||||
-rw-r--r-- | serde_dhall/src/error.rs | 11 | ||||
-rw-r--r-- | serde_dhall/src/lib.rs | 6 | ||||
-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.rs | 2 | ||||
-rw-r--r-- | serde_dhall/src/options/ser.rs | 67 | ||||
-rw-r--r-- | serde_dhall/src/serialize.rs | 312 | ||||
-rw-r--r-- | serde_dhall/src/static_type.rs | 60 | ||||
-rw-r--r-- | serde_dhall/src/value.rs | 106 | ||||
-rw-r--r-- | serde_dhall/tests/de.rs | 59 | ||||
-rw-r--r-- | serde_dhall/tests/serde.rs | 145 |
11 files changed, 724 insertions, 72 deletions
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()); + } +} |