From 84796fd247eb1a13fcd092a7cd7ec2d587b261bd Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 8 Mar 2020 17:11:34 +0000 Subject: Add SimpleValue type to facilitate deserialization --- serde_dhall/src/lib.rs | 17 +++++--- serde_dhall/src/serde.rs | 102 ++++++++++++++++------------------------------- 2 files changed, 46 insertions(+), 73 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 0a53420..55e098c 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -182,16 +182,19 @@ pub use value::Value; #[doc(hidden)] pub mod value { use dhall::syntax::Builtin; - use dhall::{Normalized, NormalizedExpr, Parsed}; + use dhall::{Normalized, NormalizedExpr, Parsed, SimpleValue}; - use super::de::{Error, Result}; + use super::de::Error; /// A Dhall value #[derive(Debug, Clone, PartialEq, Eq)] pub struct Value(Normalized); impl Value { - pub fn from_str(s: &str, ty: Option<&Value>) -> Result { + pub fn from_str( + s: &str, + ty: Option<&Value>, + ) -> super::de::Result { Value::from_str_using_dhall_error_type(s, ty).map_err(Error::Dhall) } fn from_str_using_dhall_error_type( @@ -205,8 +208,10 @@ pub mod value { }; Ok(Value(typed.normalize())) } - pub(crate) fn to_expr(&self) -> NormalizedExpr { - self.0.to_expr() + pub(crate) fn to_simple_value( + &self, + ) -> Result { + self.0.to_simple_value() } pub(crate) fn as_normalized(&self) -> &Normalized { &self.0 @@ -241,7 +246,7 @@ pub mod value { impl super::de::sealed::Sealed for Value {} impl super::de::Deserialize for Value { - fn from_dhall(v: &Value) -> Result { + fn from_dhall(v: &Value) -> super::de::Result { Ok(v.clone()) } } diff --git a/serde_dhall/src/serde.rs b/serde_dhall/src/serde.rs index 4fd7815..9cb67d1 100644 --- a/serde_dhall/src/serde.rs +++ b/serde_dhall/src/serde.rs @@ -4,25 +4,31 @@ use serde::de::value::{ MapAccessDeserializer, MapDeserializer, SeqDeserializer, }; -use dhall::syntax::{ExprKind, LitKind}; -use dhall::NormalizedExpr; +use dhall::syntax::LitKind; +use dhall::{SValKind, SimpleValue}; use crate::de::{Deserialize, Error, Result}; use crate::Value; impl<'a, T> crate::de::sealed::Sealed for T where T: serde::Deserialize<'a> {} +struct Deserializer<'a>(Cow<'a, SimpleValue>); + impl<'a, T> Deserialize for T where T: serde::Deserialize<'a>, { fn from_dhall(v: &Value) -> Result { - T::deserialize(Deserializer(Cow::Owned(v.to_expr()))) + let sval = v.to_simple_value().map_err(|expr| { + Error::Deserialize(format!( + "this cannot be deserialized into the serde data model: {}", + expr + )) + })?; + T::deserialize(Deserializer(Cow::Owned(sval))) } } -struct Deserializer<'a>(Cow<'a, NormalizedExpr>); - impl<'de: 'a, 'a> serde::de::IntoDeserializer<'de, Error> for Deserializer<'a> { type Deserializer = Deserializer<'a>; fn into_deserializer(self) -> Self::Deserializer { @@ -38,17 +44,11 @@ impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { V: serde::de::Visitor<'de>, { use std::convert::TryInto; - use ExprKind::*; use LitKind::*; - let expr = self.0.as_ref(); - let not_serde_compatible = || { - Err(Error::Deserialize(format!( - "this cannot be deserialized into the serde data model: {}", - expr - ))) - }; + use SValKind::*; - match expr.kind() { + let val = |x| Deserializer(Cow::Borrowed(x)); + match self.0.kind() { Lit(Bool(x)) => visitor.visit_bool(*x), Lit(Natural(x)) => { if let Ok(x64) = (*x).try_into() { @@ -69,54 +69,25 @@ impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { } } Lit(Double(x)) => visitor.visit_f64((*x).into()), - TextLit(x) => { - // Normal form ensures that the tail is empty. - assert!(x.tail().is_empty()); - visitor.visit_str(x.head()) + Text(x) => visitor.visit_str(x), + List(xs) => { + visitor.visit_seq(SeqDeserializer::new(xs.iter().map(val))) } - EmptyListLit(..) => { - visitor.visit_seq(SeqDeserializer::new(None::<()>.into_iter())) - } - NEListLit(xs) => visitor.visit_seq(SeqDeserializer::new( - xs.iter().map(|x| Deserializer(Cow::Borrowed(x))), + Optional(None) => visitor.visit_none(), + Optional(Some(x)) => visitor.visit_some(val(x)), + Record(m) => visitor.visit_map(MapDeserializer::new( + m.iter().map(|(k, v)| (k.as_ref(), val(v))), )), - SomeLit(x) => visitor.visit_some(Deserializer(Cow::Borrowed(x))), - App(f, x) => match f.kind() { - Builtin(dhall::syntax::Builtin::OptionalNone) => { - visitor.visit_none() - } - Field(y, name) => match y.kind() { - UnionType(..) => { - let name: String = name.into(); - visitor.visit_enum(MapAccessDeserializer::new( - MapDeserializer::new( - Some((name, Deserializer(Cow::Borrowed(x)))) - .into_iter(), - ), - )) - } - _ => not_serde_compatible(), - }, - _ => not_serde_compatible(), - }, - RecordLit(m) => visitor - .visit_map(MapDeserializer::new(m.iter().map(|(k, v)| { - (k.as_ref(), Deserializer(Cow::Borrowed(v))) - }))), - Field(y, name) => match y.kind() { - UnionType(..) => { - let name: String = name.into(); - visitor.visit_enum(MapAccessDeserializer::new( - MapDeserializer::new(Some((name, ())).into_iter()), - )) - } - _ => not_serde_compatible(), - }, - Const(..) | Var(..) | Lam(..) | Pi(..) | Let(..) | Annot(..) - | Assert(..) | Builtin(..) | BinOp(..) | BoolIf(..) - | RecordType(..) | UnionType(..) | Merge(..) | ToMap(..) - | Projection(..) | ProjectionByExpr(..) | Completion(..) - | Import(..) => not_serde_compatible(), + Union(field_name, Some(x)) => visitor.visit_enum( + MapAccessDeserializer::new(MapDeserializer::new( + Some((field_name.as_str(), val(x))).into_iter(), + )), + ), + Union(field_name, None) => visitor.visit_enum( + MapAccessDeserializer::new(MapDeserializer::new( + Some((field_name.as_str(), ())).into_iter(), + )), + ), } } @@ -124,14 +95,11 @@ impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { where V: serde::de::Visitor<'de>, { - use ExprKind::*; - let expr = self.0.as_ref(); - - match expr.kind() { + let val = |x| Deserializer(Cow::Borrowed(x)); + match self.0.kind() { // Blindly takes keys in sorted order. - RecordLit(m) => visitor.visit_seq(SeqDeserializer::new( - m.iter().map(|(_, v)| Deserializer(Cow::Borrowed(v))), - )), + SValKind::Record(m) => visitor + .visit_seq(SeqDeserializer::new(m.iter().map(|(_, v)| val(v)))), _ => self.deserialize_any(visitor), } } -- cgit v1.2.3 From 4e11e882b7d064e9ddf997f9a465206cf6e471aa Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 8 Mar 2020 17:36:51 +0000 Subject: Split serde_dhall::Value to separate values from types --- serde_dhall/src/lib.rs | 86 ++++++++++++++++++++++++++++++------------ serde_dhall/src/static_type.rs | 26 ++++++------- 2 files changed, 75 insertions(+), 37 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 55e098c..5f386e3 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -106,17 +106,17 @@ //! There are two ways to typecheck a Dhall value: you can provide the type as Dhall text or you //! can let Rust infer it for you. //! -//! To provide a type written in Dhall, first parse it into a [`serde_dhall::Value`][Value], then +//! To provide a type written in Dhall, first parse it into a [`serde_dhall::Type`][Type], then //! pass it to [`from_str_check_type`][from_str_check_type]. //! //! ```rust //! # fn main() -> serde_dhall::de::Result<()> { -//! use serde_dhall::Value; +//! use serde_dhall::Type; //! use std::collections::HashMap; //! //! // Parse a Dhall type //! let point_type_str = "{ x: Natural, y: Natural }"; -//! let point_type: Value = serde_dhall::from_str(point_type_str)?; +//! let point_type: Type = serde_dhall::from_str(point_type_str)?; //! //! // Some Dhall data //! let point_data = "{ x = 1, y = 1 + 1 }"; @@ -176,30 +176,29 @@ pub use de::{from_str, from_str_auto_type, from_str_check_type}; pub use dhall_proc_macros::StaticType; pub use static_type::StaticType; #[doc(inline)] +pub use ty::Type; +#[doc(inline)] pub use value::Value; // A Dhall value. #[doc(hidden)] pub mod value { - use dhall::syntax::Builtin; use dhall::{Normalized, NormalizedExpr, Parsed, SimpleValue}; use super::de::Error; + use super::Type; /// A Dhall value #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Value(Normalized); + pub struct Value(pub(crate) Normalized); impl Value { - pub fn from_str( - s: &str, - ty: Option<&Value>, - ) -> super::de::Result { + pub fn from_str(s: &str, ty: Option<&Type>) -> super::de::Result { Value::from_str_using_dhall_error_type(s, ty).map_err(Error::Dhall) } fn from_str_using_dhall_error_type( s: &str, - ty: Option<&Value>, + ty: Option<&Type>, ) -> dhall::error::Result { let resolved = Parsed::parse_str(s)?.resolve()?; let typed = match ty { @@ -213,41 +212,79 @@ pub mod value { ) -> Result { self.0.to_simple_value() } + } + + impl super::de::sealed::Sealed for Value {} + + impl super::de::Deserialize for Value { + fn from_dhall(v: &Value) -> super::de::Result { + Ok(v.clone()) + } + } +} + +// A Dhall type. +#[doc(hidden)] +pub mod ty { + use dhall::syntax::Builtin; + use dhall::{Normalized, Parsed}; + + use super::de::Error; + + /// A Dhall value + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Type(Normalized); + + impl Type { + pub fn from_str(s: &str, ty: Option<&Type>) -> super::de::Result { + Type::from_str_using_dhall_error_type(s, ty).map_err(Error::Dhall) + } + fn from_str_using_dhall_error_type( + s: &str, + ty: Option<&Type>, + ) -> dhall::error::Result { + let resolved = Parsed::parse_str(s)?.resolve()?; + let typed = match ty { + None => resolved.typecheck()?, + Some(t) => resolved.typecheck_with(t.as_normalized())?, + }; + Ok(Type(typed.normalize())) + } pub(crate) fn as_normalized(&self) -> &Normalized { &self.0 } pub(crate) fn make_builtin_type(b: Builtin) -> Self { - Value(Normalized::make_builtin_type(b)) + Type(Normalized::make_builtin_type(b)) } - pub(crate) fn make_optional_type(t: Value) -> Self { - Value(Normalized::make_optional_type(t.0)) + pub(crate) fn make_optional_type(t: Type) -> Self { + Type(Normalized::make_optional_type(t.0)) } - pub(crate) fn make_list_type(t: Value) -> Self { - Value(Normalized::make_list_type(t.0)) + pub(crate) fn make_list_type(t: Type) -> Self { + Type(Normalized::make_list_type(t.0)) } // Made public for the StaticType derive macro #[doc(hidden)] pub fn make_record_type( - kts: impl Iterator, + kts: impl Iterator, ) -> Self { - Value(Normalized::make_record_type(kts.map(|(k, t)| (k, t.0)))) + Type(Normalized::make_record_type(kts.map(|(k, t)| (k, t.0)))) } #[doc(hidden)] pub fn make_union_type( - kts: impl Iterator)>, + kts: impl Iterator)>, ) -> Self { - Value(Normalized::make_union_type( + Type(Normalized::make_union_type( kts.map(|(k, t)| (k, t.map(|t| t.0))), )) } } - impl super::de::sealed::Sealed for Value {} + impl super::de::sealed::Sealed for Type {} - impl super::de::Deserialize for Value { - fn from_dhall(v: &Value) -> super::de::Result { - Ok(v.clone()) + impl super::de::Deserialize for Type { + fn from_dhall(v: &super::Value) -> super::de::Result { + Ok(Type(v.0.clone())) } } } @@ -255,6 +292,7 @@ pub mod value { /// Deserialize Dhall data to a Rust data structure. pub mod de { use super::StaticType; + use super::Type; use super::Value; pub use error::{Error, Result}; @@ -324,7 +362,7 @@ pub mod de { /// /// Like [from_str], but this additionally checks that /// the type of the provided expression matches the supplied type. - pub fn from_str_check_type(s: &str, ty: &Value) -> Result + pub fn from_str_check_type(s: &str, ty: &Type) -> Result where T: Deserialize, { diff --git a/serde_dhall/src/static_type.rs b/serde_dhall/src/static_type.rs index 1eb9150..921b296 100644 --- a/serde_dhall/src/static_type.rs +++ b/serde_dhall/src/static_type.rs @@ -1,6 +1,6 @@ use dhall::syntax::Builtin; -use crate::Value; +use crate::Type; /// A Rust type that can be represented as a Dhall type. /// @@ -14,14 +14,14 @@ use crate::Value; /// [StaticType] because each different value would /// have a different Dhall record type. pub trait StaticType { - fn static_type() -> Value; + fn static_type() -> Type; } macro_rules! derive_builtin { ($ty:ty, $builtin:ident) => { impl StaticType for $ty { - fn static_type() -> Value { - Value::make_builtin_type(Builtin::$builtin) + fn static_type() -> Type { + Type::make_builtin_type(Builtin::$builtin) } } }; @@ -43,8 +43,8 @@ where A: StaticType, B: StaticType, { - fn static_type() -> Value { - Value::make_record_type( + fn static_type() -> Type { + Type::make_record_type( vec![ ("_1".to_owned(), A::static_type()), ("_2".to_owned(), B::static_type()), @@ -59,8 +59,8 @@ where T: StaticType, E: StaticType, { - fn static_type() -> Value { - Value::make_union_type( + fn static_type() -> Type { + Type::make_union_type( vec![ ("Ok".to_owned(), Some(T::static_type())), ("Err".to_owned(), Some(E::static_type())), @@ -74,8 +74,8 @@ impl StaticType for Option where T: StaticType, { - fn static_type() -> Value { - Value::make_optional_type(T::static_type()) + fn static_type() -> Type { + Type::make_optional_type(T::static_type()) } } @@ -83,8 +83,8 @@ impl StaticType for Vec where T: StaticType, { - fn static_type() -> Value { - Value::make_list_type(T::static_type()) + fn static_type() -> Type { + Type::make_list_type(T::static_type()) } } @@ -92,7 +92,7 @@ impl<'a, T> StaticType for &'a T where T: StaticType, { - fn static_type() -> Value { + fn static_type() -> Type { T::static_type() } } -- cgit v1.2.3 From 94850b720b0171444694452027f1baf947b3c18f Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 8 Mar 2020 21:47:26 +0000 Subject: Add SimpleType to mirror SimpleValue --- serde_dhall/src/lib.rs | 61 +++++++++++++++++++++--------------------- serde_dhall/src/static_type.rs | 8 +++--- 2 files changed, 35 insertions(+), 34 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 5f386e3..d019238 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -183,7 +183,7 @@ pub use value::Value; // A Dhall value. #[doc(hidden)] pub mod value { - use dhall::{Normalized, NormalizedExpr, Parsed, SimpleValue}; + use dhall::{Normalized, NormalizedExpr, Parsed, SimpleType, SimpleValue}; use super::de::Error; use super::Type; @@ -203,7 +203,7 @@ pub mod value { let resolved = Parsed::parse_str(s)?.resolve()?; let typed = match ty { None => resolved.typecheck()?, - Some(t) => resolved.typecheck_with(t.as_normalized())?, + Some(t) => resolved.typecheck_with(&t.to_normalized())?, }; Ok(Value(typed.normalize())) } @@ -212,6 +212,11 @@ pub mod value { ) -> Result { self.0.to_simple_value() } + pub(crate) fn to_simple_type( + &self, + ) -> Result { + self.0.to_simple_type() + } } impl super::de::sealed::Sealed for Value {} @@ -226,56 +231,46 @@ pub mod value { // A Dhall type. #[doc(hidden)] pub mod ty { - use dhall::syntax::Builtin; - use dhall::{Normalized, Parsed}; + use dhall::{Normalized, STyKind, SimpleType}; use super::de::Error; - /// A Dhall value + /// A Dhall type #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Type(Normalized); + pub struct Type(SimpleType); impl Type { - pub fn from_str(s: &str, ty: Option<&Type>) -> super::de::Result { - Type::from_str_using_dhall_error_type(s, ty).map_err(Error::Dhall) - } - fn from_str_using_dhall_error_type( - s: &str, - ty: Option<&Type>, - ) -> dhall::error::Result { - let resolved = Parsed::parse_str(s)?.resolve()?; - let typed = match ty { - None => resolved.typecheck()?, - Some(t) => resolved.typecheck_with(t.as_normalized())?, - }; - Ok(Type(typed.normalize())) - } - pub(crate) fn as_normalized(&self) -> &Normalized { - &self.0 + pub(crate) fn to_normalized(&self) -> Normalized { + self.0.clone().into_normalized() } - pub(crate) fn make_builtin_type(b: Builtin) -> Self { - Type(Normalized::make_builtin_type(b)) + pub(crate) fn from_simple_type(ty: SimpleType) -> Self { + Type(ty) + } + pub(crate) fn from_stykind(k: STyKind) -> Self { + Type(SimpleType::new(k)) } pub(crate) fn make_optional_type(t: Type) -> Self { - Type(Normalized::make_optional_type(t.0)) + Type::from_stykind(STyKind::Optional(t.0)) } pub(crate) fn make_list_type(t: Type) -> Self { - Type(Normalized::make_list_type(t.0)) + Type::from_stykind(STyKind::List(t.0)) } // Made public for the StaticType derive macro #[doc(hidden)] pub fn make_record_type( kts: impl Iterator, ) -> Self { - Type(Normalized::make_record_type(kts.map(|(k, t)| (k, t.0)))) + Type::from_stykind(STyKind::Record( + kts.map(|(k, t)| (k, t.0)).collect(), + )) } #[doc(hidden)] pub fn make_union_type( kts: impl Iterator)>, ) -> Self { - Type(Normalized::make_union_type( - kts.map(|(k, t)| (k, t.map(|t| t.0))), + Type::from_stykind(STyKind::Union( + kts.map(|(k, t)| (k, t.map(|t| t.0))).collect(), )) } } @@ -284,7 +279,13 @@ pub mod ty { impl super::de::Deserialize for Type { fn from_dhall(v: &super::Value) -> super::de::Result { - Ok(Type(v.0.clone())) + let sty = v.to_simple_type().map_err(|expr| { + Error::Deserialize(format!( + "this cannot be deserialized into a simple type: {}", + expr + )) + })?; + Ok(Type(sty)) } } } diff --git a/serde_dhall/src/static_type.rs b/serde_dhall/src/static_type.rs index 921b296..bc5f366 100644 --- a/serde_dhall/src/static_type.rs +++ b/serde_dhall/src/static_type.rs @@ -1,4 +1,4 @@ -use dhall::syntax::Builtin; +use dhall::{STyKind, SimpleType}; use crate::Type; @@ -18,10 +18,10 @@ pub trait StaticType { } macro_rules! derive_builtin { - ($ty:ty, $builtin:ident) => { - impl StaticType for $ty { + ($rust_ty:ty, $dhall_ty:ident) => { + impl StaticType for $rust_ty { fn static_type() -> Type { - Type::make_builtin_type(Builtin::$builtin) + Type::from_simple_type(SimpleType::new(STyKind::$dhall_ty)) } } }; -- cgit v1.2.3 From 60425d58151fef142b066d523dc4d5e832070b9c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 13 Mar 2020 14:30:15 +0000 Subject: Add new Value type in API --- serde_dhall/src/lib.rs | 47 ++++++++++++++++++++++------------------------- serde_dhall/src/serde.rs | 4 ++-- 2 files changed, 24 insertions(+), 27 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index d019238..1f7ecf8 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -183,38 +183,26 @@ pub use value::Value; // A Dhall value. #[doc(hidden)] pub mod value { - use dhall::{Normalized, NormalizedExpr, Parsed, SimpleType, SimpleValue}; + use dhall::{SimpleType, SimpleValue}; use super::de::Error; use super::Type; /// A Dhall value #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Value(pub(crate) Normalized); + pub struct Value(pub(crate) dhall::Value); impl Value { pub fn from_str(s: &str, ty: Option<&Type>) -> super::de::Result { - Value::from_str_using_dhall_error_type(s, ty).map_err(Error::Dhall) + let ty = ty.map(|t| t.to_dhall_value()); + let val = dhall::Value::from_str_with_annot(s, ty.as_ref()) + .map_err(Error::Dhall)?; + Ok(Value(val)) } - fn from_str_using_dhall_error_type( - s: &str, - ty: Option<&Type>, - ) -> dhall::error::Result { - let resolved = Parsed::parse_str(s)?.resolve()?; - let typed = match ty { - None => resolved.typecheck()?, - Some(t) => resolved.typecheck_with(&t.to_normalized())?, - }; - Ok(Value(typed.normalize())) - } - pub(crate) fn to_simple_value( - &self, - ) -> Result { + pub(crate) fn to_simple_value(&self) -> Option { self.0.to_simple_value() } - pub(crate) fn to_simple_type( - &self, - ) -> Result { + pub(crate) fn to_simple_type(&self) -> Option { self.0.to_simple_type() } } @@ -226,12 +214,21 @@ pub mod value { Ok(v.clone()) } } + + impl std::fmt::Display for Value { + fn fmt( + &self, + f: &mut std::fmt::Formatter, + ) -> Result<(), std::fmt::Error> { + self.0.fmt(f) + } + } } // A Dhall type. #[doc(hidden)] pub mod ty { - use dhall::{Normalized, STyKind, SimpleType}; + use dhall::{STyKind, SimpleType}; use super::de::Error; @@ -240,8 +237,8 @@ pub mod ty { pub struct Type(SimpleType); impl Type { - pub(crate) fn to_normalized(&self) -> Normalized { - self.0.clone().into_normalized() + pub(crate) fn to_dhall_value(&self) -> dhall::Value { + self.0.clone().into_normalized().to_value() } pub(crate) fn from_simple_type(ty: SimpleType) -> Self { @@ -279,10 +276,10 @@ pub mod ty { impl super::de::Deserialize for Type { fn from_dhall(v: &super::Value) -> super::de::Result { - let sty = v.to_simple_type().map_err(|expr| { + let sty = v.to_simple_type().ok_or_else(|| { Error::Deserialize(format!( "this cannot be deserialized into a simple type: {}", - expr + v )) })?; Ok(Type(sty)) diff --git a/serde_dhall/src/serde.rs b/serde_dhall/src/serde.rs index 9cb67d1..5d8fcf3 100644 --- a/serde_dhall/src/serde.rs +++ b/serde_dhall/src/serde.rs @@ -19,10 +19,10 @@ where T: serde::Deserialize<'a>, { fn from_dhall(v: &Value) -> Result { - let sval = v.to_simple_value().map_err(|expr| { + let sval = v.to_simple_value().ok_or_else(|| { Error::Deserialize(format!( "this cannot be deserialized into the serde data model: {}", - expr + v )) })?; T::deserialize(Deserializer(Cow::Owned(sval))) -- cgit v1.2.3 From 6fdae6f8026fe9357de63c5557676f4def3eaf92 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 13 Mar 2020 16:16:56 +0000 Subject: Remove Normalized and friends from public API --- serde_dhall/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 1f7ecf8..103fdc3 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -238,7 +238,7 @@ pub mod ty { impl Type { pub(crate) fn to_dhall_value(&self) -> dhall::Value { - self.0.clone().into_normalized().to_value() + self.0.to_value() } pub(crate) fn from_simple_type(ty: SimpleType) -> Self { -- cgit v1.2.3 From 2f5c45fd2f712f7befe6f7c92b62dc76d5f77538 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 13 Mar 2020 16:50:49 +0000 Subject: Rename LitKind to NumKind --- serde_dhall/src/serde.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/serde.rs b/serde_dhall/src/serde.rs index 5d8fcf3..227ba31 100644 --- a/serde_dhall/src/serde.rs +++ b/serde_dhall/src/serde.rs @@ -4,7 +4,7 @@ use serde::de::value::{ MapAccessDeserializer, MapDeserializer, SeqDeserializer, }; -use dhall::syntax::LitKind; +use dhall::syntax::NumKind; use dhall::{SValKind, SimpleValue}; use crate::de::{Deserialize, Error, Result}; @@ -44,13 +44,13 @@ impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { V: serde::de::Visitor<'de>, { use std::convert::TryInto; - use LitKind::*; + use NumKind::*; use SValKind::*; let val = |x| Deserializer(Cow::Borrowed(x)); match self.0.kind() { - Lit(Bool(x)) => visitor.visit_bool(*x), - Lit(Natural(x)) => { + Num(Bool(x)) => visitor.visit_bool(*x), + Num(Natural(x)) => { if let Ok(x64) = (*x).try_into() { visitor.visit_u64(x64) } else if let Ok(x32) = (*x).try_into() { @@ -59,7 +59,7 @@ impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { unimplemented!() } } - Lit(Integer(x)) => { + Num(Integer(x)) => { if let Ok(x64) = (*x).try_into() { visitor.visit_i64(x64) } else if let Ok(x32) = (*x).try_into() { @@ -68,7 +68,7 @@ impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { unimplemented!() } } - Lit(Double(x)) => visitor.visit_f64((*x).into()), + Num(Double(x)) => visitor.visit_f64((*x).into()), Text(x) => visitor.visit_str(x), List(xs) => { visitor.visit_seq(SeqDeserializer::new(xs.iter().map(val))) -- cgit v1.2.3 From b3a05d930dd8f2bbc705145219025af96e5dec86 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 13 Mar 2020 20:16:08 +0000 Subject: Use dhall::Value more in serde --- serde_dhall/src/lib.rs | 65 ++++++++++++++++++++++++------------------------ serde_dhall/src/serde.rs | 3 +-- 2 files changed, 33 insertions(+), 35 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 103fdc3..134528c 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -183,44 +183,31 @@ pub use value::Value; // A Dhall value. #[doc(hidden)] pub mod value { - use dhall::{SimpleType, SimpleValue}; + use dhall::SimpleValue; use super::de::Error; - use super::Type; - /// A Dhall value + /// A Dhall value. This is a wrapper around [`dhall::SimpleValue`]. #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Value(pub(crate) dhall::Value); + pub struct Value(SimpleValue); impl Value { - pub fn from_str(s: &str, ty: Option<&Type>) -> super::de::Result { - let ty = ty.map(|t| t.to_dhall_value()); - let val = dhall::Value::from_str_with_annot(s, ty.as_ref()) - .map_err(Error::Dhall)?; - Ok(Value(val)) - } - pub(crate) fn to_simple_value(&self) -> Option { - self.0.to_simple_value() - } - pub(crate) fn to_simple_type(&self) -> Option { - self.0.to_simple_type() + pub fn into_simple_value(self) -> SimpleValue { + self.0 } } impl super::de::sealed::Sealed for Value {} impl super::de::Deserialize for Value { - fn from_dhall(v: &Value) -> super::de::Result { - Ok(v.clone()) - } - } - - impl std::fmt::Display for Value { - fn fmt( - &self, - f: &mut std::fmt::Formatter, - ) -> Result<(), std::fmt::Error> { - self.0.fmt(f) + fn from_dhall(v: &dhall::Value) -> super::de::Result { + let sval = v.to_simple_value().ok_or_else(|| { + Error::Deserialize(format!( + "this cannot be deserialized into a simple type: {}", + v + )) + })?; + Ok(Value(sval)) } } } @@ -232,12 +219,15 @@ pub mod ty { use super::de::Error; - /// A Dhall type + /// A Dhall type. This is a wrapper around [`dhall::SimpleType`]. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Type(SimpleType); impl Type { - pub(crate) fn to_dhall_value(&self) -> dhall::Value { + pub fn into_simple_type(self) -> SimpleType { + self.0 + } + pub fn to_dhall_value(&self) -> dhall::Value { self.0.to_value() } @@ -275,7 +265,7 @@ pub mod ty { impl super::de::sealed::Sealed for Type {} impl super::de::Deserialize for Type { - fn from_dhall(v: &super::Value) -> super::de::Result { + fn from_dhall(v: &dhall::Value) -> super::de::Result { let sty = v.to_simple_type().ok_or_else(|| { Error::Deserialize(format!( "this cannot be deserialized into a simple type: {}", @@ -291,7 +281,6 @@ pub mod ty { pub mod de { use super::StaticType; use super::Type; - use super::Value; pub use error::{Error, Result}; mod error { @@ -339,7 +328,17 @@ pub mod de { /// This trait cannot be implemented manually. pub trait Deserialize: sealed::Sealed + Sized { /// See [serde_dhall::from_str][crate::from_str] - fn from_dhall(v: &Value) -> Result; + fn from_dhall(v: &dhall::Value) -> Result; + } + + fn from_str_with_annot(s: &str, ty: Option<&Type>) -> Result + where + T: Deserialize, + { + let ty = ty.map(|ty| ty.to_dhall_value()); + let val = dhall::Value::from_str_with_annot(s, ty.as_ref()) + .map_err(Error::Dhall)?; + T::from_dhall(&val) } /// Deserialize an instance of type `T` from a string of Dhall text. @@ -352,7 +351,7 @@ pub mod de { where T: Deserialize, { - T::from_dhall(&Value::from_str(s, None)?) + from_str_with_annot(s, None) } /// Deserialize an instance of type `T` from a string of Dhall text, @@ -364,7 +363,7 @@ pub mod de { where T: Deserialize, { - T::from_dhall(&Value::from_str(s, Some(ty))?) + from_str_with_annot(s, Some(ty)) } /// Deserialize an instance of type `T` from a string of Dhall text, diff --git a/serde_dhall/src/serde.rs b/serde_dhall/src/serde.rs index 227ba31..e06fb3d 100644 --- a/serde_dhall/src/serde.rs +++ b/serde_dhall/src/serde.rs @@ -8,7 +8,6 @@ use dhall::syntax::NumKind; use dhall::{SValKind, SimpleValue}; use crate::de::{Deserialize, Error, Result}; -use crate::Value; impl<'a, T> crate::de::sealed::Sealed for T where T: serde::Deserialize<'a> {} @@ -18,7 +17,7 @@ impl<'a, T> Deserialize for T where T: serde::Deserialize<'a>, { - fn from_dhall(v: &Value) -> Result { + fn from_dhall(v: &dhall::Value) -> Result { let sval = v.to_simple_value().ok_or_else(|| { Error::Deserialize(format!( "this cannot be deserialized into the serde data model: {}", -- cgit v1.2.3 From 1a0929b52af57d5963dd9da9e5cf85ffbed3a8f7 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 13 Mar 2020 20:53:23 +0000 Subject: Reorganize serde modules --- serde_dhall/src/error.rs | 30 ++++++ serde_dhall/src/lib.rs | 264 +++++++++++----------------------------------- serde_dhall/src/serde.rs | 4 +- serde_dhall/src/simple.rs | 81 ++++++++++++++ 4 files changed, 176 insertions(+), 203 deletions(-) create mode 100644 serde_dhall/src/error.rs create mode 100644 serde_dhall/src/simple.rs (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/error.rs b/serde_dhall/src/error.rs new file mode 100644 index 0000000..91a0b94 --- /dev/null +++ b/serde_dhall/src/error.rs @@ -0,0 +1,30 @@ +use dhall::error::Error as DhallError; + +pub type Result = std::result::Result; + +#[derive(Debug)] +#[non_exhaustive] +pub enum Error { + Dhall(DhallError), + Deserialize(String), +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Error::Dhall(err) => write!(f, "{}", err), + Error::Deserialize(err) => write!(f, "{}", err), + } + } +} + +impl std::error::Error for Error {} + +impl serde::de::Error for Error { + fn custom(msg: T) -> Self + where + T: std::fmt::Display, + { + Error::Deserialize(msg.to_string()) + } +} diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 134528c..15f45ce 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -22,7 +22,7 @@ //! This could mean a common Rust type like `HashMap`: //! //! ```rust -//! # fn main() -> serde_dhall::de::Result<()> { +//! # fn main() -> serde_dhall::Result<()> { //! use std::collections::HashMap; //! //! // Some Dhall data @@ -43,7 +43,7 @@ //! or a custom datatype, using serde's `derive` mechanism: //! //! ```rust -//! # fn main() -> serde_dhall::de::Result<()> { +//! # fn main() -> serde_dhall::Result<()> { //! use serde::Deserialize; //! //! #[derive(Debug, Deserialize)] @@ -110,8 +110,8 @@ //! pass it to [`from_str_check_type`][from_str_check_type]. //! //! ```rust -//! # fn main() -> serde_dhall::de::Result<()> { -//! use serde_dhall::Type; +//! # fn main() -> serde_dhall::Result<()> { +//! use serde_dhall::simple::Type; //! use std::collections::HashMap; //! //! // Parse a Dhall type @@ -138,7 +138,7 @@ //! You can also let Rust infer the appropriate Dhall type, using the [StaticType] trait. //! //! ```rust -//! # fn main() -> serde_dhall::de::Result<()> { +//! # fn main() -> serde_dhall::Result<()> { //! use serde::Deserialize; //! use serde_dhall::StaticType; //! @@ -167,215 +167,77 @@ //! [serde]: https://docs.serde.rs/serde/ //! [serde::Deserialize]: https://docs.serde.rs/serde/trait.Deserialize.html +mod error; mod serde; +pub mod simple; mod static_type; -#[doc(inline)] -pub use de::{from_str, from_str_auto_type, from_str_check_type}; #[doc(hidden)] pub use dhall_proc_macros::StaticType; +pub use error::{Error, Result}; pub use static_type::StaticType; -#[doc(inline)] -pub use ty::Type; -#[doc(inline)] -pub use value::Value; -// A Dhall value. -#[doc(hidden)] -pub mod value { - use dhall::SimpleValue; - - use super::de::Error; - - /// A Dhall value. This is a wrapper around [`dhall::SimpleValue`]. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Value(SimpleValue); +use simple::Type; - impl Value { - pub fn into_simple_value(self) -> SimpleValue { - self.0 - } - } - - impl super::de::sealed::Sealed for Value {} - - impl super::de::Deserialize for Value { - fn from_dhall(v: &dhall::Value) -> super::de::Result { - let sval = v.to_simple_value().ok_or_else(|| { - Error::Deserialize(format!( - "this cannot be deserialized into a simple type: {}", - v - )) - })?; - Ok(Value(sval)) - } - } +pub(crate) mod sealed { + pub trait Sealed {} } -// A Dhall type. -#[doc(hidden)] -pub mod ty { - use dhall::{STyKind, SimpleType}; - - use super::de::Error; - - /// A Dhall type. This is a wrapper around [`dhall::SimpleType`]. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Type(SimpleType); - - impl Type { - pub fn into_simple_type(self) -> SimpleType { - self.0 - } - pub fn to_dhall_value(&self) -> dhall::Value { - self.0.to_value() - } - - pub(crate) fn from_simple_type(ty: SimpleType) -> Self { - Type(ty) - } - pub(crate) fn from_stykind(k: STyKind) -> Self { - Type(SimpleType::new(k)) - } - pub(crate) fn make_optional_type(t: Type) -> Self { - Type::from_stykind(STyKind::Optional(t.0)) - } - pub(crate) fn make_list_type(t: Type) -> Self { - Type::from_stykind(STyKind::List(t.0)) - } - // Made public for the StaticType derive macro - #[doc(hidden)] - pub fn make_record_type( - kts: impl Iterator, - ) -> Self { - Type::from_stykind(STyKind::Record( - kts.map(|(k, t)| (k, t.0)).collect(), - )) - } - #[doc(hidden)] - pub fn make_union_type( - kts: impl Iterator)>, - ) -> Self { - Type::from_stykind(STyKind::Union( - kts.map(|(k, t)| (k, t.map(|t| t.0))).collect(), - )) - } - } - - impl super::de::sealed::Sealed for Type {} - - impl super::de::Deserialize for Type { - fn from_dhall(v: &dhall::Value) -> super::de::Result { - let sty = v.to_simple_type().ok_or_else(|| { - Error::Deserialize(format!( - "this cannot be deserialized into a simple type: {}", - v - )) - })?; - Ok(Type(sty)) - } - } +/// A data structure that can be deserialized from a Dhall expression +/// +/// This is automatically implemented for any type that [serde][serde] +/// can deserialize. +/// +/// This trait cannot be implemented manually. +pub trait Deserialize: sealed::Sealed + Sized { + /// See [serde_dhall::from_str][crate::from_str] + fn from_dhall(v: &dhall::Value) -> Result; } -/// Deserialize Dhall data to a Rust data structure. -pub mod de { - use super::StaticType; - use super::Type; - pub use error::{Error, Result}; - - mod error { - use dhall::error::Error as DhallError; - - pub type Result = std::result::Result; - - #[derive(Debug)] - #[non_exhaustive] - pub enum Error { - Dhall(DhallError), - Deserialize(String), - } - - impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Error::Dhall(err) => write!(f, "{}", err), - Error::Deserialize(err) => write!(f, "{}", err), - } - } - } - - impl std::error::Error for Error {} - - impl serde::de::Error for Error { - fn custom(msg: T) -> Self - where - T: std::fmt::Display, - { - Error::Deserialize(msg.to_string()) - } - } - } - - pub(crate) mod sealed { - pub trait Sealed {} - } - - /// A data structure that can be deserialized from a Dhall expression - /// - /// This is automatically implemented for any type that [serde][serde] - /// can deserialize. - /// - /// This trait cannot be implemented manually. - pub trait Deserialize: sealed::Sealed + Sized { - /// See [serde_dhall::from_str][crate::from_str] - fn from_dhall(v: &dhall::Value) -> Result; - } - - fn from_str_with_annot(s: &str, ty: Option<&Type>) -> Result - where - T: Deserialize, - { - let ty = ty.map(|ty| ty.to_dhall_value()); - let val = dhall::Value::from_str_with_annot(s, ty.as_ref()) - .map_err(Error::Dhall)?; - T::from_dhall(&val) - } +fn from_str_with_annot(s: &str, ty: Option<&Type>) -> Result +where + T: Deserialize, +{ + let ty = ty.map(|ty| ty.to_dhall_value()); + let val = dhall::Value::from_str_with_annot(s, ty.as_ref()) + .map_err(Error::Dhall)?; + T::from_dhall(&val) +} - /// Deserialize an instance of type `T` from a string of Dhall text. - /// - /// This will recursively resolve all imports in the expression, and - /// typecheck it before deserialization. Relative imports will be resolved relative to the - /// provided file. More control over this process is not yet available - /// but will be in a coming version of this crate. - pub fn from_str(s: &str) -> Result - where - T: Deserialize, - { - from_str_with_annot(s, None) - } +/// Deserialize an instance of type `T` from a string of Dhall text. +/// +/// This will recursively resolve all imports in the expression, and +/// typecheck it before deserialization. Relative imports will be resolved relative to the +/// provided file. More control over this process is not yet available +/// but will be in a coming version of this crate. +pub fn from_str(s: &str) -> Result +where + T: Deserialize, +{ + from_str_with_annot(s, None) +} - /// Deserialize an instance of type `T` from a string of Dhall text, - /// additionally checking that it matches the supplied type. - /// - /// Like [from_str], but this additionally checks that - /// the type of the provided expression matches the supplied type. - pub fn from_str_check_type(s: &str, ty: &Type) -> Result - where - T: Deserialize, - { - from_str_with_annot(s, Some(ty)) - } +/// Deserialize an instance of type `T` from a string of Dhall text, +/// additionally checking that it matches the supplied type. +/// +/// Like [from_str], but this additionally checks that +/// the type of the provided expression matches the supplied type. +pub fn from_str_check_type(s: &str, ty: &Type) -> Result +where + T: Deserialize, +{ + from_str_with_annot(s, Some(ty)) +} - /// Deserialize an instance of type `T` from a string of Dhall text, - /// additionally checking that it matches the type of `T`. - /// - /// Like [from_str], but this additionally checks that - /// the type of the provided expression matches the output type `T`. The [StaticType] trait - /// captures Rust types that are valid Dhall types. - pub fn from_str_auto_type(s: &str) -> Result - where - T: Deserialize + StaticType, - { - from_str_check_type(s, &::static_type()) - } +/// Deserialize an instance of type `T` from a string of Dhall text, +/// additionally checking that it matches the type of `T`. +/// +/// Like [from_str], but this additionally checks that +/// the type of the provided expression matches the output type `T`. The [StaticType] trait +/// captures Rust types that are valid Dhall types. +pub fn from_str_auto_type(s: &str) -> Result +where + T: Deserialize + StaticType, +{ + from_str_check_type(s, &::static_type()) } diff --git a/serde_dhall/src/serde.rs b/serde_dhall/src/serde.rs index e06fb3d..d144abf 100644 --- a/serde_dhall/src/serde.rs +++ b/serde_dhall/src/serde.rs @@ -7,9 +7,9 @@ use serde::de::value::{ use dhall::syntax::NumKind; use dhall::{SValKind, SimpleValue}; -use crate::de::{Deserialize, Error, Result}; +use crate::{Deserialize, Error, Result}; -impl<'a, T> crate::de::sealed::Sealed for T where T: serde::Deserialize<'a> {} +impl<'a, T> crate::sealed::Sealed for T where T: serde::Deserialize<'a> {} struct Deserializer<'a>(Cow<'a, SimpleValue>); diff --git a/serde_dhall/src/simple.rs b/serde_dhall/src/simple.rs new file mode 100644 index 0000000..93364a0 --- /dev/null +++ b/serde_dhall/src/simple.rs @@ -0,0 +1,81 @@ +use super::Error; +use dhall::{STyKind, SimpleType, SimpleValue}; + +/// A Dhall value. This is a wrapper around [`dhall::SimpleValue`]. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Value(SimpleValue); + +impl Value { + pub fn into_simple_value(self) -> SimpleValue { + self.0 + } +} + +/// A Dhall type. This is a wrapper around [`dhall::SimpleType`]. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Type(SimpleType); + +impl Type { + pub fn into_simple_type(self) -> SimpleType { + self.0 + } + pub fn to_dhall_value(&self) -> dhall::Value { + self.0.to_value() + } + + pub(crate) fn from_simple_type(ty: SimpleType) -> Self { + Type(ty) + } + pub(crate) fn from_stykind(k: STyKind) -> Self { + Type(SimpleType::new(k)) + } + pub(crate) fn make_optional_type(t: Type) -> Self { + Type::from_stykind(STyKind::Optional(t.0)) + } + pub(crate) fn make_list_type(t: Type) -> Self { + Type::from_stykind(STyKind::List(t.0)) + } + // Made public for the StaticType derive macro + #[doc(hidden)] + pub fn make_record_type(kts: impl Iterator) -> Self { + Type::from_stykind(STyKind::Record( + kts.map(|(k, t)| (k, t.0)).collect(), + )) + } + #[doc(hidden)] + pub fn make_union_type( + kts: impl Iterator)>, + ) -> Self { + Type::from_stykind(STyKind::Union( + kts.map(|(k, t)| (k, t.map(|t| t.0))).collect(), + )) + } +} + +impl super::sealed::Sealed for Value {} + +impl super::Deserialize for Value { + fn from_dhall(v: &dhall::Value) -> super::Result { + let sval = v.to_simple_value().ok_or_else(|| { + Error::Deserialize(format!( + "this cannot be deserialized into a simple type: {}", + v + )) + })?; + Ok(Value(sval)) + } +} + +impl super::sealed::Sealed for Type {} + +impl super::Deserialize for Type { + fn from_dhall(v: &dhall::Value) -> super::Result { + let sty = v.to_simple_type().ok_or_else(|| { + Error::Deserialize(format!( + "this cannot be deserialized into a simple type: {}", + v + )) + })?; + Ok(Type(sty)) + } +} -- cgit v1.2.3 From fa89e9cb319b353332c9e835944e7f86a6604c42 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 18 Mar 2020 21:37:14 +0000 Subject: Move Value, SimpleValue and SimpleType to serde --- serde_dhall/src/lib.rs | 9 ++- serde_dhall/src/serde.rs | 6 +- serde_dhall/src/simple.rs | 180 ++++++++++++++++++++++++++++++++++++++--- serde_dhall/src/static_type.rs | 3 +- serde_dhall/src/value.rs | 73 +++++++++++++++++ 5 files changed, 253 insertions(+), 18 deletions(-) create mode 100644 serde_dhall/src/value.rs (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 15f45ce..adc242f 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -171,11 +171,13 @@ mod error; mod serde; pub mod simple; mod static_type; +mod value; #[doc(hidden)] pub use dhall_proc_macros::StaticType; pub use error::{Error, Result}; pub use static_type::StaticType; +pub use value::Value; use simple::Type; @@ -191,16 +193,15 @@ pub(crate) mod sealed { /// This trait cannot be implemented manually. pub trait Deserialize: sealed::Sealed + Sized { /// See [serde_dhall::from_str][crate::from_str] - fn from_dhall(v: &dhall::Value) -> Result; + fn from_dhall(v: &Value) -> Result; } fn from_str_with_annot(s: &str, ty: Option<&Type>) -> Result where T: Deserialize, { - let ty = ty.map(|ty| ty.to_dhall_value()); - let val = dhall::Value::from_str_with_annot(s, ty.as_ref()) - .map_err(Error::Dhall)?; + let ty = ty.map(|ty| ty.to_value()); + let val = Value::from_str_with_annot(s, ty.as_ref())?; T::from_dhall(&val) } diff --git a/serde_dhall/src/serde.rs b/serde_dhall/src/serde.rs index d144abf..91ef5d7 100644 --- a/serde_dhall/src/serde.rs +++ b/serde_dhall/src/serde.rs @@ -5,9 +5,9 @@ use serde::de::value::{ }; use dhall::syntax::NumKind; -use dhall::{SValKind, SimpleValue}; -use crate::{Deserialize, Error, Result}; +use crate::simple::{SValKind, SimpleValue}; +use crate::{Deserialize, Error, Result, Value}; impl<'a, T> crate::sealed::Sealed for T where T: serde::Deserialize<'a> {} @@ -17,7 +17,7 @@ impl<'a, T> Deserialize for T where T: serde::Deserialize<'a>, { - fn from_dhall(v: &dhall::Value) -> Result { + fn from_dhall(v: &Value) -> Result { let sval = v.to_simple_value().ok_or_else(|| { Error::Deserialize(format!( "this cannot be deserialized into the serde data model: {}", diff --git a/serde_dhall/src/simple.rs b/serde_dhall/src/simple.rs index 93364a0..0b322ae 100644 --- a/serde_dhall/src/simple.rs +++ b/serde_dhall/src/simple.rs @@ -1,25 +1,62 @@ -use super::Error; -use dhall::{STyKind, SimpleType, SimpleValue}; +use std::collections::BTreeMap; + +use dhall::semantics::{Hir, HirKind, Nir, NirKind}; +use dhall::syntax::{Builtin, ExprKind, NumKind, Span}; + +use crate::Error; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SimpleValue { + kind: Box, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SValKind { + Num(NumKind), + Text(String), + Optional(Option), + List(Vec), + Record(BTreeMap), + Union(String, Option), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SimpleType { + kind: Box, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum STyKind { + Bool, + Natural, + Integer, + Double, + Text, + Optional(SimpleType), + List(SimpleType), + Record(BTreeMap), + Union(BTreeMap>), +} /// A Dhall value. This is a wrapper around [`dhall::SimpleValue`]. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Value(SimpleValue); +/// A Dhall type. This is a wrapper around [`dhall::SimpleType`]. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Type(SimpleType); + impl Value { pub fn into_simple_value(self) -> SimpleValue { self.0 } } -/// A Dhall type. This is a wrapper around [`dhall::SimpleType`]. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Type(SimpleType); - impl Type { pub fn into_simple_type(self) -> SimpleType { self.0 } - pub fn to_dhall_value(&self) -> dhall::Value { + pub fn to_value(&self) -> crate::Value { self.0.to_value() } @@ -52,10 +89,135 @@ impl Type { } } +impl SimpleValue { + pub fn new(kind: SValKind) -> Self { + SimpleValue { + kind: Box::new(kind), + } + } + pub fn from_nir(nir: &Nir) -> Option { + Some(SimpleValue::new(match nir.kind() { + NirKind::Num(lit) => SValKind::Num(lit.clone()), + NirKind::TextLit(x) => SValKind::Text( + x.as_text() + .expect("Normal form should ensure the text is a string"), + ), + NirKind::EmptyOptionalLit(_) => SValKind::Optional(None), + NirKind::NEOptionalLit(x) => { + SValKind::Optional(Some(Self::from_nir(x)?)) + } + NirKind::EmptyListLit(_) => SValKind::List(vec![]), + NirKind::NEListLit(xs) => SValKind::List( + xs.iter() + .map(|v| Self::from_nir(v)) + .collect::>()?, + ), + NirKind::RecordLit(kvs) => SValKind::Record( + kvs.iter() + .map(|(k, v)| Some((k.into(), Self::from_nir(v)?))) + .collect::>()?, + ), + NirKind::UnionLit(field, x, _) => { + SValKind::Union(field.into(), Some(Self::from_nir(x)?)) + } + NirKind::UnionConstructor(field, ty) + if ty.get(field).map(|f| f.is_some()) == Some(false) => + { + SValKind::Union(field.into(), None) + } + _ => return None, + })) + } + + pub fn kind(&self) -> &SValKind { + self.kind.as_ref() + } +} + +impl SimpleType { + pub fn new(kind: STyKind) -> Self { + SimpleType { + kind: Box::new(kind), + } + } + pub fn from_nir(nir: &Nir) -> Option { + Some(SimpleType::new(match nir.kind() { + NirKind::BuiltinType(b) => match b { + Builtin::Bool => STyKind::Bool, + Builtin::Natural => STyKind::Natural, + Builtin::Integer => STyKind::Integer, + Builtin::Double => STyKind::Double, + Builtin::Text => STyKind::Text, + _ => unreachable!(), + }, + NirKind::OptionalType(t) => STyKind::Optional(Self::from_nir(t)?), + NirKind::ListType(t) => STyKind::List(Self::from_nir(t)?), + NirKind::RecordType(kts) => STyKind::Record( + kts.iter() + .map(|(k, v)| Some((k.into(), Self::from_nir(v)?))) + .collect::>()?, + ), + NirKind::UnionType(kts) => STyKind::Union( + kts.iter() + .map(|(k, v)| { + Some(( + k.into(), + v.as_ref() + .map(|v| Ok(Self::from_nir(v)?)) + .transpose()?, + )) + }) + .collect::>()?, + ), + _ => return None, + })) + } + + pub fn kind(&self) -> &STyKind { + self.kind.as_ref() + } + pub fn to_value(&self) -> crate::Value { + crate::Value { + hir: self.to_hir(), + as_simple_val: None, + as_simple_ty: Some(self.clone()), + } + } + pub fn to_hir(&self) -> Hir { + let hir = |k| Hir::new(HirKind::Expr(k), Span::Artificial); + hir(match self.kind() { + STyKind::Bool => ExprKind::Builtin(Builtin::Bool), + STyKind::Natural => ExprKind::Builtin(Builtin::Natural), + STyKind::Integer => ExprKind::Builtin(Builtin::Integer), + STyKind::Double => ExprKind::Builtin(Builtin::Double), + STyKind::Text => ExprKind::Builtin(Builtin::Text), + STyKind::Optional(t) => ExprKind::App( + hir(ExprKind::Builtin(Builtin::Optional)), + t.to_hir(), + ), + STyKind::List(t) => { + ExprKind::App(hir(ExprKind::Builtin(Builtin::List)), t.to_hir()) + } + STyKind::Record(kts) => ExprKind::RecordType( + kts.into_iter() + .map(|(k, t)| (k.as_str().into(), t.to_hir())) + .collect(), + ), + STyKind::Union(kts) => ExprKind::UnionType( + kts.into_iter() + .map(|(k, t)| { + (k.as_str().into(), t.as_ref().map(|t| t.to_hir())) + }) + .collect(), + ), + }) + } +} + impl super::sealed::Sealed for Value {} impl super::Deserialize for Value { - fn from_dhall(v: &dhall::Value) -> super::Result { + fn from_dhall(v: &crate::Value) -> super::Result { let sval = v.to_simple_value().ok_or_else(|| { Error::Deserialize(format!( "this cannot be deserialized into a simple type: {}", @@ -69,7 +231,7 @@ impl super::Deserialize for Value { impl super::sealed::Sealed for Type {} impl super::Deserialize for Type { - fn from_dhall(v: &dhall::Value) -> super::Result { + fn from_dhall(v: &crate::Value) -> super::Result { let sty = v.to_simple_type().ok_or_else(|| { Error::Deserialize(format!( "this cannot be deserialized into a simple type: {}", diff --git a/serde_dhall/src/static_type.rs b/serde_dhall/src/static_type.rs index bc5f366..4e30f34 100644 --- a/serde_dhall/src/static_type.rs +++ b/serde_dhall/src/static_type.rs @@ -1,5 +1,4 @@ -use dhall::{STyKind, SimpleType}; - +use crate::simple::{STyKind, SimpleType}; use crate::Type; /// A Rust type that can be represented as a Dhall type. diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs new file mode 100644 index 0000000..a24f211 --- /dev/null +++ b/serde_dhall/src/value.rs @@ -0,0 +1,73 @@ +use dhall::semantics::Hir; +use dhall::syntax::Expr; +use dhall::{Normalized, Parsed}; + +use crate::simple::{SimpleType, SimpleValue}; +use crate::Error; + +/// A Dhall value. +#[derive(Debug, Clone)] +pub struct Value { + /// Invariant: in normal form + pub(crate) hir: Hir, + /// Cached conversions because they are annoying to construct from Hir. + /// At most one of them will be `Some`. + pub(crate) as_simple_val: Option, + pub(crate) as_simple_ty: Option, +} + +impl Value { + /// Parse a string into a Value, and optionally ensure that the value matches the provided type + /// annotation. + pub fn from_str_with_annot( + s: &str, + ty: Option<&Self>, + ) -> Result { + Self::from_str_with_annot_dhall_error(s, ty).map_err(Error::Dhall) + } + fn from_normalized(x: &Normalized) -> Self { + Value { + hir: x.to_hir(), + as_simple_val: SimpleValue::from_nir(x.as_nir()), + as_simple_ty: SimpleType::from_nir(x.as_nir()), + } + } + + fn from_str_with_annot_dhall_error( + s: &str, + ty: Option<&Self>, + ) -> Result { + let resolved = Parsed::parse_str(s)?.resolve()?; + let typed = match ty { + None => resolved.typecheck()?, + Some(ty) => resolved.typecheck_with(&ty.hir)?, + }; + Ok(Self::from_normalized(&typed.normalize())) + } + + /// Converts a Value into a SimpleValue. + pub fn to_simple_value(&self) -> Option { + self.as_simple_val.clone() + } + /// Converts a Value into a SimpleType. + pub fn to_simple_type(&self) -> Option { + self.as_simple_ty.clone() + } + + /// Converts a value back to the corresponding AST expression. + pub fn to_expr(&self) -> Expr { + self.hir.to_expr(Default::default()) + } +} + +impl Eq for Value {} +impl PartialEq for Value { + fn eq(&self, other: &Self) -> bool { + self.hir == other.hir + } +} +impl std::fmt::Display for Value { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + self.to_expr().fmt(f) + } +} -- cgit v1.2.3 From a1f370cb68974c1e69f8f85345e91ec763b23ae2 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 18 Mar 2020 22:28:41 +0000 Subject: Expose simple::Val/Ty properly in the API --- serde_dhall/src/lib.rs | 31 +++---- serde_dhall/src/serde.rs | 6 +- serde_dhall/src/simple.rs | 195 ++++++++++++++++------------------------- serde_dhall/src/static_type.rs | 21 +++-- serde_dhall/src/value.rs | 27 +++--- 5 files changed, 120 insertions(+), 160 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index adc242f..4b1a689 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -16,7 +16,7 @@ //! //! # Basic usage //! -//! The main entrypoint of this library is the [`from_str`][from_str] function. It reads a string +//! The main entrypoint of this library is the [`from_str`](fn.from_str.html) function. It reads a string //! containing a Dhall expression and deserializes it into any serde-compatible type. //! //! This could mean a common Rust type like `HashMap`: @@ -88,11 +88,11 @@ //! //! # Replacing `serde_json` or `serde_yaml` //! -//! If you used to consume JSON or YAML, you only need to replace [serde_json::from_str] or -//! [serde_yaml::from_str] with [serde_dhall::from_str][from_str]. +//! If you used to consume JSON or YAML, you only need to replace [`serde_json::from_str`] or +//! [`serde_yaml::from_str`] with [`serde_dhall::from_str`](fn.from_str.html). //! -//! [serde_json::from_str]: https://docs.serde.rs/serde_json/de/fn.from_str.html -//! [serde_yaml::from_str]: https://docs.serde.rs/serde_yaml/fn.from_str.html +//! [`serde_json::from_str`]: https://docs.serde.rs/serde_json/fn.from_str.html +//! [`serde_yaml::from_str`]: https://docs.serde.rs/serde_yaml/fn.from_str.html //! //! //! # Additional Dhall typechecking @@ -106,8 +106,8 @@ //! There are two ways to typecheck a Dhall value: you can provide the type as Dhall text or you //! can let Rust infer it for you. //! -//! To provide a type written in Dhall, first parse it into a [`serde_dhall::Type`][Type], then -//! pass it to [`from_str_check_type`][from_str_check_type]. +//! To provide a type written in Dhall, first parse it into a [`simple::Type`](simple/struct.Type.html), then +//! pass it to [`from_str_check_type`](fn.from_str_check_type.html). //! //! ```rust //! # fn main() -> serde_dhall::Result<()> { @@ -135,7 +135,8 @@ //! # } //! ``` //! -//! You can also let Rust infer the appropriate Dhall type, using the [StaticType] trait. +//! You can also let Rust infer the appropriate Dhall type, using the +//! [StaticType](trait.StaticType.html) trait. //! //! ```rust //! # fn main() -> serde_dhall::Result<()> { @@ -169,18 +170,19 @@ mod error; mod serde; +/// Serde-compatible values and their type pub mod simple; mod static_type; -mod value; +/// Arbitrary Dhall values +pub mod value; +pub use crate::simple::{Type as SimpleType, Value as SimpleValue}; #[doc(hidden)] pub use dhall_proc_macros::StaticType; pub use error::{Error, Result}; pub use static_type::StaticType; pub use value::Value; -use simple::Type; - pub(crate) mod sealed { pub trait Sealed {} } @@ -196,12 +198,11 @@ pub trait Deserialize: sealed::Sealed + Sized { fn from_dhall(v: &Value) -> Result; } -fn from_str_with_annot(s: &str, ty: Option<&Type>) -> Result +fn from_str_with_annot(s: &str, ty: Option<&simple::Type>) -> Result where T: Deserialize, { - let ty = ty.map(|ty| ty.to_value()); - let val = Value::from_str_with_annot(s, ty.as_ref())?; + let val = Value::from_str_with_annot(s, ty)?; T::from_dhall(&val) } @@ -223,7 +224,7 @@ where /// /// Like [from_str], but this additionally checks that /// the type of the provided expression matches the supplied type. -pub fn from_str_check_type(s: &str, ty: &Type) -> Result +pub fn from_str_check_type(s: &str, ty: &simple::Type) -> Result where T: Deserialize, { diff --git a/serde_dhall/src/serde.rs b/serde_dhall/src/serde.rs index 91ef5d7..717450a 100644 --- a/serde_dhall/src/serde.rs +++ b/serde_dhall/src/serde.rs @@ -6,7 +6,7 @@ use serde::de::value::{ use dhall::syntax::NumKind; -use crate::simple::{SValKind, SimpleValue}; +use crate::simple::{ValKind, Value as SimpleValue}; use crate::{Deserialize, Error, Result, Value}; impl<'a, T> crate::sealed::Sealed for T where T: serde::Deserialize<'a> {} @@ -44,7 +44,7 @@ impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { { use std::convert::TryInto; use NumKind::*; - use SValKind::*; + use ValKind::*; let val = |x| Deserializer(Cow::Borrowed(x)); match self.0.kind() { @@ -97,7 +97,7 @@ impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { let val = |x| Deserializer(Cow::Borrowed(x)); match self.0.kind() { // Blindly takes keys in sorted order. - SValKind::Record(m) => visitor + ValKind::Record(m) => visitor .visit_seq(SeqDeserializer::new(m.iter().map(|(_, v)| val(v)))), _ => self.deserialize_any(visitor), } diff --git a/serde_dhall/src/simple.rs b/serde_dhall/src/simple.rs index 0b322ae..4cd4ab7 100644 --- a/serde_dhall/src/simple.rs +++ b/serde_dhall/src/simple.rs @@ -3,161 +3,114 @@ use std::collections::BTreeMap; use dhall::semantics::{Hir, HirKind, Nir, NirKind}; use dhall::syntax::{Builtin, ExprKind, NumKind, Span}; -use crate::Error; +use crate::{sealed::Sealed, Deserialize, Error, Result}; +/// A simple value of the kind that can be encoded/decoded with serde #[derive(Debug, Clone, PartialEq, Eq)] -pub struct SimpleValue { - kind: Box, +pub struct Value { + kind: Box, } #[derive(Debug, Clone, PartialEq, Eq)] -pub enum SValKind { +pub enum ValKind { + // TODO: redefine NumKind locally Num(NumKind), Text(String), - Optional(Option), - List(Vec), - Record(BTreeMap), - Union(String, Option), + Optional(Option), + List(Vec), + // TODO: HashMap ? + Record(BTreeMap), + Union(String, Option), } +/// The type of a `simple::Value`. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct SimpleType { - kind: Box, +pub struct Type { + kind: Box, } #[derive(Debug, Clone, PartialEq, Eq)] -pub enum STyKind { +pub enum TyKind { Bool, Natural, Integer, Double, Text, - Optional(SimpleType), - List(SimpleType), - Record(BTreeMap), - Union(BTreeMap>), + Optional(Type), + List(Type), + Record(BTreeMap), + Union(BTreeMap>), } -/// A Dhall value. This is a wrapper around [`dhall::SimpleValue`]. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Value(SimpleValue); - -/// A Dhall type. This is a wrapper around [`dhall::SimpleType`]. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Type(SimpleType); - impl Value { - pub fn into_simple_value(self) -> SimpleValue { - self.0 - } -} - -impl Type { - pub fn into_simple_type(self) -> SimpleType { - self.0 - } - pub fn to_value(&self) -> crate::Value { - self.0.to_value() - } - - pub(crate) fn from_simple_type(ty: SimpleType) -> Self { - Type(ty) - } - pub(crate) fn from_stykind(k: STyKind) -> Self { - Type(SimpleType::new(k)) - } - pub(crate) fn make_optional_type(t: Type) -> Self { - Type::from_stykind(STyKind::Optional(t.0)) - } - pub(crate) fn make_list_type(t: Type) -> Self { - Type::from_stykind(STyKind::List(t.0)) - } - // Made public for the StaticType derive macro - #[doc(hidden)] - pub fn make_record_type(kts: impl Iterator) -> Self { - Type::from_stykind(STyKind::Record( - kts.map(|(k, t)| (k, t.0)).collect(), - )) - } - #[doc(hidden)] - pub fn make_union_type( - kts: impl Iterator)>, - ) -> Self { - Type::from_stykind(STyKind::Union( - kts.map(|(k, t)| (k, t.map(|t| t.0))).collect(), - )) - } -} - -impl SimpleValue { - pub fn new(kind: SValKind) -> Self { - SimpleValue { + pub(crate) fn new(kind: ValKind) -> Self { + Value { kind: Box::new(kind), } } - pub fn from_nir(nir: &Nir) -> Option { - Some(SimpleValue::new(match nir.kind() { - NirKind::Num(lit) => SValKind::Num(lit.clone()), - NirKind::TextLit(x) => SValKind::Text( + pub(crate) fn from_nir(nir: &Nir) -> Option { + Some(Value::new(match nir.kind() { + NirKind::Num(lit) => ValKind::Num(lit.clone()), + NirKind::TextLit(x) => ValKind::Text( x.as_text() .expect("Normal form should ensure the text is a string"), ), - NirKind::EmptyOptionalLit(_) => SValKind::Optional(None), + NirKind::EmptyOptionalLit(_) => ValKind::Optional(None), NirKind::NEOptionalLit(x) => { - SValKind::Optional(Some(Self::from_nir(x)?)) + ValKind::Optional(Some(Self::from_nir(x)?)) } - NirKind::EmptyListLit(_) => SValKind::List(vec![]), - NirKind::NEListLit(xs) => SValKind::List( + NirKind::EmptyListLit(_) => ValKind::List(vec![]), + NirKind::NEListLit(xs) => ValKind::List( xs.iter() .map(|v| Self::from_nir(v)) .collect::>()?, ), - NirKind::RecordLit(kvs) => SValKind::Record( + NirKind::RecordLit(kvs) => ValKind::Record( kvs.iter() .map(|(k, v)| Some((k.into(), Self::from_nir(v)?))) .collect::>()?, ), NirKind::UnionLit(field, x, _) => { - SValKind::Union(field.into(), Some(Self::from_nir(x)?)) + ValKind::Union(field.into(), Some(Self::from_nir(x)?)) } NirKind::UnionConstructor(field, ty) if ty.get(field).map(|f| f.is_some()) == Some(false) => { - SValKind::Union(field.into(), None) + ValKind::Union(field.into(), None) } _ => return None, })) } - pub fn kind(&self) -> &SValKind { + pub fn kind(&self) -> &ValKind { self.kind.as_ref() } } -impl SimpleType { - pub fn new(kind: STyKind) -> Self { - SimpleType { +impl Type { + pub fn new(kind: TyKind) -> Self { + Type { kind: Box::new(kind), } } - pub fn from_nir(nir: &Nir) -> Option { - Some(SimpleType::new(match nir.kind() { + pub(crate) fn from_nir(nir: &Nir) -> Option { + Some(Type::new(match nir.kind() { NirKind::BuiltinType(b) => match b { - Builtin::Bool => STyKind::Bool, - Builtin::Natural => STyKind::Natural, - Builtin::Integer => STyKind::Integer, - Builtin::Double => STyKind::Double, - Builtin::Text => STyKind::Text, + Builtin::Bool => TyKind::Bool, + Builtin::Natural => TyKind::Natural, + Builtin::Integer => TyKind::Integer, + Builtin::Double => TyKind::Double, + Builtin::Text => TyKind::Text, _ => unreachable!(), }, - NirKind::OptionalType(t) => STyKind::Optional(Self::from_nir(t)?), - NirKind::ListType(t) => STyKind::List(Self::from_nir(t)?), - NirKind::RecordType(kts) => STyKind::Record( + NirKind::OptionalType(t) => TyKind::Optional(Self::from_nir(t)?), + NirKind::ListType(t) => TyKind::List(Self::from_nir(t)?), + NirKind::RecordType(kts) => TyKind::Record( kts.iter() .map(|(k, v)| Some((k.into(), Self::from_nir(v)?))) .collect::>()?, ), - NirKind::UnionType(kts) => STyKind::Union( + NirKind::UnionType(kts) => TyKind::Union( kts.iter() .map(|(k, v)| { Some(( @@ -173,37 +126,37 @@ impl SimpleType { })) } - pub fn kind(&self) -> &STyKind { + pub fn kind(&self) -> &TyKind { self.kind.as_ref() } - pub fn to_value(&self) -> crate::Value { - crate::Value { + pub fn to_value(&self) -> crate::value::Value { + crate::value::Value { hir: self.to_hir(), as_simple_val: None, as_simple_ty: Some(self.clone()), } } - pub fn to_hir(&self) -> Hir { + pub(crate) fn to_hir(&self) -> Hir { let hir = |k| Hir::new(HirKind::Expr(k), Span::Artificial); hir(match self.kind() { - STyKind::Bool => ExprKind::Builtin(Builtin::Bool), - STyKind::Natural => ExprKind::Builtin(Builtin::Natural), - STyKind::Integer => ExprKind::Builtin(Builtin::Integer), - STyKind::Double => ExprKind::Builtin(Builtin::Double), - STyKind::Text => ExprKind::Builtin(Builtin::Text), - STyKind::Optional(t) => ExprKind::App( + TyKind::Bool => ExprKind::Builtin(Builtin::Bool), + TyKind::Natural => ExprKind::Builtin(Builtin::Natural), + TyKind::Integer => ExprKind::Builtin(Builtin::Integer), + TyKind::Double => ExprKind::Builtin(Builtin::Double), + TyKind::Text => ExprKind::Builtin(Builtin::Text), + TyKind::Optional(t) => ExprKind::App( hir(ExprKind::Builtin(Builtin::Optional)), t.to_hir(), ), - STyKind::List(t) => { + TyKind::List(t) => { ExprKind::App(hir(ExprKind::Builtin(Builtin::List)), t.to_hir()) } - STyKind::Record(kts) => ExprKind::RecordType( + TyKind::Record(kts) => ExprKind::RecordType( kts.into_iter() .map(|(k, t)| (k.as_str().into(), t.to_hir())) .collect(), ), - STyKind::Union(kts) => ExprKind::UnionType( + TyKind::Union(kts) => ExprKind::UnionType( kts.into_iter() .map(|(k, t)| { (k.as_str().into(), t.as_ref().map(|t| t.to_hir())) @@ -214,30 +167,34 @@ impl SimpleType { } } -impl super::sealed::Sealed for Value {} +impl Sealed for Value {} -impl super::Deserialize for Value { - fn from_dhall(v: &crate::Value) -> super::Result { - let sval = v.to_simple_value().ok_or_else(|| { +impl Deserialize for Value { + fn from_dhall(v: &crate::value::Value) -> Result { + v.to_simple_value().ok_or_else(|| { Error::Deserialize(format!( "this cannot be deserialized into a simple type: {}", v )) - })?; - Ok(Value(sval)) + }) } } -impl super::sealed::Sealed for Type {} +impl Sealed for Type {} -impl super::Deserialize for Type { - fn from_dhall(v: &crate::Value) -> super::Result { - let sty = v.to_simple_type().ok_or_else(|| { +impl Deserialize for Type { + fn from_dhall(v: &crate::value::Value) -> Result { + v.to_simple_type().ok_or_else(|| { Error::Deserialize(format!( "this cannot be deserialized into a simple type: {}", v )) - })?; - Ok(Type(sty)) + }) + } +} + +impl From for Type { + fn from(x: TyKind) -> Type { + Type::new(x) } } diff --git a/serde_dhall/src/static_type.rs b/serde_dhall/src/static_type.rs index 4e30f34..79418d2 100644 --- a/serde_dhall/src/static_type.rs +++ b/serde_dhall/src/static_type.rs @@ -1,5 +1,4 @@ -use crate::simple::{STyKind, SimpleType}; -use crate::Type; +use crate::simple::{TyKind, Type}; /// A Rust type that can be represented as a Dhall type. /// @@ -20,7 +19,7 @@ macro_rules! derive_builtin { ($rust_ty:ty, $dhall_ty:ident) => { impl StaticType for $rust_ty { fn static_type() -> Type { - Type::from_simple_type(SimpleType::new(STyKind::$dhall_ty)) + Type::new(TyKind::$dhall_ty) } } }; @@ -43,13 +42,15 @@ where B: StaticType, { fn static_type() -> Type { - Type::make_record_type( + TyKind::Record( vec![ ("_1".to_owned(), A::static_type()), ("_2".to_owned(), B::static_type()), ] - .into_iter(), + .into_iter() + .collect(), ) + .into() } } @@ -59,13 +60,15 @@ where E: StaticType, { fn static_type() -> Type { - Type::make_union_type( + TyKind::Union( vec![ ("Ok".to_owned(), Some(T::static_type())), ("Err".to_owned(), Some(E::static_type())), ] - .into_iter(), + .into_iter() + .collect(), ) + .into() } } @@ -74,7 +77,7 @@ where T: StaticType, { fn static_type() -> Type { - Type::make_optional_type(T::static_type()) + TyKind::Optional(T::static_type()).into() } } @@ -83,7 +86,7 @@ where T: StaticType, { fn static_type() -> Type { - Type::make_list_type(T::static_type()) + TyKind::List(T::static_type()).into() } } diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs index a24f211..62632e5 100644 --- a/serde_dhall/src/value.rs +++ b/serde_dhall/src/value.rs @@ -1,11 +1,11 @@ -use dhall::semantics::Hir; +use dhall::semantics::{Hir, Nir}; use dhall::syntax::Expr; -use dhall::{Normalized, Parsed}; +use dhall::Parsed; -use crate::simple::{SimpleType, SimpleValue}; +use crate::simple::{Type as SimpleType, Value as SimpleValue}; use crate::Error; -/// A Dhall value. +/// An arbitrary Dhall value. #[derive(Debug, Clone)] pub struct Value { /// Invariant: in normal form @@ -21,28 +21,27 @@ impl Value { /// annotation. pub fn from_str_with_annot( s: &str, - ty: Option<&Self>, + ty: Option<&SimpleType>, ) -> Result { Self::from_str_with_annot_dhall_error(s, ty).map_err(Error::Dhall) } - fn from_normalized(x: &Normalized) -> Self { + fn from_nir(x: &Nir) -> Self { Value { - hir: x.to_hir(), - as_simple_val: SimpleValue::from_nir(x.as_nir()), - as_simple_ty: SimpleType::from_nir(x.as_nir()), + hir: x.to_hir_noenv(), + as_simple_val: SimpleValue::from_nir(x), + as_simple_ty: SimpleType::from_nir(x), } } - fn from_str_with_annot_dhall_error( s: &str, - ty: Option<&Self>, + ty: Option<&SimpleType>, ) -> Result { let resolved = Parsed::parse_str(s)?.resolve()?; let typed = match ty { None => resolved.typecheck()?, - Some(ty) => resolved.typecheck_with(&ty.hir)?, + Some(ty) => resolved.typecheck_with(&ty.to_value().hir)?, }; - Ok(Self::from_normalized(&typed.normalize())) + Ok(Self::from_nir(typed.normalize().as_nir())) } /// Converts a Value into a SimpleValue. @@ -55,7 +54,7 @@ impl Value { } /// Converts a value back to the corresponding AST expression. - pub fn to_expr(&self) -> Expr { + pub(crate) fn to_expr(&self) -> Expr { self.hir.to_expr(Default::default()) } } -- cgit v1.2.3 From 85e2e8ee5e83dadd05b6974ba6c951350cb97a61 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 21 Mar 2020 21:30:30 +0000 Subject: Introduce option builder --- serde_dhall/src/lib.rs | 16 ++---- serde_dhall/src/options.rs | 138 +++++++++++++++++++++++++++++++++++++++++++++ serde_dhall/src/value.rs | 32 ++++------- 3 files changed, 153 insertions(+), 33 deletions(-) create mode 100644 serde_dhall/src/options.rs (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 4b1a689..5b8f470 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -169,6 +169,8 @@ //! [serde::Deserialize]: https://docs.serde.rs/serde/trait.Deserialize.html mod error; +/// Finer-grained control over deserializing Dhall +pub mod options; mod serde; /// Serde-compatible values and their type pub mod simple; @@ -198,14 +200,6 @@ pub trait Deserialize: sealed::Sealed + Sized { fn from_dhall(v: &Value) -> Result; } -fn from_str_with_annot(s: &str, ty: Option<&simple::Type>) -> Result -where - T: Deserialize, -{ - let val = Value::from_str_with_annot(s, ty)?; - T::from_dhall(&val) -} - /// Deserialize an instance of type `T` from a string of Dhall text. /// /// This will recursively resolve all imports in the expression, and @@ -216,7 +210,7 @@ pub fn from_str(s: &str) -> Result where T: Deserialize, { - from_str_with_annot(s, None) + options::from_str(s).parse() } /// Deserialize an instance of type `T` from a string of Dhall text, @@ -228,7 +222,7 @@ pub fn from_str_check_type(s: &str, ty: &simple::Type) -> Result where T: Deserialize, { - from_str_with_annot(s, Some(ty)) + options::from_str(s).type_annotation(ty).parse() } /// Deserialize an instance of type `T` from a string of Dhall text, @@ -241,5 +235,5 @@ pub fn from_str_auto_type(s: &str) -> Result where T: Deserialize + StaticType, { - from_str_check_type(s, &::static_type()) + options::from_str(s).static_type_annotation().parse() } diff --git a/serde_dhall/src/options.rs b/serde_dhall/src/options.rs new file mode 100644 index 0000000..542804b --- /dev/null +++ b/serde_dhall/src/options.rs @@ -0,0 +1,138 @@ +use std::path::{Path, PathBuf}; + +use dhall::Parsed; + +use crate::simple::Type as SimpleType; +use crate::{Deserialize, Error, Result, StaticType, Value}; + +#[derive(Debug, Clone)] +enum Source<'a> { + Str(&'a str), + File(PathBuf), + // Url(&'a str), +} + +/// Options and flags which can be used to configure how a dhall value is read. +/// +/// This builder exposes the ability to configure how a value is deserialized and what operations +/// are permitted during evaluation. The functions in the crate root are aliases for +/// commonly used options using this builder. +/// +/// Generally speaking, when using `Options`, you'll create it with `from_str` or `from_file`, then +/// chain calls to methods to set each option, then call `parse`. This will give you a +/// `serde_dhall::Result` where `T` a deserializable type of your choice. +/// +/// # Examples +/// +/// Reading from a file: +/// +/// ```no_run +/// # fn main() -> serde_dhall::Result<()> { +/// use serde_dhall::options; +/// +/// let data = options::from_file("foo.dhall").parse()?; +/// # Ok(()) +/// # } +/// ``` +/// +/// Reading from a file and checking the value against a provided type: +/// +/// ```no_run +/// # fn main() -> serde_dhall::Result<()> { +/// use serde_dhall::options; +/// +/// let ty = options::from_str("{ x: Natural, y: Natural }").parse()?; +/// let data = options::from_file("foo.dhall") +/// .type_annotation(&ty) +/// .parse()?; +/// # Ok(()) +/// # } +/// ``` +#[derive(Debug, Clone)] +pub struct Options<'a, T> { + source: Source<'a>, + annot: Option, + allow_imports: bool, + // allow_remote_imports: bool, + // use_cache: bool, + target_type: std::marker::PhantomData, +} + +impl<'a, T> Options<'a, T> { + fn default_with_source(source: Source<'a>) -> Self { + Options { + source, + annot: None, + allow_imports: true, + // allow_remote_imports: true, + // use_cache: true, + target_type: std::marker::PhantomData, + } + } + fn from_str(s: &'a str) -> Self { + Self::default_with_source(Source::Str(s)) + } + fn from_file>(path: P) -> Self { + Self::default_with_source(Source::File(path.as_ref().to_owned())) + } + // fn from_url(url: &'a str) -> Self { + // Self::default_with_source(Source::Url(url)) + // } + + pub fn imports(&mut self, imports: bool) -> &mut Self { + self.allow_imports = imports; + self + } + // pub fn remote_imports(&mut self, imports: bool) -> &mut Self { + // self.allow_remote_imports = imports; + // if imports { + // self.allow_imports = true; + // } + // self + // } + pub fn type_annotation(&mut self, ty: &SimpleType) -> &mut Self { + self.annot = Some(ty.clone()); + self + } + pub fn static_type_annotation(&mut self) -> &mut Self + where + T: StaticType, + { + self.annot = Some(T::static_type()); + self + } + + fn _parse(&self) -> dhall::error::Result { + let parsed = match &self.source { + Source::Str(s) => Parsed::parse_str(s)?, + Source::File(p) => Parsed::parse_file(p.as_ref())?, + }; + let resolved = if self.allow_imports { + parsed.resolve()? + } else { + parsed.skip_resolve()? + }; + let typed = match &self.annot { + None => resolved.typecheck()?, + Some(ty) => resolved.typecheck_with(&ty.to_value().hir)?, + }; + Ok(Value::from_nir(typed.normalize().as_nir())) + } + pub fn parse(&self) -> Result + where + T: Deserialize, + { + let val = self._parse().map_err(Error::Dhall)?; + T::from_dhall(&val) + } +} + +pub fn from_str<'a, T>(s: &'a str) -> Options<'a, T> { + Options::from_str(s) +} +pub fn from_file<'a, T, P: AsRef>(path: P) -> Options<'a, T> { + Options::from_file(path) +} +// pub fn from_url<'a, T>(url: &'a str) -> Options<'a, T> { +// Options::from_url(url) +// } diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs index 62632e5..d4ded90 100644 --- a/serde_dhall/src/value.rs +++ b/serde_dhall/src/value.rs @@ -1,9 +1,8 @@ use dhall::semantics::{Hir, Nir}; use dhall::syntax::Expr; -use dhall::Parsed; use crate::simple::{Type as SimpleType, Value as SimpleValue}; -use crate::Error; +use crate::{sealed::Sealed, Deserialize, Error}; /// An arbitrary Dhall value. #[derive(Debug, Clone)] @@ -17,32 +16,13 @@ pub struct Value { } impl Value { - /// Parse a string into a Value, and optionally ensure that the value matches the provided type - /// annotation. - pub fn from_str_with_annot( - s: &str, - ty: Option<&SimpleType>, - ) -> Result { - Self::from_str_with_annot_dhall_error(s, ty).map_err(Error::Dhall) - } - fn from_nir(x: &Nir) -> Self { + pub(crate) fn from_nir(x: &Nir) -> Self { Value { hir: x.to_hir_noenv(), as_simple_val: SimpleValue::from_nir(x), as_simple_ty: SimpleType::from_nir(x), } } - fn from_str_with_annot_dhall_error( - s: &str, - ty: Option<&SimpleType>, - ) -> Result { - let resolved = Parsed::parse_str(s)?.resolve()?; - let typed = match ty { - None => resolved.typecheck()?, - Some(ty) => resolved.typecheck_with(&ty.to_value().hir)?, - }; - Ok(Self::from_nir(typed.normalize().as_nir())) - } /// Converts a Value into a SimpleValue. pub fn to_simple_value(&self) -> Option { @@ -70,3 +50,11 @@ impl std::fmt::Display for Value { self.to_expr().fmt(f) } } + +impl Sealed for Value {} + +impl Deserialize for Value { + fn from_dhall(v: &Value) -> Result { + Ok(v.clone()) + } +} -- cgit v1.2.3 From b0ee39b006ad2ca99bd3dddf06ab0e0dcc966d2f Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 21 Mar 2020 21:42:54 +0000 Subject: Test README code samples using doc_comment --- serde_dhall/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 5b8f470..4250882 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -168,6 +168,9 @@ //! [serde]: https://docs.serde.rs/serde/ //! [serde::Deserialize]: https://docs.serde.rs/serde/trait.Deserialize.html +#[cfg(doctest)] +doc_comment::doctest!("../../README.md"); + mod error; /// Finer-grained control over deserializing Dhall pub mod options; -- cgit v1.2.3 From 1a98b506055779e1a60558d9c5a56b071b3d61a0 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 22 Mar 2020 21:20:58 +0000 Subject: Reorganize API and internals of serde_dhall a bit --- serde_dhall/src/deserialize.rs | 124 +++++++++++++++++++++++++++++++++++++++++ serde_dhall/src/lib.rs | 80 ++++++-------------------- serde_dhall/src/serde.rs | 111 ------------------------------------ serde_dhall/src/shortcuts.rs | 100 +++++++++++++++++++++++++++++++++ serde_dhall/src/simple.rs | 2 +- serde_dhall/src/value.rs | 2 +- 6 files changed, 244 insertions(+), 175 deletions(-) create mode 100644 serde_dhall/src/deserialize.rs delete mode 100644 serde_dhall/src/serde.rs create mode 100644 serde_dhall/src/shortcuts.rs (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/deserialize.rs b/serde_dhall/src/deserialize.rs new file mode 100644 index 0000000..75a08a9 --- /dev/null +++ b/serde_dhall/src/deserialize.rs @@ -0,0 +1,124 @@ +use serde::de::value::{ + MapAccessDeserializer, MapDeserializer, SeqDeserializer, +}; +use std::borrow::Cow; + +use dhall::syntax::NumKind; + +use crate::simple::{ValKind, Value as SimpleValue}; +use crate::{Error, Result, Value}; + +pub trait Sealed {} + +/// A data structure that can be deserialized from a Dhall expression +/// +/// This is automatically implemented for any type that [serde][serde] +/// can deserialize. +/// +/// This trait cannot be implemented manually. +pub trait Deserialize: Sealed + Sized { + #[doc(hidden)] + /// See [serde_dhall::from_str][crate::from_str] + fn from_dhall(v: &Value) -> Result; +} + +impl<'a, T> Sealed for T where T: serde::Deserialize<'a> {} + +struct Deserializer<'a>(Cow<'a, SimpleValue>); + +impl<'a, T> Deserialize for T +where + T: serde::Deserialize<'a>, +{ + fn from_dhall(v: &Value) -> Result { + let sval = v.to_simple_value().ok_or_else(|| { + Error::Deserialize(format!( + "this cannot be deserialized into the serde data model: {}", + v + )) + })?; + T::deserialize(Deserializer(Cow::Owned(sval))) + } +} + +impl<'de: 'a, 'a> serde::de::IntoDeserializer<'de, Error> for Deserializer<'a> { + type Deserializer = Deserializer<'a>; + fn into_deserializer(self) -> Self::Deserializer { + self + } +} + +impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { + type Error = Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + use std::convert::TryInto; + use NumKind::*; + use ValKind::*; + + let val = |x| Deserializer(Cow::Borrowed(x)); + match self.0.kind() { + Num(Bool(x)) => visitor.visit_bool(*x), + Num(Natural(x)) => { + if let Ok(x64) = (*x).try_into() { + visitor.visit_u64(x64) + } else if let Ok(x32) = (*x).try_into() { + visitor.visit_u32(x32) + } else { + unimplemented!() + } + } + Num(Integer(x)) => { + if let Ok(x64) = (*x).try_into() { + visitor.visit_i64(x64) + } else if let Ok(x32) = (*x).try_into() { + visitor.visit_i32(x32) + } else { + unimplemented!() + } + } + Num(Double(x)) => visitor.visit_f64((*x).into()), + Text(x) => visitor.visit_str(x), + List(xs) => { + visitor.visit_seq(SeqDeserializer::new(xs.iter().map(val))) + } + Optional(None) => visitor.visit_none(), + Optional(Some(x)) => visitor.visit_some(val(x)), + Record(m) => visitor.visit_map(MapDeserializer::new( + m.iter().map(|(k, v)| (k.as_ref(), val(v))), + )), + Union(field_name, Some(x)) => visitor.visit_enum( + MapAccessDeserializer::new(MapDeserializer::new( + Some((field_name.as_str(), val(x))).into_iter(), + )), + ), + Union(field_name, None) => visitor.visit_enum( + MapAccessDeserializer::new(MapDeserializer::new( + Some((field_name.as_str(), ())).into_iter(), + )), + ), + } + } + + fn deserialize_tuple(self, _: usize, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + let val = |x| Deserializer(Cow::Borrowed(x)); + match self.0.kind() { + // Blindly takes keys in sorted order. + ValKind::Record(m) => visitor + .visit_seq(SeqDeserializer::new(m.iter().map(|(_, v)| val(v)))), + _ => 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 + tuple_struct map struct enum identifier ignored_any + } +} diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 4250882..5ee1cf6 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -1,4 +1,6 @@ #![doc(html_root_url = "https://docs.rs/serde_dhall/0.4.0")] +// #![warn(missing_docs)] +// #![warn(missing_doc_code_examples)] //! [Dhall][dhall] is a programmable configuration language that provides a non-repetitive //! alternative to JSON and YAML. //! @@ -107,7 +109,7 @@ //! can let Rust infer it for you. //! //! To provide a type written in Dhall, first parse it into a [`simple::Type`](simple/struct.Type.html), then -//! pass it to [`from_str_check_type`](fn.from_str_check_type.html). +//! pass it to [`from_str_manual_type`](fn.from_str_manual_type.html). //! //! ```rust //! # fn main() -> serde_dhall::Result<()> { @@ -124,7 +126,7 @@ //! // Deserialize the data to a Rust type. This checks that //! // the data matches the provided type. //! let deserialized_map: HashMap = -//! serde_dhall::from_str_check_type(point_data, &point_type)?; +//! serde_dhall::from_str_manual_type(point_data, &point_type)?; //! //! let mut expected_map = HashMap::new(); //! expected_map.insert("x".to_string(), 1); @@ -153,13 +155,13 @@ //! let data = "{ x = 1, y = 1 + 1 }"; //! //! // Convert the Dhall string to a Point. -//! let point: Point = serde_dhall::from_str_auto_type(data)?; +//! let point: Point = serde_dhall::from_str_static_type(data)?; //! assert_eq!(point.x, 1); //! assert_eq!(point.y, 2); //! //! // Invalid data fails the type validation //! let invalid_data = "{ x = 1, z = 0.3 }"; -//! assert!(serde_dhall::from_str_auto_type::(invalid_data).is_err()); +//! assert!(serde_dhall::from_str_static_type::(invalid_data).is_err()); //! # Ok(()) //! # } //! ``` @@ -171,72 +173,26 @@ #[cfg(doctest)] doc_comment::doctest!("../../README.md"); -mod error; /// Finer-grained control over deserializing Dhall pub mod options; -mod serde; -/// Serde-compatible values and their type +/// Serde-compatible Dhall values and their type pub mod simple; -mod static_type; /// Arbitrary Dhall values pub mod value; -pub use crate::simple::{Type as SimpleType, Value as SimpleValue}; +mod deserialize; +mod error; +/// Common patterns made easier +mod shortcuts; +mod static_type; + #[doc(hidden)] pub use dhall_proc_macros::StaticType; + +pub use deserialize::Deserialize; +pub(crate) use deserialize::Sealed; pub use error::{Error, Result}; +pub use shortcuts::{from_str, from_str_manual_type, from_str_static_type}; +pub use simple::{Type as SimpleType, Value as SimpleValue}; pub use static_type::StaticType; pub use value::Value; - -pub(crate) mod sealed { - pub trait Sealed {} -} - -/// A data structure that can be deserialized from a Dhall expression -/// -/// This is automatically implemented for any type that [serde][serde] -/// can deserialize. -/// -/// This trait cannot be implemented manually. -pub trait Deserialize: sealed::Sealed + Sized { - /// See [serde_dhall::from_str][crate::from_str] - fn from_dhall(v: &Value) -> Result; -} - -/// Deserialize an instance of type `T` from a string of Dhall text. -/// -/// This will recursively resolve all imports in the expression, and -/// typecheck it before deserialization. Relative imports will be resolved relative to the -/// provided file. More control over this process is not yet available -/// but will be in a coming version of this crate. -pub fn from_str(s: &str) -> Result -where - T: Deserialize, -{ - options::from_str(s).parse() -} - -/// Deserialize an instance of type `T` from a string of Dhall text, -/// additionally checking that it matches the supplied type. -/// -/// Like [from_str], but this additionally checks that -/// the type of the provided expression matches the supplied type. -pub fn from_str_check_type(s: &str, ty: &simple::Type) -> Result -where - T: Deserialize, -{ - options::from_str(s).type_annotation(ty).parse() -} - -/// Deserialize an instance of type `T` from a string of Dhall text, -/// additionally checking that it matches the type of `T`. -/// -/// Like [from_str], but this additionally checks that -/// the type of the provided expression matches the output type `T`. The [StaticType] trait -/// captures Rust types that are valid Dhall types. -pub fn from_str_auto_type(s: &str) -> Result -where - T: Deserialize + StaticType, -{ - options::from_str(s).static_type_annotation().parse() -} diff --git a/serde_dhall/src/serde.rs b/serde_dhall/src/serde.rs deleted file mode 100644 index 717450a..0000000 --- a/serde_dhall/src/serde.rs +++ /dev/null @@ -1,111 +0,0 @@ -use std::borrow::Cow; - -use serde::de::value::{ - MapAccessDeserializer, MapDeserializer, SeqDeserializer, -}; - -use dhall::syntax::NumKind; - -use crate::simple::{ValKind, Value as SimpleValue}; -use crate::{Deserialize, Error, Result, Value}; - -impl<'a, T> crate::sealed::Sealed for T where T: serde::Deserialize<'a> {} - -struct Deserializer<'a>(Cow<'a, SimpleValue>); - -impl<'a, T> Deserialize for T -where - T: serde::Deserialize<'a>, -{ - fn from_dhall(v: &Value) -> Result { - let sval = v.to_simple_value().ok_or_else(|| { - Error::Deserialize(format!( - "this cannot be deserialized into the serde data model: {}", - v - )) - })?; - T::deserialize(Deserializer(Cow::Owned(sval))) - } -} - -impl<'de: 'a, 'a> serde::de::IntoDeserializer<'de, Error> for Deserializer<'a> { - type Deserializer = Deserializer<'a>; - fn into_deserializer(self) -> Self::Deserializer { - self - } -} - -impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { - type Error = Error; - - fn deserialize_any(self, visitor: V) -> Result - where - V: serde::de::Visitor<'de>, - { - use std::convert::TryInto; - use NumKind::*; - use ValKind::*; - - let val = |x| Deserializer(Cow::Borrowed(x)); - match self.0.kind() { - Num(Bool(x)) => visitor.visit_bool(*x), - Num(Natural(x)) => { - if let Ok(x64) = (*x).try_into() { - visitor.visit_u64(x64) - } else if let Ok(x32) = (*x).try_into() { - visitor.visit_u32(x32) - } else { - unimplemented!() - } - } - Num(Integer(x)) => { - if let Ok(x64) = (*x).try_into() { - visitor.visit_i64(x64) - } else if let Ok(x32) = (*x).try_into() { - visitor.visit_i32(x32) - } else { - unimplemented!() - } - } - Num(Double(x)) => visitor.visit_f64((*x).into()), - Text(x) => visitor.visit_str(x), - List(xs) => { - visitor.visit_seq(SeqDeserializer::new(xs.iter().map(val))) - } - Optional(None) => visitor.visit_none(), - Optional(Some(x)) => visitor.visit_some(val(x)), - Record(m) => visitor.visit_map(MapDeserializer::new( - m.iter().map(|(k, v)| (k.as_ref(), val(v))), - )), - Union(field_name, Some(x)) => visitor.visit_enum( - MapAccessDeserializer::new(MapDeserializer::new( - Some((field_name.as_str(), val(x))).into_iter(), - )), - ), - Union(field_name, None) => visitor.visit_enum( - MapAccessDeserializer::new(MapDeserializer::new( - Some((field_name.as_str(), ())).into_iter(), - )), - ), - } - } - - fn deserialize_tuple(self, _: usize, visitor: V) -> Result - where - V: serde::de::Visitor<'de>, - { - let val = |x| Deserializer(Cow::Borrowed(x)); - match self.0.kind() { - // Blindly takes keys in sorted order. - ValKind::Record(m) => visitor - .visit_seq(SeqDeserializer::new(m.iter().map(|(_, v)| val(v)))), - _ => 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 - tuple_struct map struct enum identifier ignored_any - } -} diff --git a/serde_dhall/src/shortcuts.rs b/serde_dhall/src/shortcuts.rs new file mode 100644 index 0000000..1fb1032 --- /dev/null +++ b/serde_dhall/src/shortcuts.rs @@ -0,0 +1,100 @@ +use crate::error::Result; +use crate::options; +use crate::simple::Type as SimpleType; +use crate::static_type::StaticType; +use crate::Deserialize; + +/// Deserialize an instance of type `T` from a string of Dhall text. +/// +/// This will recursively resolve all imports in the expression, and typecheck it before +/// deserialization. Relative imports will be resolved relative to the current directory. +/// See [`options`][`options`] for more control over this process. +/// +/// For additional type safety, prefer [`from_str_static_type`][`from_str_static_type`] or +/// [`from_str_manual_type`][`from_str_manual_type`]. +/// +/// +/// # Example +/// +/// ```rust +/// # fn main() -> serde_dhall::Result<()> { +/// use serde::Deserialize; +/// +/// #[derive(Debug, Deserialize)] +/// struct Point { +/// x: u64, +/// y: u64, +/// } +/// +/// // Some Dhall data +/// let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }"; +/// +/// // Convert the Dhall string to a Point. +/// let point: Point = serde_dhall::from_str(data)?; +/// assert_eq!(point.x, 1); +/// assert_eq!(point.y, 2); +/// +/// # Ok(()) +/// # } +/// ``` +/// +/// [`options`]: options/index.html +/// [`from_str_manual_type`]: fn.from_str_manual_type.html +/// [`from_str_static_type`]: fn.from_str_static_type.html +pub fn from_str(s: &str) -> Result +where + T: Deserialize, +{ + options::from_str(s).parse() +} + +/// Deserialize an instance of type `T` from a string of Dhall text, +/// additionally checking that it matches the supplied type. +/// +/// Like [`from_str`], but this additionally checks that +/// the type of the provided expression matches the supplied type. +/// +/// ```rust +/// # fn main() -> serde_dhall::Result<()> { +/// use serde_dhall::simple::Type; +/// use std::collections::HashMap; +/// +/// // Parse a Dhall type +/// let point_type_str = "{ x: Natural, y: Natural }"; +/// let point_type: Type = serde_dhall::from_str(point_type_str)?; +/// +/// // Some Dhall data +/// let point_data = "{ x = 1, y = 1 + 1 }"; +/// +/// // Deserialize the data to a Rust type. This checks that +/// // the data matches the provided type. +/// let deserialized_map: HashMap = +/// serde_dhall::from_str_manual_type(point_data, &point_type)?; +/// +/// let mut expected_map = HashMap::new(); +/// expected_map.insert("x".to_string(), 1); +/// expected_map.insert("y".to_string(), 2); +/// +/// assert_eq!(deserialized_map, expected_map); +/// # Ok(()) +/// # } +/// ``` +pub fn from_str_manual_type(s: &str, ty: &SimpleType) -> Result +where + T: Deserialize, +{ + options::from_str(s).type_annotation(ty).parse() +} + +/// Deserialize an instance of type `T` from a string of Dhall text, +/// additionally checking that it matches the type of `T`. +/// +/// Like [from_str], but this additionally checks that +/// the type of the provided expression matches the output type `T`. The [StaticType] trait +/// captures Rust types that are valid Dhall types. +pub fn from_str_static_type(s: &str) -> Result +where + T: Deserialize + StaticType, +{ + options::from_str(s).static_type_annotation().parse() +} diff --git a/serde_dhall/src/simple.rs b/serde_dhall/src/simple.rs index 4cd4ab7..95642bd 100644 --- a/serde_dhall/src/simple.rs +++ b/serde_dhall/src/simple.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use dhall::semantics::{Hir, HirKind, Nir, NirKind}; use dhall::syntax::{Builtin, ExprKind, NumKind, Span}; -use crate::{sealed::Sealed, Deserialize, Error, Result}; +use crate::{Deserialize, Error, Result, Sealed}; /// A simple value of the kind that can be encoded/decoded with serde #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs index d4ded90..a119028 100644 --- a/serde_dhall/src/value.rs +++ b/serde_dhall/src/value.rs @@ -2,7 +2,7 @@ use dhall::semantics::{Hir, Nir}; use dhall::syntax::Expr; use crate::simple::{Type as SimpleType, Value as SimpleValue}; -use crate::{sealed::Sealed, Deserialize, Error}; +use crate::{Deserialize, Error, Sealed}; /// An arbitrary Dhall value. #[derive(Debug, Clone)] -- cgit v1.2.3 From 002d7c2a74647312a821598ac3d9f5521296873d Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 22 Mar 2020 21:25:44 +0000 Subject: Add a bunch of TODOs --- serde_dhall/src/deserialize.rs | 3 ++- serde_dhall/src/error.rs | 2 ++ serde_dhall/src/lib.rs | 5 ++--- serde_dhall/src/options.rs | 9 +++++++++ serde_dhall/src/shortcuts.rs | 4 ++++ serde_dhall/src/simple.rs | 2 ++ serde_dhall/src/static_type.rs | 1 + serde_dhall/src/value.rs | 3 +++ 8 files changed, 25 insertions(+), 4 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/deserialize.rs b/serde_dhall/src/deserialize.rs index 75a08a9..cccadad 100644 --- a/serde_dhall/src/deserialize.rs +++ b/serde_dhall/src/deserialize.rs @@ -16,9 +16,10 @@ pub trait Sealed {} /// can deserialize. /// /// This trait cannot be implemented manually. +/// +/// TODO pub trait Deserialize: Sealed + Sized { #[doc(hidden)] - /// See [serde_dhall::from_str][crate::from_str] fn from_dhall(v: &Value) -> Result; } diff --git a/serde_dhall/src/error.rs b/serde_dhall/src/error.rs index 91a0b94..23d1b02 100644 --- a/serde_dhall/src/error.rs +++ b/serde_dhall/src/error.rs @@ -1,9 +1,11 @@ use dhall::error::Error as DhallError; +/// TODO pub type Result = std::result::Result; #[derive(Debug)] #[non_exhaustive] +/// TODO pub enum Error { Dhall(DhallError), Deserialize(String), diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 5ee1cf6..e59255c 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -1,6 +1,5 @@ #![doc(html_root_url = "https://docs.rs/serde_dhall/0.4.0")] -// #![warn(missing_docs)] -// #![warn(missing_doc_code_examples)] +// TODO #![warn(missing_docs)] #![warn(missing_doc_code_examples)] //! [Dhall][dhall] is a programmable configuration language that provides a non-repetitive //! alternative to JSON and YAML. //! @@ -173,7 +172,7 @@ #[cfg(doctest)] doc_comment::doctest!("../../README.md"); -/// Finer-grained control over deserializing Dhall +/// Finer-grained control over deserializing Dhall values pub mod options; /// Serde-compatible Dhall values and their type pub mod simple; diff --git a/serde_dhall/src/options.rs b/serde_dhall/src/options.rs index 542804b..19b8587 100644 --- a/serde_dhall/src/options.rs +++ b/serde_dhall/src/options.rs @@ -48,6 +48,7 @@ enum Source<'a> { /// # Ok(()) /// # } /// ``` +/// /// TODO #[derive(Debug, Clone)] pub struct Options<'a, T> { source: Source<'a>, @@ -69,9 +70,11 @@ impl<'a, T> Options<'a, T> { target_type: std::marker::PhantomData, } } + /// TODO fn from_str(s: &'a str) -> Self { Self::default_with_source(Source::Str(s)) } + /// TODO fn from_file>(path: P) -> Self { Self::default_with_source(Source::File(path.as_ref().to_owned())) } @@ -79,6 +82,7 @@ impl<'a, T> Options<'a, T> { // Self::default_with_source(Source::Url(url)) // } + /// TODO pub fn imports(&mut self, imports: bool) -> &mut Self { self.allow_imports = imports; self @@ -90,10 +94,12 @@ impl<'a, T> Options<'a, T> { // } // self // } + // /// TODO pub fn type_annotation(&mut self, ty: &SimpleType) -> &mut Self { self.annot = Some(ty.clone()); self } + /// TODO pub fn static_type_annotation(&mut self) -> &mut Self where T: StaticType, @@ -118,6 +124,7 @@ impl<'a, T> Options<'a, T> { }; Ok(Value::from_nir(typed.normalize().as_nir())) } + /// TODO pub fn parse(&self) -> Result where T: Deserialize, @@ -127,9 +134,11 @@ impl<'a, T> Options<'a, T> { } } +/// TODO pub fn from_str<'a, T>(s: &'a str) -> Options<'a, T> { Options::from_str(s) } +/// TODO pub fn from_file<'a, T, P: AsRef>(path: P) -> Options<'a, T> { Options::from_file(path) } diff --git a/serde_dhall/src/shortcuts.rs b/serde_dhall/src/shortcuts.rs index 1fb1032..ddb738c 100644 --- a/serde_dhall/src/shortcuts.rs +++ b/serde_dhall/src/shortcuts.rs @@ -79,6 +79,8 @@ where /// # Ok(()) /// # } /// ``` +/// +/// TODO pub fn from_str_manual_type(s: &str, ty: &SimpleType) -> Result where T: Deserialize, @@ -92,6 +94,8 @@ where /// Like [from_str], but this additionally checks that /// the type of the provided expression matches the output type `T`. The [StaticType] trait /// captures Rust types that are valid Dhall types. +/// +/// TODO pub fn from_str_static_type(s: &str) -> Result where T: Deserialize + StaticType, diff --git a/serde_dhall/src/simple.rs b/serde_dhall/src/simple.rs index 95642bd..3d77853 100644 --- a/serde_dhall/src/simple.rs +++ b/serde_dhall/src/simple.rs @@ -6,6 +6,7 @@ use dhall::syntax::{Builtin, ExprKind, NumKind, Span}; use crate::{Deserialize, Error, Result, Sealed}; /// A simple value of the kind that can be encoded/decoded with serde +/// TODO #[derive(Debug, Clone, PartialEq, Eq)] pub struct Value { kind: Box, @@ -24,6 +25,7 @@ pub enum ValKind { } /// The type of a `simple::Value`. +/// TODO #[derive(Debug, Clone, PartialEq, Eq)] pub struct Type { kind: Box, diff --git a/serde_dhall/src/static_type.rs b/serde_dhall/src/static_type.rs index 79418d2..3fdff39 100644 --- a/serde_dhall/src/static_type.rs +++ b/serde_dhall/src/static_type.rs @@ -11,6 +11,7 @@ use crate::simple::{TyKind, Type}; /// For this reason, something like `HashMap` cannot implement /// [StaticType] because each different value would /// have a different Dhall record type. +/// TODO pub trait StaticType { fn static_type() -> Type; } diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs index a119028..2557efe 100644 --- a/serde_dhall/src/value.rs +++ b/serde_dhall/src/value.rs @@ -5,6 +5,7 @@ use crate::simple::{Type as SimpleType, Value as SimpleValue}; use crate::{Deserialize, Error, Sealed}; /// An arbitrary Dhall value. +/// TODO #[derive(Debug, Clone)] pub struct Value { /// Invariant: in normal form @@ -25,10 +26,12 @@ impl Value { } /// Converts a Value into a SimpleValue. + /// TODO pub fn to_simple_value(&self) -> Option { self.as_simple_val.clone() } /// Converts a Value into a SimpleType. + /// TODO pub fn to_simple_type(&self) -> Option { self.as_simple_ty.clone() } -- cgit v1.2.3 From fd4a81b1a92c1859941538b7f2212c621f4b43fd Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 22 Mar 2020 21:29:36 +0000 Subject: Hide SimpleValue from api --- serde_dhall/src/lib.rs | 4 ++-- serde_dhall/src/simple.rs | 8 +++----- serde_dhall/src/value.rs | 3 +-- 3 files changed, 6 insertions(+), 9 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index e59255c..19d3f7e 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -174,7 +174,7 @@ doc_comment::doctest!("../../README.md"); /// Finer-grained control over deserializing Dhall values pub mod options; -/// Serde-compatible Dhall values and their type +/// Serde-compatible Dhall types pub mod simple; /// Arbitrary Dhall values pub mod value; @@ -192,6 +192,6 @@ pub use deserialize::Deserialize; pub(crate) use deserialize::Sealed; pub use error::{Error, Result}; pub use shortcuts::{from_str, from_str_manual_type, from_str_static_type}; -pub use simple::{Type as SimpleType, Value as SimpleValue}; +pub use simple::{Type as SimpleType}; pub use static_type::StaticType; pub use value::Value; diff --git a/serde_dhall/src/simple.rs b/serde_dhall/src/simple.rs index 3d77853..6f3dc93 100644 --- a/serde_dhall/src/simple.rs +++ b/serde_dhall/src/simple.rs @@ -6,14 +6,13 @@ use dhall::syntax::{Builtin, ExprKind, NumKind, Span}; use crate::{Deserialize, Error, Result, Sealed}; /// A simple value of the kind that can be encoded/decoded with serde -/// TODO #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Value { +pub(crate) struct Value { kind: Box, } #[derive(Debug, Clone, PartialEq, Eq)] -pub enum ValKind { +pub(crate) enum ValKind { // TODO: redefine NumKind locally Num(NumKind), Text(String), @@ -24,8 +23,7 @@ pub enum ValKind { Union(String, Option), } -/// The type of a `simple::Value`. -/// TODO +/// The type of a value that can be decoded by Serde. For example, `{ x: Bool, y: List Natural }`. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Type { kind: Box, diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs index 2557efe..41a96ea 100644 --- a/serde_dhall/src/value.rs +++ b/serde_dhall/src/value.rs @@ -26,8 +26,7 @@ impl Value { } /// Converts a Value into a SimpleValue. - /// TODO - pub fn to_simple_value(&self) -> Option { + pub(crate) fn to_simple_value(&self) -> Option { self.as_simple_val.clone() } /// Converts a Value into a SimpleType. -- cgit v1.2.3 From a70922c6c6beb58a45da80f15576b54fb915ec28 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 22 Mar 2020 22:11:00 +0000 Subject: Rework SimpleType --- serde_dhall/src/lib.rs | 12 ++-- serde_dhall/src/options.rs | 5 +- serde_dhall/src/shortcuts.rs | 10 +-- serde_dhall/src/simple.rs | 145 ++++++++++++++++++++++++++--------------- serde_dhall/src/static_type.rs | 26 ++++---- serde_dhall/src/value.rs | 2 +- 6 files changed, 118 insertions(+), 82 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 19d3f7e..8780682 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -107,17 +107,17 @@ //! There are two ways to typecheck a Dhall value: you can provide the type as Dhall text or you //! can let Rust infer it for you. //! -//! To provide a type written in Dhall, first parse it into a [`simple::Type`](simple/struct.Type.html), then +//! To provide a type written in Dhall, first parse it into a [`SimpleType`](enum.SimpleType.html), then //! pass it to [`from_str_manual_type`](fn.from_str_manual_type.html). //! //! ```rust //! # fn main() -> serde_dhall::Result<()> { -//! use serde_dhall::simple::Type; +//! use serde_dhall::SimpleType; //! use std::collections::HashMap; //! //! // Parse a Dhall type //! let point_type_str = "{ x: Natural, y: Natural }"; -//! let point_type: Type = serde_dhall::from_str(point_type_str)?; +//! let point_type: SimpleType = serde_dhall::from_str(point_type_str)?; //! //! // Some Dhall data //! let point_data = "{ x = 1, y = 1 + 1 }"; @@ -174,8 +174,6 @@ doc_comment::doctest!("../../README.md"); /// Finer-grained control over deserializing Dhall values pub mod options; -/// Serde-compatible Dhall types -pub mod simple; /// Arbitrary Dhall values pub mod value; @@ -183,6 +181,8 @@ mod deserialize; mod error; /// Common patterns made easier mod shortcuts; +/// Serde-compatible Dhall types +mod simple; mod static_type; #[doc(hidden)] @@ -192,6 +192,6 @@ pub use deserialize::Deserialize; pub(crate) use deserialize::Sealed; pub use error::{Error, Result}; pub use shortcuts::{from_str, from_str_manual_type, from_str_static_type}; -pub use simple::{Type as SimpleType}; +pub use simple::SimpleType; pub use static_type::StaticType; pub use value::Value; diff --git a/serde_dhall/src/options.rs b/serde_dhall/src/options.rs index 19b8587..0072393 100644 --- a/serde_dhall/src/options.rs +++ b/serde_dhall/src/options.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use dhall::Parsed; -use crate::simple::Type as SimpleType; +use crate::SimpleType; use crate::{Deserialize, Error, Result, StaticType, Value}; #[derive(Debug, Clone)] @@ -94,7 +94,8 @@ impl<'a, T> Options<'a, T> { // } // self // } - // /// TODO + // + /// TODO pub fn type_annotation(&mut self, ty: &SimpleType) -> &mut Self { self.annot = Some(ty.clone()); self diff --git a/serde_dhall/src/shortcuts.rs b/serde_dhall/src/shortcuts.rs index ddb738c..d88b9ac 100644 --- a/serde_dhall/src/shortcuts.rs +++ b/serde_dhall/src/shortcuts.rs @@ -1,8 +1,4 @@ -use crate::error::Result; -use crate::options; -use crate::simple::Type as SimpleType; -use crate::static_type::StaticType; -use crate::Deserialize; +use crate::{options, Deserialize, Result, SimpleType, StaticType}; /// Deserialize an instance of type `T` from a string of Dhall text. /// @@ -56,12 +52,12 @@ where /// /// ```rust /// # fn main() -> serde_dhall::Result<()> { -/// use serde_dhall::simple::Type; +/// use serde_dhall::SimpleType; /// use std::collections::HashMap; /// /// // Parse a Dhall type /// let point_type_str = "{ x: Natural, y: Natural }"; -/// let point_type: Type = serde_dhall::from_str(point_type_str)?; +/// let point_type: SimpleType = serde_dhall::from_str(point_type_str)?; /// /// // Some Dhall data /// let point_data = "{ x = 1, y = 1 + 1 }"; diff --git a/serde_dhall/src/simple.rs b/serde_dhall/src/simple.rs index 6f3dc93..fc640b6 100644 --- a/serde_dhall/src/simple.rs +++ b/serde_dhall/src/simple.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; use dhall::semantics::{Hir, HirKind, Nir, NirKind}; use dhall::syntax::{Builtin, ExprKind, NumKind, Span}; @@ -13,33 +13,82 @@ pub(crate) struct Value { #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) enum ValKind { - // TODO: redefine NumKind locally Num(NumKind), Text(String), Optional(Option), List(Vec), - // TODO: HashMap ? Record(BTreeMap), Union(String, Option), } -/// The type of a value that can be decoded by Serde. For example, `{ x: Bool, y: List Natural }`. +/// The type of a value that can be decoded by Serde, like `{ x: Bool, y: List Natural }`. +/// +/// A `SimpleType` is used when deserializing values to ensure they are of the expected type. +/// Rather than letting `serde` handle potential type mismatches, this uses the type-checking +/// capabilities of Dhall to catch errors early and cleanly indicate in the user's code where the +/// mismatch happened. +/// +/// You would typically not manipulate `SimpleType`s by hand but rather let Rust infer it for your +/// datatype using the [`StaticType`][TODO] trait, and methods that require it like +/// [`from_file_static_type`][TODO] and [`Options::static_type_annotation`][TODO]. If you need to supply a +/// `SimpleType` manually however, you can deserialize it like any other Dhall value using the +/// functions provided by this crate. +/// +/// # Examples +/// +/// ```rust +/// # fn main() -> serde_dhall::Result<()> { +/// use std::collections::HashMap; +/// use serde_dhall::SimpleType; +/// +/// let ty: SimpleType = +/// serde_dhall::from_str("{ x: Natural, y: Natural }")?; +/// +/// let mut map = HashMap::new(); +/// map.insert("x".to_string(), SimpleType::Natural); +/// map.insert("y".to_string(), SimpleType::Natural); +/// assert_eq!(ty, SimpleType::Record(map)); +/// # Ok(()) +/// # } +/// ``` +/// +/// ```rust +/// # fn main() -> serde_dhall::Result<()> { +/// use serde_dhall::{SimpleType, StaticType}; +/// +/// #[derive(StaticType)] +/// struct Foo { +/// x: bool, +/// y: Vec, +/// } +/// +/// let ty: SimpleType = +/// serde_dhall::from_str("{ x: Bool, y: List Natural }")?; +/// +/// assert_eq!(ty, Foo::static_type()); +/// # Ok(()) +/// # } +/// ``` #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Type { - kind: Box, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum TyKind { +pub enum SimpleType { + /// Corresponds to the Dhall type `Bool` Bool, + /// Corresponds to the Dhall type `Natural` Natural, + /// Corresponds to the Dhall type `Integer` Integer, + /// Corresponds to the Dhall type `Double` Double, + /// Corresponds to the Dhall type `Text` Text, - Optional(Type), - List(Type), - Record(BTreeMap), - Union(BTreeMap>), + /// Corresponds to the Dhall type `Optional T` + Optional(Box), + /// Corresponds to the Dhall type `List T` + List(Box), + /// Corresponds to the Dhall type `{ x : T, y : U }` + Record(HashMap), + /// Corresponds to the Dhall type `< x : T | y : U >` + Union(HashMap>), } impl Value { @@ -87,30 +136,29 @@ impl Value { } } -impl Type { - pub fn new(kind: TyKind) -> Self { - Type { - kind: Box::new(kind), - } - } +impl SimpleType { pub(crate) fn from_nir(nir: &Nir) -> Option { - Some(Type::new(match nir.kind() { + Some(match nir.kind() { NirKind::BuiltinType(b) => match b { - Builtin::Bool => TyKind::Bool, - Builtin::Natural => TyKind::Natural, - Builtin::Integer => TyKind::Integer, - Builtin::Double => TyKind::Double, - Builtin::Text => TyKind::Text, + Builtin::Bool => SimpleType::Bool, + Builtin::Natural => SimpleType::Natural, + Builtin::Integer => SimpleType::Integer, + Builtin::Double => SimpleType::Double, + Builtin::Text => SimpleType::Text, _ => unreachable!(), }, - NirKind::OptionalType(t) => TyKind::Optional(Self::from_nir(t)?), - NirKind::ListType(t) => TyKind::List(Self::from_nir(t)?), - NirKind::RecordType(kts) => TyKind::Record( + NirKind::OptionalType(t) => { + SimpleType::Optional(Box::new(Self::from_nir(t)?)) + } + NirKind::ListType(t) => { + SimpleType::List(Box::new(Self::from_nir(t)?)) + } + NirKind::RecordType(kts) => SimpleType::Record( kts.iter() .map(|(k, v)| Some((k.into(), Self::from_nir(v)?))) .collect::>()?, ), - NirKind::UnionType(kts) => TyKind::Union( + NirKind::UnionType(kts) => SimpleType::Union( kts.iter() .map(|(k, v)| { Some(( @@ -123,13 +171,10 @@ impl Type { .collect::>()?, ), _ => return None, - })) + }) } - pub fn kind(&self) -> &TyKind { - self.kind.as_ref() - } - pub fn to_value(&self) -> crate::value::Value { + pub(crate) fn to_value(&self) -> crate::value::Value { crate::value::Value { hir: self.to_hir(), as_simple_val: None, @@ -138,25 +183,25 @@ impl Type { } pub(crate) fn to_hir(&self) -> Hir { let hir = |k| Hir::new(HirKind::Expr(k), Span::Artificial); - hir(match self.kind() { - TyKind::Bool => ExprKind::Builtin(Builtin::Bool), - TyKind::Natural => ExprKind::Builtin(Builtin::Natural), - TyKind::Integer => ExprKind::Builtin(Builtin::Integer), - TyKind::Double => ExprKind::Builtin(Builtin::Double), - TyKind::Text => ExprKind::Builtin(Builtin::Text), - TyKind::Optional(t) => ExprKind::App( + hir(match self { + SimpleType::Bool => ExprKind::Builtin(Builtin::Bool), + SimpleType::Natural => ExprKind::Builtin(Builtin::Natural), + SimpleType::Integer => ExprKind::Builtin(Builtin::Integer), + SimpleType::Double => ExprKind::Builtin(Builtin::Double), + SimpleType::Text => ExprKind::Builtin(Builtin::Text), + SimpleType::Optional(t) => ExprKind::App( hir(ExprKind::Builtin(Builtin::Optional)), t.to_hir(), ), - TyKind::List(t) => { + SimpleType::List(t) => { ExprKind::App(hir(ExprKind::Builtin(Builtin::List)), t.to_hir()) } - TyKind::Record(kts) => ExprKind::RecordType( + SimpleType::Record(kts) => ExprKind::RecordType( kts.into_iter() .map(|(k, t)| (k.as_str().into(), t.to_hir())) .collect(), ), - TyKind::Union(kts) => ExprKind::UnionType( + SimpleType::Union(kts) => ExprKind::UnionType( kts.into_iter() .map(|(k, t)| { (k.as_str().into(), t.as_ref().map(|t| t.to_hir())) @@ -180,9 +225,9 @@ impl Deserialize for Value { } } -impl Sealed for Type {} +impl Sealed for SimpleType {} -impl Deserialize for Type { +impl Deserialize for SimpleType { fn from_dhall(v: &crate::value::Value) -> Result { v.to_simple_type().ok_or_else(|| { Error::Deserialize(format!( @@ -192,9 +237,3 @@ impl Deserialize for Type { }) } } - -impl From for Type { - fn from(x: TyKind) -> Type { - Type::new(x) - } -} diff --git a/serde_dhall/src/static_type.rs b/serde_dhall/src/static_type.rs index 3fdff39..ffbc3ad 100644 --- a/serde_dhall/src/static_type.rs +++ b/serde_dhall/src/static_type.rs @@ -1,4 +1,4 @@ -use crate::simple::{TyKind, Type}; +use crate::SimpleType; /// A Rust type that can be represented as a Dhall type. /// @@ -13,14 +13,14 @@ use crate::simple::{TyKind, Type}; /// have a different Dhall record type. /// TODO pub trait StaticType { - fn static_type() -> Type; + fn static_type() -> SimpleType; } macro_rules! derive_builtin { ($rust_ty:ty, $dhall_ty:ident) => { impl StaticType for $rust_ty { - fn static_type() -> Type { - Type::new(TyKind::$dhall_ty) + fn static_type() -> SimpleType { + SimpleType::$dhall_ty } } }; @@ -42,8 +42,8 @@ where A: StaticType, B: StaticType, { - fn static_type() -> Type { - TyKind::Record( + fn static_type() -> SimpleType { + SimpleType::Record( vec![ ("_1".to_owned(), A::static_type()), ("_2".to_owned(), B::static_type()), @@ -60,8 +60,8 @@ where T: StaticType, E: StaticType, { - fn static_type() -> Type { - TyKind::Union( + fn static_type() -> SimpleType { + SimpleType::Union( vec![ ("Ok".to_owned(), Some(T::static_type())), ("Err".to_owned(), Some(E::static_type())), @@ -77,8 +77,8 @@ impl StaticType for Option where T: StaticType, { - fn static_type() -> Type { - TyKind::Optional(T::static_type()).into() + fn static_type() -> SimpleType { + SimpleType::Optional(Box::new(T::static_type())).into() } } @@ -86,8 +86,8 @@ impl StaticType for Vec where T: StaticType, { - fn static_type() -> Type { - TyKind::List(T::static_type()).into() + fn static_type() -> SimpleType { + SimpleType::List(Box::new(T::static_type())).into() } } @@ -95,7 +95,7 @@ impl<'a, T> StaticType for &'a T where T: StaticType, { - fn static_type() -> Type { + fn static_type() -> SimpleType { T::static_type() } } diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs index 41a96ea..e8aae49 100644 --- a/serde_dhall/src/value.rs +++ b/serde_dhall/src/value.rs @@ -1,7 +1,7 @@ use dhall::semantics::{Hir, Nir}; use dhall::syntax::Expr; -use crate::simple::{Type as SimpleType, Value as SimpleValue}; +use crate::simple::{SimpleType, Value as SimpleValue}; use crate::{Deserialize, Error, Sealed}; /// An arbitrary Dhall value. -- cgit v1.2.3 From f598e3612a854076b570bbc9c88abcdc1da528bd Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 22 Mar 2020 22:17:22 +0000 Subject: Rework SimpleValue --- serde_dhall/src/deserialize.rs | 10 +++--- serde_dhall/src/simple.rs | 73 +++++++++++++++++------------------------- serde_dhall/src/value.rs | 2 +- 3 files changed, 35 insertions(+), 50 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/deserialize.rs b/serde_dhall/src/deserialize.rs index cccadad..43b7914 100644 --- a/serde_dhall/src/deserialize.rs +++ b/serde_dhall/src/deserialize.rs @@ -5,7 +5,7 @@ use std::borrow::Cow; use dhall::syntax::NumKind; -use crate::simple::{ValKind, Value as SimpleValue}; +use crate::simple::SimpleValue; use crate::{Error, Result, Value}; pub trait Sealed {} @@ -58,10 +58,10 @@ impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { { use std::convert::TryInto; use NumKind::*; - use ValKind::*; + use SimpleValue::*; let val = |x| Deserializer(Cow::Borrowed(x)); - match self.0.kind() { + match self.0.as_ref() { Num(Bool(x)) => visitor.visit_bool(*x), Num(Natural(x)) => { if let Ok(x64) = (*x).try_into() { @@ -109,9 +109,9 @@ impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { V: serde::de::Visitor<'de>, { let val = |x| Deserializer(Cow::Borrowed(x)); - match self.0.kind() { + match self.0.as_ref() { // Blindly takes keys in sorted order. - ValKind::Record(m) => visitor + SimpleValue::Record(m) => visitor .visit_seq(SeqDeserializer::new(m.iter().map(|(_, v)| val(v)))), _ => self.deserialize_any(visitor), } diff --git a/serde_dhall/src/simple.rs b/serde_dhall/src/simple.rs index fc640b6..c1f0047 100644 --- a/serde_dhall/src/simple.rs +++ b/serde_dhall/src/simple.rs @@ -3,22 +3,17 @@ use std::collections::{BTreeMap, HashMap}; use dhall::semantics::{Hir, HirKind, Nir, NirKind}; use dhall::syntax::{Builtin, ExprKind, NumKind, Span}; -use crate::{Deserialize, Error, Result, Sealed}; +use crate::{Deserialize, Error, Result, Sealed, Value}; -/// A simple value of the kind that can be encoded/decoded with serde +/// A simple value of the kind that can be decoded with serde #[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) struct Value { - kind: Box, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) enum ValKind { +pub(crate) enum SimpleValue { Num(NumKind), Text(String), - Optional(Option), - List(Vec), - Record(BTreeMap), - Union(String, Option), + Optional(Option>), + List(Vec), + Record(BTreeMap), + Union(String, Option>), } /// The type of a value that can be decoded by Serde, like `{ x: Bool, y: List Natural }`. @@ -91,48 +86,38 @@ pub enum SimpleType { Union(HashMap>), } -impl Value { - pub(crate) fn new(kind: ValKind) -> Self { - Value { - kind: Box::new(kind), - } - } +impl SimpleValue { pub(crate) fn from_nir(nir: &Nir) -> Option { - Some(Value::new(match nir.kind() { - NirKind::Num(lit) => ValKind::Num(lit.clone()), - NirKind::TextLit(x) => ValKind::Text( + Some(match nir.kind() { + NirKind::Num(lit) => SimpleValue::Num(lit.clone()), + NirKind::TextLit(x) => SimpleValue::Text( x.as_text() .expect("Normal form should ensure the text is a string"), ), - NirKind::EmptyOptionalLit(_) => ValKind::Optional(None), + NirKind::EmptyOptionalLit(_) => SimpleValue::Optional(None), NirKind::NEOptionalLit(x) => { - ValKind::Optional(Some(Self::from_nir(x)?)) + SimpleValue::Optional(Some(Box::new(Self::from_nir(x)?))) } - NirKind::EmptyListLit(_) => ValKind::List(vec![]), - NirKind::NEListLit(xs) => ValKind::List( - xs.iter() - .map(|v| Self::from_nir(v)) - .collect::>()?, + NirKind::EmptyListLit(_) => SimpleValue::List(vec![]), + NirKind::NEListLit(xs) => SimpleValue::List( + xs.iter().map(Self::from_nir).collect::>()?, ), - NirKind::RecordLit(kvs) => ValKind::Record( + NirKind::RecordLit(kvs) => SimpleValue::Record( kvs.iter() .map(|(k, v)| Some((k.into(), Self::from_nir(v)?))) .collect::>()?, ), - NirKind::UnionLit(field, x, _) => { - ValKind::Union(field.into(), Some(Self::from_nir(x)?)) - } + NirKind::UnionLit(field, x, _) => SimpleValue::Union( + field.into(), + Some(Box::new(Self::from_nir(x)?)), + ), NirKind::UnionConstructor(field, ty) if ty.get(field).map(|f| f.is_some()) == Some(false) => { - ValKind::Union(field.into(), None) + SimpleValue::Union(field.into(), None) } _ => return None, - })) - } - - pub fn kind(&self) -> &ValKind { - self.kind.as_ref() + }) } } @@ -174,8 +159,8 @@ impl SimpleType { }) } - pub(crate) fn to_value(&self) -> crate::value::Value { - crate::value::Value { + pub(crate) fn to_value(&self) -> Value { + Value { hir: self.to_hir(), as_simple_val: None, as_simple_ty: Some(self.clone()), @@ -212,10 +197,10 @@ impl SimpleType { } } -impl Sealed for Value {} +impl Sealed for SimpleValue {} -impl Deserialize for Value { - fn from_dhall(v: &crate::value::Value) -> Result { +impl Deserialize for SimpleValue { + fn from_dhall(v: &Value) -> Result { v.to_simple_value().ok_or_else(|| { Error::Deserialize(format!( "this cannot be deserialized into a simple type: {}", @@ -228,7 +213,7 @@ impl Deserialize for Value { impl Sealed for SimpleType {} impl Deserialize for SimpleType { - fn from_dhall(v: &crate::value::Value) -> Result { + fn from_dhall(v: &Value) -> Result { v.to_simple_type().ok_or_else(|| { Error::Deserialize(format!( "this cannot be deserialized into a simple type: {}", diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs index e8aae49..ed932f5 100644 --- a/serde_dhall/src/value.rs +++ b/serde_dhall/src/value.rs @@ -1,7 +1,7 @@ use dhall::semantics::{Hir, Nir}; use dhall::syntax::Expr; -use crate::simple::{SimpleType, Value as SimpleValue}; +use crate::simple::{SimpleType, SimpleValue}; use crate::{Deserialize, Error, Sealed}; /// An arbitrary Dhall value. -- cgit v1.2.3 From 263fbebda7bc3c0f8c4497e1e508125ca91382f1 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 22 Mar 2020 22:22:46 +0000 Subject: Hide Value from API --- serde_dhall/src/lib.rs | 4 ++-- serde_dhall/src/value.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 8780682..c6c6bf9 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -174,8 +174,6 @@ doc_comment::doctest!("../../README.md"); /// Finer-grained control over deserializing Dhall values pub mod options; -/// Arbitrary Dhall values -pub mod value; mod deserialize; mod error; @@ -184,6 +182,8 @@ mod shortcuts; /// Serde-compatible Dhall types mod simple; mod static_type; +/// Dhall values +mod value; #[doc(hidden)] pub use dhall_proc_macros::StaticType; diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs index ed932f5..88727bc 100644 --- a/serde_dhall/src/value.rs +++ b/serde_dhall/src/value.rs @@ -4,8 +4,8 @@ use dhall::syntax::Expr; use crate::simple::{SimpleType, SimpleValue}; use crate::{Deserialize, Error, Sealed}; +#[doc(hidden)] /// An arbitrary Dhall value. -/// TODO #[derive(Debug, Clone)] pub struct Value { /// Invariant: in normal form @@ -29,9 +29,9 @@ impl Value { pub(crate) fn to_simple_value(&self) -> Option { self.as_simple_val.clone() } + /// Converts a Value into a SimpleType. - /// TODO - pub fn to_simple_type(&self) -> Option { + pub(crate) fn to_simple_type(&self) -> Option { self.as_simple_ty.clone() } -- cgit v1.2.3 From 3a5f2044954aae7278f16e30561a81626dba6923 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 22 Mar 2020 22:27:56 +0000 Subject: Move mod simple into value --- serde_dhall/src/deserialize.rs | 2 +- serde_dhall/src/lib.rs | 5 +- serde_dhall/src/options.rs | 2 +- serde_dhall/src/simple.rs | 224 ------------------------------------ serde_dhall/src/value.rs | 252 ++++++++++++++++++++++++++++++++++++++--- 5 files changed, 240 insertions(+), 245 deletions(-) delete mode 100644 serde_dhall/src/simple.rs (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/deserialize.rs b/serde_dhall/src/deserialize.rs index 43b7914..ab5580e 100644 --- a/serde_dhall/src/deserialize.rs +++ b/serde_dhall/src/deserialize.rs @@ -5,7 +5,7 @@ use std::borrow::Cow; use dhall::syntax::NumKind; -use crate::simple::SimpleValue; +use crate::value::SimpleValue; use crate::{Error, Result, Value}; pub trait Sealed {} diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index c6c6bf9..02b34a7 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -179,8 +179,6 @@ mod deserialize; mod error; /// Common patterns made easier mod shortcuts; -/// Serde-compatible Dhall types -mod simple; mod static_type; /// Dhall values mod value; @@ -192,6 +190,5 @@ pub use deserialize::Deserialize; pub(crate) use deserialize::Sealed; pub use error::{Error, Result}; pub use shortcuts::{from_str, from_str_manual_type, from_str_static_type}; -pub use simple::SimpleType; pub use static_type::StaticType; -pub use value::Value; +pub use value::{SimpleType, Value}; diff --git a/serde_dhall/src/options.rs b/serde_dhall/src/options.rs index 0072393..f468cf0 100644 --- a/serde_dhall/src/options.rs +++ b/serde_dhall/src/options.rs @@ -121,7 +121,7 @@ impl<'a, T> Options<'a, T> { }; let typed = match &self.annot { None => resolved.typecheck()?, - Some(ty) => resolved.typecheck_with(&ty.to_value().hir)?, + Some(ty) => resolved.typecheck_with(ty.to_value().as_hir())?, }; Ok(Value::from_nir(typed.normalize().as_nir())) } diff --git a/serde_dhall/src/simple.rs b/serde_dhall/src/simple.rs deleted file mode 100644 index c1f0047..0000000 --- a/serde_dhall/src/simple.rs +++ /dev/null @@ -1,224 +0,0 @@ -use std::collections::{BTreeMap, HashMap}; - -use dhall::semantics::{Hir, HirKind, Nir, NirKind}; -use dhall::syntax::{Builtin, ExprKind, NumKind, Span}; - -use crate::{Deserialize, Error, Result, Sealed, Value}; - -/// A simple value of the kind that can be decoded with serde -#[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) enum SimpleValue { - Num(NumKind), - Text(String), - Optional(Option>), - List(Vec), - Record(BTreeMap), - Union(String, Option>), -} - -/// The type of a value that can be decoded by Serde, like `{ x: Bool, y: List Natural }`. -/// -/// A `SimpleType` is used when deserializing values to ensure they are of the expected type. -/// Rather than letting `serde` handle potential type mismatches, this uses the type-checking -/// capabilities of Dhall to catch errors early and cleanly indicate in the user's code where the -/// mismatch happened. -/// -/// You would typically not manipulate `SimpleType`s by hand but rather let Rust infer it for your -/// datatype using the [`StaticType`][TODO] trait, and methods that require it like -/// [`from_file_static_type`][TODO] and [`Options::static_type_annotation`][TODO]. If you need to supply a -/// `SimpleType` manually however, you can deserialize it like any other Dhall value using the -/// functions provided by this crate. -/// -/// # Examples -/// -/// ```rust -/// # fn main() -> serde_dhall::Result<()> { -/// use std::collections::HashMap; -/// use serde_dhall::SimpleType; -/// -/// let ty: SimpleType = -/// serde_dhall::from_str("{ x: Natural, y: Natural }")?; -/// -/// let mut map = HashMap::new(); -/// map.insert("x".to_string(), SimpleType::Natural); -/// map.insert("y".to_string(), SimpleType::Natural); -/// assert_eq!(ty, SimpleType::Record(map)); -/// # Ok(()) -/// # } -/// ``` -/// -/// ```rust -/// # fn main() -> serde_dhall::Result<()> { -/// use serde_dhall::{SimpleType, StaticType}; -/// -/// #[derive(StaticType)] -/// struct Foo { -/// x: bool, -/// y: Vec, -/// } -/// -/// let ty: SimpleType = -/// serde_dhall::from_str("{ x: Bool, y: List Natural }")?; -/// -/// assert_eq!(ty, Foo::static_type()); -/// # Ok(()) -/// # } -/// ``` -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum SimpleType { - /// Corresponds to the Dhall type `Bool` - Bool, - /// Corresponds to the Dhall type `Natural` - Natural, - /// Corresponds to the Dhall type `Integer` - Integer, - /// Corresponds to the Dhall type `Double` - Double, - /// Corresponds to the Dhall type `Text` - Text, - /// Corresponds to the Dhall type `Optional T` - Optional(Box), - /// Corresponds to the Dhall type `List T` - List(Box), - /// Corresponds to the Dhall type `{ x : T, y : U }` - Record(HashMap), - /// Corresponds to the Dhall type `< x : T | y : U >` - Union(HashMap>), -} - -impl SimpleValue { - pub(crate) fn from_nir(nir: &Nir) -> Option { - Some(match nir.kind() { - NirKind::Num(lit) => SimpleValue::Num(lit.clone()), - NirKind::TextLit(x) => SimpleValue::Text( - x.as_text() - .expect("Normal form should ensure the text is a string"), - ), - NirKind::EmptyOptionalLit(_) => SimpleValue::Optional(None), - NirKind::NEOptionalLit(x) => { - SimpleValue::Optional(Some(Box::new(Self::from_nir(x)?))) - } - NirKind::EmptyListLit(_) => SimpleValue::List(vec![]), - NirKind::NEListLit(xs) => SimpleValue::List( - xs.iter().map(Self::from_nir).collect::>()?, - ), - NirKind::RecordLit(kvs) => SimpleValue::Record( - kvs.iter() - .map(|(k, v)| Some((k.into(), Self::from_nir(v)?))) - .collect::>()?, - ), - NirKind::UnionLit(field, x, _) => SimpleValue::Union( - field.into(), - Some(Box::new(Self::from_nir(x)?)), - ), - NirKind::UnionConstructor(field, ty) - if ty.get(field).map(|f| f.is_some()) == Some(false) => - { - SimpleValue::Union(field.into(), None) - } - _ => return None, - }) - } -} - -impl SimpleType { - pub(crate) fn from_nir(nir: &Nir) -> Option { - Some(match nir.kind() { - NirKind::BuiltinType(b) => match b { - Builtin::Bool => SimpleType::Bool, - Builtin::Natural => SimpleType::Natural, - Builtin::Integer => SimpleType::Integer, - Builtin::Double => SimpleType::Double, - Builtin::Text => SimpleType::Text, - _ => unreachable!(), - }, - NirKind::OptionalType(t) => { - SimpleType::Optional(Box::new(Self::from_nir(t)?)) - } - NirKind::ListType(t) => { - SimpleType::List(Box::new(Self::from_nir(t)?)) - } - NirKind::RecordType(kts) => SimpleType::Record( - kts.iter() - .map(|(k, v)| Some((k.into(), Self::from_nir(v)?))) - .collect::>()?, - ), - NirKind::UnionType(kts) => SimpleType::Union( - kts.iter() - .map(|(k, v)| { - Some(( - k.into(), - v.as_ref() - .map(|v| Ok(Self::from_nir(v)?)) - .transpose()?, - )) - }) - .collect::>()?, - ), - _ => return None, - }) - } - - pub(crate) fn to_value(&self) -> Value { - Value { - hir: self.to_hir(), - as_simple_val: None, - as_simple_ty: Some(self.clone()), - } - } - pub(crate) fn to_hir(&self) -> Hir { - let hir = |k| Hir::new(HirKind::Expr(k), Span::Artificial); - hir(match self { - SimpleType::Bool => ExprKind::Builtin(Builtin::Bool), - SimpleType::Natural => ExprKind::Builtin(Builtin::Natural), - SimpleType::Integer => ExprKind::Builtin(Builtin::Integer), - SimpleType::Double => ExprKind::Builtin(Builtin::Double), - SimpleType::Text => ExprKind::Builtin(Builtin::Text), - SimpleType::Optional(t) => ExprKind::App( - hir(ExprKind::Builtin(Builtin::Optional)), - t.to_hir(), - ), - SimpleType::List(t) => { - ExprKind::App(hir(ExprKind::Builtin(Builtin::List)), t.to_hir()) - } - SimpleType::Record(kts) => ExprKind::RecordType( - kts.into_iter() - .map(|(k, t)| (k.as_str().into(), t.to_hir())) - .collect(), - ), - SimpleType::Union(kts) => ExprKind::UnionType( - kts.into_iter() - .map(|(k, t)| { - (k.as_str().into(), t.as_ref().map(|t| t.to_hir())) - }) - .collect(), - ), - }) - } -} - -impl Sealed for SimpleValue {} - -impl Deserialize for SimpleValue { - fn from_dhall(v: &Value) -> Result { - v.to_simple_value().ok_or_else(|| { - Error::Deserialize(format!( - "this cannot be deserialized into a simple type: {}", - v - )) - }) - } -} - -impl Sealed for SimpleType {} - -impl Deserialize for SimpleType { - fn from_dhall(v: &Value) -> Result { - v.to_simple_type().ok_or_else(|| { - Error::Deserialize(format!( - "this cannot be deserialized into a simple type: {}", - v - )) - }) - } -} diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs index 88727bc..f542f0a 100644 --- a/serde_dhall/src/value.rs +++ b/serde_dhall/src/value.rs @@ -1,19 +1,101 @@ -use dhall::semantics::{Hir, Nir}; -use dhall::syntax::Expr; +use std::collections::{BTreeMap, HashMap}; -use crate::simple::{SimpleType, SimpleValue}; -use crate::{Deserialize, Error, Sealed}; +use dhall::semantics::{Hir, HirKind, Nir, NirKind}; +use dhall::syntax::{Builtin, Expr, ExprKind, NumKind, Span}; + +use crate::{Deserialize, Error, Result, Sealed}; #[doc(hidden)] /// An arbitrary Dhall value. #[derive(Debug, Clone)] pub struct Value { /// Invariant: in normal form - pub(crate) hir: Hir, + hir: Hir, /// Cached conversions because they are annoying to construct from Hir. /// At most one of them will be `Some`. - pub(crate) as_simple_val: Option, - pub(crate) as_simple_ty: Option, + as_simple_val: Option, + as_simple_ty: Option, +} + +/// A simple value of the kind that can be decoded with serde +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum SimpleValue { + Num(NumKind), + Text(String), + Optional(Option>), + List(Vec), + Record(BTreeMap), + Union(String, Option>), +} + +/// The type of a value that can be decoded by Serde, like `{ x: Bool, y: List Natural }`. +/// +/// A `SimpleType` is used when deserializing values to ensure they are of the expected type. +/// Rather than letting `serde` handle potential type mismatches, this uses the type-checking +/// capabilities of Dhall to catch errors early and cleanly indicate in the user's code where the +/// mismatch happened. +/// +/// You would typically not manipulate `SimpleType`s by hand but rather let Rust infer it for your +/// datatype using the [`StaticType`][TODO] trait, and methods that require it like +/// [`from_file_static_type`][TODO] and [`Options::static_type_annotation`][TODO]. If you need to supply a +/// `SimpleType` manually however, you can deserialize it like any other Dhall value using the +/// functions provided by this crate. +/// +/// # Examples +/// +/// ```rust +/// # fn main() -> serde_dhall::Result<()> { +/// use std::collections::HashMap; +/// use serde_dhall::SimpleType; +/// +/// let ty: SimpleType = +/// serde_dhall::from_str("{ x: Natural, y: Natural }")?; +/// +/// let mut map = HashMap::new(); +/// map.insert("x".to_string(), SimpleType::Natural); +/// map.insert("y".to_string(), SimpleType::Natural); +/// assert_eq!(ty, SimpleType::Record(map)); +/// # Ok(()) +/// # } +/// ``` +/// +/// ```rust +/// # fn main() -> serde_dhall::Result<()> { +/// use serde_dhall::{SimpleType, StaticType}; +/// +/// #[derive(StaticType)] +/// struct Foo { +/// x: bool, +/// y: Vec, +/// } +/// +/// let ty: SimpleType = +/// serde_dhall::from_str("{ x: Bool, y: List Natural }")?; +/// +/// assert_eq!(ty, Foo::static_type()); +/// # Ok(()) +/// # } +/// ``` +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SimpleType { + /// Corresponds to the Dhall type `Bool` + Bool, + /// Corresponds to the Dhall type `Natural` + Natural, + /// Corresponds to the Dhall type `Integer` + Integer, + /// Corresponds to the Dhall type `Double` + Double, + /// Corresponds to the Dhall type `Text` + Text, + /// Corresponds to the Dhall type `Optional T` + Optional(Box), + /// Corresponds to the Dhall type `List T` + List(Box), + /// Corresponds to the Dhall type `{ x : T, y : U }` + Record(HashMap), + /// Corresponds to the Dhall type `< x : T | y : U >` + Union(HashMap>), } impl Value { @@ -25,6 +107,10 @@ impl Value { } } + pub(crate) fn as_hir(&self) -> &Hir { + &self.hir + } + /// Converts a Value into a SimpleValue. pub(crate) fn to_simple_value(&self) -> Option { self.as_simple_val.clone() @@ -41,22 +127,158 @@ impl Value { } } -impl Eq for Value {} -impl PartialEq for Value { - fn eq(&self, other: &Self) -> bool { - self.hir == other.hir +impl SimpleValue { + pub(crate) fn from_nir(nir: &Nir) -> Option { + Some(match nir.kind() { + NirKind::Num(lit) => SimpleValue::Num(lit.clone()), + NirKind::TextLit(x) => SimpleValue::Text( + x.as_text() + .expect("Normal form should ensure the text is a string"), + ), + NirKind::EmptyOptionalLit(_) => SimpleValue::Optional(None), + NirKind::NEOptionalLit(x) => { + SimpleValue::Optional(Some(Box::new(Self::from_nir(x)?))) + } + NirKind::EmptyListLit(_) => SimpleValue::List(vec![]), + NirKind::NEListLit(xs) => SimpleValue::List( + xs.iter().map(Self::from_nir).collect::>()?, + ), + NirKind::RecordLit(kvs) => SimpleValue::Record( + kvs.iter() + .map(|(k, v)| Some((k.into(), Self::from_nir(v)?))) + .collect::>()?, + ), + NirKind::UnionLit(field, x, _) => SimpleValue::Union( + field.into(), + Some(Box::new(Self::from_nir(x)?)), + ), + NirKind::UnionConstructor(field, ty) + if ty.get(field).map(|f| f.is_some()) == Some(false) => + { + SimpleValue::Union(field.into(), None) + } + _ => return None, + }) } } -impl std::fmt::Display for Value { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - self.to_expr().fmt(f) + +impl SimpleType { + pub(crate) fn from_nir(nir: &Nir) -> Option { + Some(match nir.kind() { + NirKind::BuiltinType(b) => match b { + Builtin::Bool => SimpleType::Bool, + Builtin::Natural => SimpleType::Natural, + Builtin::Integer => SimpleType::Integer, + Builtin::Double => SimpleType::Double, + Builtin::Text => SimpleType::Text, + _ => unreachable!(), + }, + NirKind::OptionalType(t) => { + SimpleType::Optional(Box::new(Self::from_nir(t)?)) + } + NirKind::ListType(t) => { + SimpleType::List(Box::new(Self::from_nir(t)?)) + } + NirKind::RecordType(kts) => SimpleType::Record( + kts.iter() + .map(|(k, v)| Some((k.into(), Self::from_nir(v)?))) + .collect::>()?, + ), + NirKind::UnionType(kts) => SimpleType::Union( + kts.iter() + .map(|(k, v)| { + Some(( + k.into(), + v.as_ref() + .map(|v| Ok(Self::from_nir(v)?)) + .transpose()?, + )) + }) + .collect::>()?, + ), + _ => return None, + }) + } + + pub(crate) fn to_value(&self) -> Value { + Value { + hir: self.to_hir(), + as_simple_val: None, + as_simple_ty: Some(self.clone()), + } + } + pub(crate) fn to_hir(&self) -> Hir { + let hir = |k| Hir::new(HirKind::Expr(k), Span::Artificial); + hir(match self { + SimpleType::Bool => ExprKind::Builtin(Builtin::Bool), + SimpleType::Natural => ExprKind::Builtin(Builtin::Natural), + SimpleType::Integer => ExprKind::Builtin(Builtin::Integer), + SimpleType::Double => ExprKind::Builtin(Builtin::Double), + SimpleType::Text => ExprKind::Builtin(Builtin::Text), + SimpleType::Optional(t) => ExprKind::App( + hir(ExprKind::Builtin(Builtin::Optional)), + t.to_hir(), + ), + SimpleType::List(t) => { + ExprKind::App(hir(ExprKind::Builtin(Builtin::List)), t.to_hir()) + } + SimpleType::Record(kts) => ExprKind::RecordType( + kts.into_iter() + .map(|(k, t)| (k.as_str().into(), t.to_hir())) + .collect(), + ), + SimpleType::Union(kts) => ExprKind::UnionType( + kts.into_iter() + .map(|(k, t)| { + (k.as_str().into(), t.as_ref().map(|t| t.to_hir())) + }) + .collect(), + ), + }) } } impl Sealed for Value {} +impl Sealed for SimpleValue {} +impl Sealed for SimpleType {} impl Deserialize for Value { - fn from_dhall(v: &Value) -> Result { + fn from_dhall(v: &Value) -> Result { Ok(v.clone()) } } +impl Deserialize for SimpleValue { + fn from_dhall(v: &Value) -> Result { + v.to_simple_value().ok_or_else(|| { + Error::Deserialize(format!( + "this cannot be deserialized into a simple type: {}", + v + )) + }) + } +} +impl Deserialize for SimpleType { + fn from_dhall(v: &Value) -> Result { + v.to_simple_type().ok_or_else(|| { + Error::Deserialize(format!( + "this cannot be deserialized into a simple type: {}", + v + )) + }) + } +} + +impl Eq for Value {} +impl PartialEq for Value { + fn eq(&self, other: &Self) -> bool { + self.hir == other.hir + } +} +impl std::fmt::Display for Value { + fn fmt( + &self, + f: &mut std::fmt::Formatter, + ) -> std::result::Result<(), std::fmt::Error> { + self.to_expr().fmt(f) + } +} -- cgit v1.2.3 From 25c879e802e90a447e10e5f9a0f522217e34f20d Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 22 Mar 2020 22:59:11 +0000 Subject: Document more --- serde_dhall/src/shortcuts.rs | 41 ++++++++++++++++++++++++++--------------- serde_dhall/src/static_type.rs | 33 +++++++++++++++++++++++++-------- serde_dhall/src/value.rs | 10 +++++++--- 3 files changed, 58 insertions(+), 26 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/shortcuts.rs b/serde_dhall/src/shortcuts.rs index d88b9ac..54888c5 100644 --- a/serde_dhall/src/shortcuts.rs +++ b/serde_dhall/src/shortcuts.rs @@ -4,11 +4,13 @@ use crate::{options, Deserialize, Result, SimpleType, StaticType}; /// /// This will recursively resolve all imports in the expression, and typecheck it before /// deserialization. Relative imports will be resolved relative to the current directory. -/// See [`options`][`options`] for more control over this process. +/// See [`options`] for more control over this process. /// -/// For additional type safety, prefer [`from_str_static_type`][`from_str_static_type`] or -/// [`from_str_manual_type`][`from_str_manual_type`]. +/// For additional type safety, prefer [`from_str_static_type`] or [`from_str_manual_type`]. /// +/// [`options`]: options/index.html +/// [`from_str_manual_type`]: fn.from_str_manual_type.html +/// [`from_str_static_type`]: fn.from_str_static_type.html /// /// # Example /// @@ -16,6 +18,7 @@ use crate::{options, Deserialize, Result, SimpleType, StaticType}; /// # fn main() -> serde_dhall::Result<()> { /// use serde::Deserialize; /// +/// // We use serde's derive feature /// #[derive(Debug, Deserialize)] /// struct Point { /// x: u64, @@ -25,18 +28,15 @@ use crate::{options, Deserialize, Result, SimpleType, StaticType}; /// // Some Dhall data /// let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }"; /// -/// // Convert the Dhall string to a Point. +/// // Parse the Dhall string as a Point. /// let point: Point = serde_dhall::from_str(data)?; +/// /// assert_eq!(point.x, 1); /// assert_eq!(point.y, 2); /// /// # Ok(()) /// # } /// ``` -/// -/// [`options`]: options/index.html -/// [`from_str_manual_type`]: fn.from_str_manual_type.html -/// [`from_str_static_type`]: fn.from_str_static_type.html pub fn from_str(s: &str) -> Result where T: Deserialize, @@ -47,8 +47,16 @@ where /// Deserialize an instance of type `T` from a string of Dhall text, /// additionally checking that it matches the supplied type. /// -/// Like [`from_str`], but this additionally checks that -/// the type of the provided expression matches the supplied type. +/// This will recursively resolve all imports in the expression, and typecheck it against the +/// supplied type before deserialization. Relative imports will be resolved relative to the current +/// directory. See [`options`] for more control over this process. +/// +/// See also [`from_str_static_type`]. +/// +/// [`options`]: options/index.html +/// [`from_str_static_type`]: fn.from_str_static_type.html +/// +/// # Example /// /// ```rust /// # fn main() -> serde_dhall::Result<()> { @@ -75,8 +83,6 @@ where /// # Ok(()) /// # } /// ``` -/// -/// TODO pub fn from_str_manual_type(s: &str, ty: &SimpleType) -> Result where T: Deserialize, @@ -87,9 +93,14 @@ where /// Deserialize an instance of type `T` from a string of Dhall text, /// additionally checking that it matches the type of `T`. /// -/// Like [from_str], but this additionally checks that -/// the type of the provided expression matches the output type `T`. The [StaticType] trait -/// captures Rust types that are valid Dhall types. +/// `T` must implement the [`StaticType`] trait. +/// +/// This will recursively resolve all imports in the expression, and typecheck it against the +/// type of `T`. Relative imports will be resolved relative to the current +/// directory. See [`options`] for more control over this process. +/// +/// [`options`]: options/index.html +/// [`StaticType`]: trait.StaticType.html /// /// TODO pub fn from_str_static_type(s: &str) -> Result diff --git a/serde_dhall/src/static_type.rs b/serde_dhall/src/static_type.rs index ffbc3ad..19f1202 100644 --- a/serde_dhall/src/static_type.rs +++ b/serde_dhall/src/static_type.rs @@ -2,16 +2,33 @@ use crate::SimpleType; /// A Rust type that can be represented as a Dhall type. /// -/// A typical example is `Option`, -/// represented by the dhall expression `Optional Bool`. +/// A typical example is `Option`, represented by the Dhall expression `Optional Bool`. /// -/// This trait can and should be automatically derived. +/// This trait can be automatically derived, and this is the recommended way of implementing it. /// -/// The representation needs to be independent of the value. -/// For this reason, something like `HashMap` cannot implement -/// [StaticType] because each different value would -/// have a different Dhall record type. -/// TODO +/// Some Rust types cannot implement this trait, because there isn't a single Dhall type that +/// corresponds to them. For example, `HashMap` could correspond to multiple different +/// Dhall types, e.g. `{ foo: Natural, bar: Natural }` and `{ baz: Natural }`. +/// +/// # Example +/// +/// ```rust +/// # fn main() -> serde_dhall::Result<()> { +/// use serde_dhall::{SimpleType, StaticType}; +/// +/// #[derive(StaticType)] +/// struct Foo { +/// x: bool, +/// y: Vec, +/// } +/// +/// let ty: SimpleType = +/// serde_dhall::from_str("{ x: Bool, y: List Natural }")?; +/// +/// assert_eq!(Foo::static_type(), ty); +/// # Ok(()) +/// # } +/// ``` pub trait StaticType { fn static_type() -> SimpleType; } diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs index f542f0a..bdc914f 100644 --- a/serde_dhall/src/value.rs +++ b/serde_dhall/src/value.rs @@ -36,11 +36,15 @@ pub(crate) enum SimpleValue { /// mismatch happened. /// /// You would typically not manipulate `SimpleType`s by hand but rather let Rust infer it for your -/// datatype using the [`StaticType`][TODO] trait, and methods that require it like -/// [`from_file_static_type`][TODO] and [`Options::static_type_annotation`][TODO]. If you need to supply a +/// datatype using the [`StaticType`] trait, and methods that require it like +/// [`from_file_static_type`] and [`Options::static_type_annotation`]. If you need to supply a /// `SimpleType` manually however, you can deserialize it like any other Dhall value using the /// functions provided by this crate. /// +/// [`StaticType`]: trait.StaticType.html +/// [`from_file_static_type`]: fn.from_file_static_type.html +/// [`Options::static_type_annotation`]: options/struct.Options.html#method.static_type_annotation +/// /// # Examples /// /// ```rust @@ -72,7 +76,7 @@ pub(crate) enum SimpleValue { /// let ty: SimpleType = /// serde_dhall::from_str("{ x: Bool, y: List Natural }")?; /// -/// assert_eq!(ty, Foo::static_type()); +/// assert_eq!(Foo::static_type(), ty); /// # Ok(()) /// # } /// ``` -- cgit v1.2.3 From 8e74abfc55f86dbd9142a9f38e070d2583454ddd Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Mar 2020 22:30:48 +0000 Subject: Go mad with doc macros --- serde_dhall/src/lib.rs | 5 +- serde_dhall/src/shortcuts.rs | 366 +++++++++++++++++++++++++++++++------------ 2 files changed, 266 insertions(+), 105 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 02b34a7..4cb25e5 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -189,6 +189,9 @@ pub use dhall_proc_macros::StaticType; pub use deserialize::Deserialize; pub(crate) use deserialize::Sealed; pub use error::{Error, Result}; -pub use shortcuts::{from_str, from_str_manual_type, from_str_static_type}; +pub use shortcuts::{ + from_file, from_file_manual_type, from_file_static_type, from_str, + from_str_manual_type, from_str_static_type, +}; pub use static_type::StaticType; pub use value::{SimpleType, Value}; diff --git a/serde_dhall/src/shortcuts.rs b/serde_dhall/src/shortcuts.rs index 54888c5..4aba9d1 100644 --- a/serde_dhall/src/shortcuts.rs +++ b/serde_dhall/src/shortcuts.rs @@ -1,111 +1,269 @@ +use doc_comment::doc_comment; +use std::path::Path; + use crate::{options, Deserialize, Result, SimpleType, StaticType}; -/// Deserialize an instance of type `T` from a string of Dhall text. -/// -/// This will recursively resolve all imports in the expression, and typecheck it before -/// deserialization. Relative imports will be resolved relative to the current directory. -/// See [`options`] for more control over this process. -/// -/// For additional type safety, prefer [`from_str_static_type`] or [`from_str_manual_type`]. -/// -/// [`options`]: options/index.html -/// [`from_str_manual_type`]: fn.from_str_manual_type.html -/// [`from_str_static_type`]: fn.from_str_static_type.html -/// -/// # Example -/// -/// ```rust -/// # fn main() -> serde_dhall::Result<()> { -/// use serde::Deserialize; -/// -/// // We use serde's derive feature -/// #[derive(Debug, Deserialize)] -/// struct Point { -/// x: u64, -/// y: u64, -/// } -/// -/// // Some Dhall data -/// let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }"; -/// -/// // Parse the Dhall string as a Point. -/// let point: Point = serde_dhall::from_str(data)?; -/// -/// assert_eq!(point.x, 1); -/// assert_eq!(point.y, 2); -/// -/// # Ok(()) -/// # } -/// ``` -pub fn from_str(s: &str) -> Result -where - T: Deserialize, -{ - options::from_str(s).parse() +// Avoid copy-pasting documentation + +#[rustfmt::skip] +macro_rules! gen_doc { + (@source_desc, str) => {"a string of Dhall text"}; + (@source_desc, file) => {"a Dhall file"}; + (@source_desc, url) => {"a remote url"}; + + (@tck_info1, none) => {""}; + (@tck_info1, manual) => {", additionally checking that it matches the supplied type"}; + (@tck_info1, static) => {", additionally checking that it matches the type of `T`"}; + + (@tck_info2, none) => {""}; + (@tck_info2, manual) => {" against the supplied type"}; + (@tck_info2, static) => {" against the type of `T`"}; + + (@tck_req, none) => {""}; + (@tck_req, manual) => {""}; + (@tck_req, static) => {"`T` must implement the [`StaticType`] trait.\n"}; + + (@tck_comment, $src:tt, none) => { + concat!("For additional type safety, prefer [`from_", stringify!($src), "_static_type`] + or [`from_", stringify!($src), "_manual_type`].\n") + }; + (@tck_comment, $src:tt, manual) => {concat!("See also [`from_", stringify!($src), "_static_type`].\n")}; + (@tck_comment, $src:tt, static) => {""}; + + (@run_example, str) => {""}; + (@run_example, file) => {"no_run"}; + (@run_example, url) => {"no_run"}; + + ($src:tt, $ty:tt) => {concat!(" +Deserialize an instance of type `T` from ", gen_doc!(@source_desc, $src), gen_doc!(@tck_info1, $ty),". + +", gen_doc!(@tck_req, $ty), " +This will recursively resolve all imports in the expression, and typecheck it", gen_doc!(@tck_info2, $ty)," +before deserialization. Relative imports will be resolved relative to the current directory. +See [`options`] for more control over this process. + +", gen_doc!(@tck_comment, $src, $ty), " + +# Example + +```", gen_doc!(@run_example, $src), " +# fn main() -> serde_dhall::Result<()> {", +gen_example!($src, $ty), " +# Ok(()) +# } +``` + +[`options`]: options/index.html +[`from_", stringify!($src), "_manual_type`]: fn.from_", stringify!($src), "_manual_type.html +[`from_", stringify!($src), "_static_type`]: fn.from_", stringify!($src), "_static_type.html +[`StaticType`]: trait.StaticType.html +")}; +} + +#[rustfmt::skip] +macro_rules! gen_example { + (str, none) => {concat!(r#" +use serde::Deserialize; + +// We use serde's derive feature +#[derive(Debug, Deserialize)] +struct Point { + x: u64, + y: u64, +} + +// Some Dhall data +let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }"; + +// Parse the Dhall string as a Point. +let point: Point = serde_dhall::from_str(data)?; + +assert_eq!(point.x, 1); +assert_eq!(point.y, 2); +"#)}; + + (str, manual) => {concat!(r#" +use std::collections::HashMap; +use serde_dhall::SimpleType; + +// Parse a Dhall type +let point_type_str = "{ x: Natural, y: Natural }"; +let point_type: SimpleType = serde_dhall::from_str(point_type_str)?; + +// Some Dhall data +let point_data = "{ x = 1, y = 1 + 1 }"; + +// Deserialize the data to a Rust type. This checks that +// the data matches the provided type. +let deserialized_map: HashMap = + serde_dhall::from_str_manual_type(point_data, &point_type)?; + +let mut expected_map = HashMap::new(); +expected_map.insert("x".to_string(), 1); +expected_map.insert("y".to_string(), 2); + +assert_eq!(deserialized_map, expected_map); +"#)}; + + (str, static) => {concat!(r#" +use serde::Deserialize; +use serde_dhall::StaticType; + +#[derive(Debug, Deserialize, StaticType)] +struct Point { + x: u64, + y: u64, } -/// Deserialize an instance of type `T` from a string of Dhall text, -/// additionally checking that it matches the supplied type. -/// -/// This will recursively resolve all imports in the expression, and typecheck it against the -/// supplied type before deserialization. Relative imports will be resolved relative to the current -/// directory. See [`options`] for more control over this process. -/// -/// See also [`from_str_static_type`]. -/// -/// [`options`]: options/index.html -/// [`from_str_static_type`]: fn.from_str_static_type.html -/// -/// # Example -/// -/// ```rust -/// # fn main() -> serde_dhall::Result<()> { -/// use serde_dhall::SimpleType; -/// use std::collections::HashMap; -/// -/// // Parse a Dhall type -/// let point_type_str = "{ x: Natural, y: Natural }"; -/// let point_type: SimpleType = serde_dhall::from_str(point_type_str)?; -/// -/// // Some Dhall data -/// let point_data = "{ x = 1, y = 1 + 1 }"; -/// -/// // Deserialize the data to a Rust type. This checks that -/// // the data matches the provided type. -/// let deserialized_map: HashMap = -/// serde_dhall::from_str_manual_type(point_data, &point_type)?; -/// -/// let mut expected_map = HashMap::new(); -/// expected_map.insert("x".to_string(), 1); -/// expected_map.insert("y".to_string(), 2); -/// -/// assert_eq!(deserialized_map, expected_map); -/// # Ok(()) -/// # } -/// ``` -pub fn from_str_manual_type(s: &str, ty: &SimpleType) -> Result -where - T: Deserialize, -{ - options::from_str(s).type_annotation(ty).parse() +// Some Dhall data +let data = "{ x = 1, y = 1 + 1 }"; + +// Convert the Dhall string to a Point. +let point: Point = serde_dhall::from_str_static_type(data)?; +assert_eq!(point.x, 1); +assert_eq!(point.y, 2); + +// Invalid data fails the type validation +let invalid_data = "{ x = 1, z = 0.3 }"; +assert!(serde_dhall::from_str_static_type::(invalid_data).is_err()); +"#)}; + + (file, none) => {concat!(r#" +use serde::Deserialize; + +// We use serde's derive feature +#[derive(Debug, Deserialize)] +struct Point { + x: u64, + y: u64, } -/// Deserialize an instance of type `T` from a string of Dhall text, -/// additionally checking that it matches the type of `T`. -/// -/// `T` must implement the [`StaticType`] trait. -/// -/// This will recursively resolve all imports in the expression, and typecheck it against the -/// type of `T`. Relative imports will be resolved relative to the current -/// directory. See [`options`] for more control over this process. -/// -/// [`options`]: options/index.html -/// [`StaticType`]: trait.StaticType.html -/// -/// TODO -pub fn from_str_static_type(s: &str) -> Result -where - T: Deserialize + StaticType, -{ - options::from_str(s).static_type_annotation().parse() +// Parse the Dhall file as a Point. +let point: Point = serde_dhall::from_file("foo.dhall")?; +"#)}; + + (file, manual) => {concat!(r#" +use std::collections::HashMap; +use serde_dhall::SimpleType; + +// Parse a Dhall type +let point_type_str = "{ x: Natural, y: Natural }"; +let point_type: SimpleType = serde_dhall::from_str(point_type_str)?; + +// Deserialize the data to a Rust type. This checks that +// the data matches the provided type. +let deserialized_map: HashMap = + serde_dhall::from_file_manual_type("foo.dhall", &point_type)?; +"#)}; + + (file, static) => {concat!(r#" +use serde::Deserialize; +use serde_dhall::StaticType; + +#[derive(Debug, Deserialize, StaticType)] +struct Point { + x: u64, + y: u64, +} + +// Convert the Dhall string to a Point. +let point: Point = serde_dhall::from_file_static_type("foo.dhall")?; +"#)}; + + ($src:tt, $ty:tt) => {""}; } + +macro_rules! generate_fn { + (@generate_src, + str, $ty:tt, $name:ident, + ) => { + generate_fn!(@generate_ty, + str, $ty, $name, + (), + (s: &str), + (options::from_str(s)), + ); + }; + (@generate_src, + file, $ty:tt, $name:ident, + ) => { + generate_fn!(@generate_ty, + file, $ty, $name, + (P: AsRef), + (path: P), + (options::from_file(path)), + ); + }; + + (@generate_ty, + $src:tt, none, $name:ident, + ($($ty_params:tt)*), + ($($input_args:tt)*), + ($($create_options:tt)*), + ) => { + generate_fn!(@generate, + $src, none, $name, + ($($ty_params)*), + ($($input_args)*), + (), + ($($create_options)*), + ); + }; + (@generate_ty, + $src:tt, manual, $name:ident, + ($($ty_params:tt)*), + ($($input_args:tt)*), + ($($create_options:tt)*), + ) => { + generate_fn!(@generate, + $src, manual, $name, + ($($ty_params)*), + ($($input_args)*, ty: &SimpleType), + (), + ($($create_options)* .type_annotation(ty)), + ); + }; + (@generate_ty, + $src:tt, static, $name:ident, + ($($ty_params:tt)*), + ($($input_args:tt)*), + ($($create_options:tt)*), + ) => { + generate_fn!(@generate, + $src, static, $name, + ($($ty_params)*), + ($($input_args)*), + (+ StaticType), + ($($create_options)* .static_type_annotation()), + ); + }; + + (@generate, + $src:tt, $ty:tt, $name:ident, + ($($ty_params:tt)*), + ($($input_args:tt)*), + ($($extra_bounds:tt)*), + ($($create_options:tt)*), + ) => { + doc_comment! { + gen_doc!($src, $ty), + pub fn $name ($($input_args)*) -> Result + where + T: Deserialize $($extra_bounds)*, + { + $($create_options)* .parse() + } + } + }; + + ($src:tt, $ty:tt, $name:ident) => { + generate_fn!(@generate_src, $src, $ty, $name,); + }; +} + +generate_fn!(str, none, from_str); +generate_fn!(str, manual, from_str_manual_type); +generate_fn!(str, static, from_str_static_type); +generate_fn!(file, none, from_file); +generate_fn!(file, manual, from_file_manual_type); +generate_fn!(file, static, from_file_static_type); -- cgit v1.2.3 From f9848b54fe2e64901042fba66fb471999f415ff1 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 24 Mar 2020 18:53:25 +0000 Subject: Hide serde Error internals --- serde_dhall/src/deserialize.rs | 6 +++--- serde_dhall/src/error.rs | 24 ++++++++++++++++-------- serde_dhall/src/lib.rs | 1 + serde_dhall/src/options.rs | 4 ++-- serde_dhall/src/value.rs | 10 +++++----- 5 files changed, 27 insertions(+), 18 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/deserialize.rs b/serde_dhall/src/deserialize.rs index ab5580e..7bb0051 100644 --- a/serde_dhall/src/deserialize.rs +++ b/serde_dhall/src/deserialize.rs @@ -6,7 +6,7 @@ use std::borrow::Cow; use dhall::syntax::NumKind; use crate::value::SimpleValue; -use crate::{Error, Result, Value}; +use crate::{Error, ErrorKind, Result, Value}; pub trait Sealed {} @@ -33,10 +33,10 @@ where { fn from_dhall(v: &Value) -> Result { let sval = v.to_simple_value().ok_or_else(|| { - Error::Deserialize(format!( + Error(ErrorKind::Deserialize(format!( "this cannot be deserialized into the serde data model: {}", v - )) + ))) })?; T::deserialize(Deserializer(Cow::Owned(sval))) } diff --git a/serde_dhall/src/error.rs b/serde_dhall/src/error.rs index 23d1b02..896e8b9 100644 --- a/serde_dhall/src/error.rs +++ b/serde_dhall/src/error.rs @@ -1,21 +1,29 @@ use dhall::error::Error as DhallError; -/// TODO +/// Alias for a `Result` with the error type `serde_dhall::Error`. pub type Result = std::result::Result; +/// Errors that can occur when deserializing Dhall data. #[derive(Debug)] -#[non_exhaustive] -/// TODO -pub enum Error { +pub struct Error(pub(crate) ErrorKind); + +#[derive(Debug)] +pub(crate) enum ErrorKind { Dhall(DhallError), Deserialize(String), } +impl From for Error { + fn from(kind: ErrorKind) -> Error { + Error(kind) + } +} + impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Error::Dhall(err) => write!(f, "{}", err), - Error::Deserialize(err) => write!(f, "{}", err), + match &self.0 { + ErrorKind::Dhall(err) => write!(f, "{}", err), + ErrorKind::Deserialize(err) => write!(f, "{}", err), } } } @@ -27,6 +35,6 @@ impl serde::de::Error for Error { where T: std::fmt::Display, { - Error::Deserialize(msg.to_string()) + ErrorKind::Deserialize(msg.to_string()).into() } } diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 4cb25e5..fe1f4b2 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -188,6 +188,7 @@ pub use dhall_proc_macros::StaticType; pub use deserialize::Deserialize; pub(crate) use deserialize::Sealed; +pub(crate) use error::ErrorKind; pub use error::{Error, Result}; pub use shortcuts::{ from_file, from_file_manual_type, from_file_static_type, from_str, diff --git a/serde_dhall/src/options.rs b/serde_dhall/src/options.rs index f468cf0..f260572 100644 --- a/serde_dhall/src/options.rs +++ b/serde_dhall/src/options.rs @@ -3,7 +3,7 @@ use std::path::{Path, PathBuf}; use dhall::Parsed; use crate::SimpleType; -use crate::{Deserialize, Error, Result, StaticType, Value}; +use crate::{Deserialize, Error, ErrorKind, Result, StaticType, Value}; #[derive(Debug, Clone)] enum Source<'a> { @@ -130,7 +130,7 @@ impl<'a, T> Options<'a, T> { where T: Deserialize, { - let val = self._parse().map_err(Error::Dhall)?; + let val = self._parse().map_err(ErrorKind::Dhall).map_err(Error)?; T::from_dhall(&val) } } diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs index bdc914f..3543f4d 100644 --- a/serde_dhall/src/value.rs +++ b/serde_dhall/src/value.rs @@ -3,7 +3,7 @@ use std::collections::{BTreeMap, HashMap}; use dhall::semantics::{Hir, HirKind, Nir, NirKind}; use dhall::syntax::{Builtin, Expr, ExprKind, NumKind, Span}; -use crate::{Deserialize, Error, Result, Sealed}; +use crate::{Deserialize, Error, ErrorKind, Result, Sealed}; #[doc(hidden)] /// An arbitrary Dhall value. @@ -254,20 +254,20 @@ impl Deserialize for Value { impl Deserialize for SimpleValue { fn from_dhall(v: &Value) -> Result { v.to_simple_value().ok_or_else(|| { - Error::Deserialize(format!( + Error(ErrorKind::Deserialize(format!( "this cannot be deserialized into a simple type: {}", v - )) + ))) }) } } impl Deserialize for SimpleType { fn from_dhall(v: &Value) -> Result { v.to_simple_type().ok_or_else(|| { - Error::Deserialize(format!( + Error(ErrorKind::Deserialize(format!( "this cannot be deserialized into a simple type: {}", v - )) + ))) }) } } -- cgit v1.2.3 From 060c835db9638556763b98cfcf7c4be196653644 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 24 Mar 2020 19:42:48 +0000 Subject: More doc --- serde_dhall/src/deserialize.rs | 34 ++++++++++---- serde_dhall/src/lib.rs | 102 ++++++++++++++++++----------------------- serde_dhall/src/shortcuts.rs | 19 ++++---- serde_dhall/src/static_type.rs | 47 +++++++++++++++++++ 4 files changed, 128 insertions(+), 74 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/deserialize.rs b/serde_dhall/src/deserialize.rs index 7bb0051..b9b711c 100644 --- a/serde_dhall/src/deserialize.rs +++ b/serde_dhall/src/deserialize.rs @@ -10,26 +10,44 @@ use crate::{Error, ErrorKind, Result, Value}; pub trait Sealed {} -/// A data structure that can be deserialized from a Dhall expression +/// A data structure that can be deserialized from a Dhall expression. /// -/// This is automatically implemented for any type that [serde][serde] -/// can deserialize. +/// This is automatically implemented for any type that [serde] can deserialize. +/// In fact, this trait cannot be implemented manually. To implement it for your type, +/// use serde's derive mechanism. /// -/// This trait cannot be implemented manually. +/// # Example /// -/// TODO +/// ```rust +/// # fn main() -> serde_dhall::Result<()> { +/// use serde::Deserialize; +/// +/// // Use serde's derive +/// #[derive(Deserialize)] +/// struct Point { +/// x: u64, +/// y: u64, +/// } +/// +/// // Convert a Dhall string to a Point. +/// let point: Point = serde_dhall::from_str("{ x = 1, y = 1 + 1 }")?; +/// # Ok(()) +/// # } +/// ``` +/// +/// [serde]: https://serde.rs pub trait Deserialize: Sealed + Sized { #[doc(hidden)] fn from_dhall(v: &Value) -> Result; } -impl<'a, T> Sealed for T where T: serde::Deserialize<'a> {} +impl Sealed for T where T: serde::de::DeserializeOwned {} struct Deserializer<'a>(Cow<'a, SimpleValue>); -impl<'a, T> Deserialize for T +impl Deserialize for T where - T: serde::Deserialize<'a>, + T: serde::de::DeserializeOwned, { fn from_dhall(v: &Value) -> Result { let sval = v.to_simple_value().ok_or_else(|| { diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index fe1f4b2..71274b6 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -1,5 +1,5 @@ #![doc(html_root_url = "https://docs.rs/serde_dhall/0.4.0")] -// TODO #![warn(missing_docs)] #![warn(missing_doc_code_examples)] +#![warn(missing_docs, missing_doc_code_examples)] //! [Dhall][dhall] is a programmable configuration language that provides a non-repetitive //! alternative to JSON and YAML. //! @@ -47,7 +47,7 @@ //! # fn main() -> serde_dhall::Result<()> { //! use serde::Deserialize; //! -//! #[derive(Debug, Deserialize)] +//! #[derive(Deserialize)] //! struct Point { //! x: u64, //! y: u64, @@ -65,28 +65,6 @@ //! # } //! ``` //! -//! # Type correspondence -//! -//! The following Dhall types correspond to the following Rust types: -//! -//! Dhall | Rust -//! -------|------ -//! `Bool` | `bool` -//! `Natural` | `u64`, `u32`, ... -//! `Integer` | `i64`, `i32`, ... -//! `Double` | `f64`, `f32`, ... -//! `Text` | `String` -//! `List T` | `Vec` -//! `Optional T` | `Option` -//! `{ x: T, y: U }` | structs -//! `{ _1: T, _2: U }` | `(T, U)`, structs -//! `{ x: T, y: T }` | `HashMap`, structs -//! `< x: T \| y: U >` | enums -//! `T -> U` | unsupported -//! `Prelude.JSON.Type` | unsupported -//! `Prelude.Map.Type T U` | unsupported -//! -//! //! # Replacing `serde_json` or `serde_yaml` //! //! If you used to consume JSON or YAML, you only need to replace [`serde_json::from_str`] or @@ -99,16 +77,45 @@ //! # Additional Dhall typechecking //! //! When deserializing, normal type checking is done to ensure that the returned value is a valid -//! Dhall value, and that it can be deserialized into the required Rust type. However types are -//! first-class in Dhall, and this library allows you to additionally check that some input data +//! Dhall value. However types are +//! first-class in Dhall, and this library allows you to additionally check that the input data //! matches a given Dhall type. That way, a type error will be caught on the Dhall side, and have //! pretty and explicit errors that point to the source file. //! -//! There are two ways to typecheck a Dhall value: you can provide the type as Dhall text or you -//! can let Rust infer it for you. +//! There are two ways to typecheck a Dhall value in this way: you can provide the type manually or +//! you can let Rust infer it for you. +//! +//! To let Rust infer the appropriate Dhall type, use the [StaticType](trait.StaticType.html) +//! trait. +//! +//! ```rust +//! # fn main() -> serde_dhall::Result<()> { +//! use serde::Deserialize; +//! use serde_dhall::StaticType; +//! +//! #[derive(Deserialize, StaticType)] +//! struct Point { +//! x: u64, +//! y: u64, +//! } +//! +//! // Some Dhall data +//! let data = "{ x = 1, y = 1 + 1 }"; +//! +//! // Convert the Dhall string to a Point. +//! let point: Point = serde_dhall::from_str_static_type(data)?; +//! assert_eq!(point.x, 1); +//! assert_eq!(point.y, 2); +//! +//! // Invalid data fails the type validation +//! let invalid_data = "{ x = 1, z = 0.3 }"; +//! assert!(serde_dhall::from_str_static_type::(invalid_data).is_err()); +//! # Ok(()) +//! # } +//! ``` //! -//! To provide a type written in Dhall, first parse it into a [`SimpleType`](enum.SimpleType.html), then -//! pass it to [`from_str_manual_type`](fn.from_str_manual_type.html). +//! To provide a type manually, you need a [`SimpleType`](enum.SimpleType.html) value. You +//! can parse it from some Dhall text like you would parse any other value. //! //! ```rust //! # fn main() -> serde_dhall::Result<()> { @@ -136,41 +143,20 @@ //! # } //! ``` //! -//! You can also let Rust infer the appropriate Dhall type, using the -//! [StaticType](trait.StaticType.html) trait. -//! -//! ```rust -//! # fn main() -> serde_dhall::Result<()> { -//! use serde::Deserialize; -//! use serde_dhall::StaticType; -//! -//! #[derive(Debug, Deserialize, StaticType)] -//! struct Point { -//! x: u64, -//! y: u64, -//! } +//! # Controlling deserialization //! -//! // Some Dhall data -//! let data = "{ x = 1, y = 1 + 1 }"; -//! -//! // Convert the Dhall string to a Point. -//! let point: Point = serde_dhall::from_str_static_type(data)?; -//! assert_eq!(point.x, 1); -//! assert_eq!(point.y, 2); -//! -//! // Invalid data fails the type validation -//! let invalid_data = "{ x = 1, z = 0.3 }"; -//! assert!(serde_dhall::from_str_static_type::(invalid_data).is_err()); -//! # Ok(()) -//! # } -//! ``` +//! If you need more control over the process of reading Dhall values, e.g. disabling +//! imports, see the [`options`] module. //! +//! [`options`]: options/index.html //! [dhall]: https://dhall-lang.org/ //! [serde]: https://docs.serde.rs/serde/ //! [serde::Deserialize]: https://docs.serde.rs/serde/trait.Deserialize.html #[cfg(doctest)] -doc_comment::doctest!("../../README.md"); +mod test_readme { + doc_comment::doctest!("../../README.md"); +} /// Finer-grained control over deserializing Dhall values pub mod options; diff --git a/serde_dhall/src/shortcuts.rs b/serde_dhall/src/shortcuts.rs index 4aba9d1..cd31402 100644 --- a/serde_dhall/src/shortcuts.rs +++ b/serde_dhall/src/shortcuts.rs @@ -19,9 +19,12 @@ macro_rules! gen_doc { (@tck_info2, manual) => {" against the supplied type"}; (@tck_info2, static) => {" against the type of `T`"}; - (@tck_req, none) => {""}; - (@tck_req, manual) => {""}; - (@tck_req, static) => {"`T` must implement the [`StaticType`] trait.\n"}; + (@tck_req, $src:tt, none) => {""}; + (@tck_req, $src:tt, manual) => {""}; + (@tck_req, $src:tt, static) => { + concat!("`T` must implement the [`StaticType`] trait. Use [`from_", stringify!($src), + "_manual_type`] to provide a type manually.\n") + }; (@tck_comment, $src:tt, none) => { concat!("For additional type safety, prefer [`from_", stringify!($src), "_static_type`] @@ -37,7 +40,7 @@ macro_rules! gen_doc { ($src:tt, $ty:tt) => {concat!(" Deserialize an instance of type `T` from ", gen_doc!(@source_desc, $src), gen_doc!(@tck_info1, $ty),". -", gen_doc!(@tck_req, $ty), " +", gen_doc!(@tck_req, $src, $ty), " This will recursively resolve all imports in the expression, and typecheck it", gen_doc!(@tck_info2, $ty)," before deserialization. Relative imports will be resolved relative to the current directory. See [`options`] for more control over this process. @@ -66,7 +69,7 @@ macro_rules! gen_example { use serde::Deserialize; // We use serde's derive feature -#[derive(Debug, Deserialize)] +#[derive(Deserialize)] struct Point { x: u64, y: u64, @@ -109,7 +112,7 @@ assert_eq!(deserialized_map, expected_map); use serde::Deserialize; use serde_dhall::StaticType; -#[derive(Debug, Deserialize, StaticType)] +#[derive(Deserialize, StaticType)] struct Point { x: u64, y: u64, @@ -132,7 +135,7 @@ assert!(serde_dhall::from_str_static_type::(invalid_data).is_err()); use serde::Deserialize; // We use serde's derive feature -#[derive(Debug, Deserialize)] +#[derive(Deserialize)] struct Point { x: u64, y: u64, @@ -160,7 +163,7 @@ let deserialized_map: HashMap = use serde::Deserialize; use serde_dhall::StaticType; -#[derive(Debug, Deserialize, StaticType)] +#[derive(Deserialize, StaticType)] struct Point { x: u64, y: u64, diff --git a/serde_dhall/src/static_type.rs b/serde_dhall/src/static_type.rs index 19f1202..020dfce 100644 --- a/serde_dhall/src/static_type.rs +++ b/serde_dhall/src/static_type.rs @@ -29,7 +29,54 @@ use crate::SimpleType; /// # Ok(()) /// # } /// ``` +/// +/// # Type correspondence +/// +/// The following Dhall types correspond to the following Rust types: +/// +/// Dhall | Rust +/// -------|------ +/// `Bool` | `bool` +/// `Natural` | `u64`, `u32`, ... +/// `Integer` | `i64`, `i32`, ... +/// `Double` | `f64`, `f32`, ... +/// `Text` | `String` +/// `List T` | `Vec` +/// `Optional T` | `Option` +/// `{ x: T, y: U }` | structs +/// `{ _1: T, _2: U }` | `(T, U)`, structs +/// `{ x: T, y: T }` | `HashMap`, structs +/// `< x: T \| y: U >` | enums +/// `T -> U` | unsupported +/// `Prelude.JSON.Type` | unsupported +/// `Prelude.Map.Type T U` | unsupported pub trait StaticType { + /// Return the Dhall type that represents this type. + /// + /// # Example + /// + /// ```rust + /// # fn main() -> serde_dhall::Result<()> { + /// use serde::Deserialize; + /// use serde_dhall::{SimpleType, StaticType}; + /// + /// // Using `derive(StaticType)` here would give it the type `{ _1: List Natural }`. + /// #[derive(Deserialize)] + /// #[serde(transparent)] + /// struct Foo(Vec); + /// + /// impl StaticType for Foo { + /// fn static_type() -> SimpleType { + /// SimpleType::List(Box::new(SimpleType::Natural)) + /// } + /// } + /// + /// let foo: Foo = serde_dhall::from_str_static_type("[ 1, 2 ]")?; + /// + /// assert_eq!(foo.0, vec![1, 2]); + /// # Ok(()) + /// # } + /// ``` fn static_type() -> SimpleType; } -- cgit v1.2.3 From 158117e4096f5af571e79e1a33d91f09e7b2cc5d Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 5 Apr 2020 11:39:58 +0100 Subject: Rename Options to Deserializer --- serde_dhall/src/options.rs | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/options.rs b/serde_dhall/src/options.rs index f260572..237647f 100644 --- a/serde_dhall/src/options.rs +++ b/serde_dhall/src/options.rs @@ -12,13 +12,13 @@ enum Source<'a> { // Url(&'a str), } -/// Options and flags which can be used to configure how a dhall value is read. +/// Controls how a dhall value is read. /// /// This builder exposes the ability to configure how a value is deserialized and what operations /// are permitted during evaluation. The functions in the crate root are aliases for /// commonly used options using this builder. /// -/// Generally speaking, when using `Options`, you'll create it with `from_str` or `from_file`, then +/// Generally speaking, when using `Deserializer`, you'll create it with `from_str` or `from_file`, then /// chain calls to methods to set each option, then call `parse`. This will give you a /// `serde_dhall::Result` where `T` a deserializable type of your choice. /// @@ -48,9 +48,9 @@ enum Source<'a> { /// # Ok(()) /// # } /// ``` -/// /// TODO +/// TODO #[derive(Debug, Clone)] -pub struct Options<'a, T> { +pub struct Deserializer<'a, T> { source: Source<'a>, annot: Option, allow_imports: bool, @@ -59,9 +59,9 @@ pub struct Options<'a, T> { target_type: std::marker::PhantomData, } -impl<'a, T> Options<'a, T> { +impl<'a, T> Deserializer<'a, T> { fn default_with_source(source: Source<'a>) -> Self { - Options { + Deserializer { source, annot: None, allow_imports: true, @@ -135,14 +135,22 @@ impl<'a, T> Options<'a, T> { } } -/// TODO -pub fn from_str<'a, T>(s: &'a str) -> Options<'a, T> { - Options::from_str(s) +/// Deserialize an instance of type `T` from a string of Dhall text. +/// +/// This returns a [`Deserializer`] object. Call the [`parse`] method to get the deserialized value, or +/// use other [`Deserializer`] methods to add type annotations or control import behavior beforehand. +/// +/// [`Deserializer`]: struct.Deserializer.html +/// [`parse`]: struct.Deserializer.html#method.parse +pub fn from_str<'a, T>(s: &'a str) -> Deserializer<'a, T> { + Deserializer::from_str(s) } + /// TODO -pub fn from_file<'a, T, P: AsRef>(path: P) -> Options<'a, T> { - Options::from_file(path) +pub fn from_file<'a, T, P: AsRef>(path: P) -> Deserializer<'a, T> { + Deserializer::from_file(path) } -// pub fn from_url<'a, T>(url: &'a str) -> Options<'a, T> { -// Options::from_url(url) + +// pub fn from_url<'a, T>(url: &'a str) -> Deserializer<'a, T> { +// Deserializer::from_url(url) // } -- cgit v1.2.3 From 15a0902b50ff6d1b36dbf7a220b6ff42ceefada6 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 5 Apr 2020 11:43:22 +0100 Subject: Rename Deserialize trait to FromDhall --- serde_dhall/src/deserialize.rs | 4 ++-- serde_dhall/src/lib.rs | 2 +- serde_dhall/src/options.rs | 4 ++-- serde_dhall/src/shortcuts.rs | 4 ++-- serde_dhall/src/value.rs | 8 ++++---- 5 files changed, 11 insertions(+), 11 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/deserialize.rs b/serde_dhall/src/deserialize.rs index b9b711c..1be68c4 100644 --- a/serde_dhall/src/deserialize.rs +++ b/serde_dhall/src/deserialize.rs @@ -36,7 +36,7 @@ pub trait Sealed {} /// ``` /// /// [serde]: https://serde.rs -pub trait Deserialize: Sealed + Sized { +pub trait FromDhall: Sealed + Sized { #[doc(hidden)] fn from_dhall(v: &Value) -> Result; } @@ -45,7 +45,7 @@ impl Sealed for T where T: serde::de::DeserializeOwned {} struct Deserializer<'a>(Cow<'a, SimpleValue>); -impl Deserialize for T +impl FromDhall for T where T: serde::de::DeserializeOwned, { diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 71274b6..9f99adb 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -172,7 +172,7 @@ mod value; #[doc(hidden)] pub use dhall_proc_macros::StaticType; -pub use deserialize::Deserialize; +pub use deserialize::FromDhall; pub(crate) use deserialize::Sealed; pub(crate) use error::ErrorKind; pub use error::{Error, Result}; diff --git a/serde_dhall/src/options.rs b/serde_dhall/src/options.rs index 237647f..d7a91a2 100644 --- a/serde_dhall/src/options.rs +++ b/serde_dhall/src/options.rs @@ -3,7 +3,7 @@ use std::path::{Path, PathBuf}; use dhall::Parsed; use crate::SimpleType; -use crate::{Deserialize, Error, ErrorKind, Result, StaticType, Value}; +use crate::{FromDhall, Error, ErrorKind, Result, StaticType, Value}; #[derive(Debug, Clone)] enum Source<'a> { @@ -128,7 +128,7 @@ impl<'a, T> Deserializer<'a, T> { /// TODO pub fn parse(&self) -> Result where - T: Deserialize, + T: FromDhall, { let val = self._parse().map_err(ErrorKind::Dhall).map_err(Error)?; T::from_dhall(&val) diff --git a/serde_dhall/src/shortcuts.rs b/serde_dhall/src/shortcuts.rs index cd31402..9c9ce9f 100644 --- a/serde_dhall/src/shortcuts.rs +++ b/serde_dhall/src/shortcuts.rs @@ -1,7 +1,7 @@ use doc_comment::doc_comment; use std::path::Path; -use crate::{options, Deserialize, Result, SimpleType, StaticType}; +use crate::{options, FromDhall, Result, SimpleType, StaticType}; // Avoid copy-pasting documentation @@ -252,7 +252,7 @@ macro_rules! generate_fn { gen_doc!($src, $ty), pub fn $name ($($input_args)*) -> Result where - T: Deserialize $($extra_bounds)*, + T: FromDhall $($extra_bounds)*, { $($create_options)* .parse() } diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs index 3543f4d..fee9d73 100644 --- a/serde_dhall/src/value.rs +++ b/serde_dhall/src/value.rs @@ -3,7 +3,7 @@ use std::collections::{BTreeMap, HashMap}; use dhall::semantics::{Hir, HirKind, Nir, NirKind}; use dhall::syntax::{Builtin, Expr, ExprKind, NumKind, Span}; -use crate::{Deserialize, Error, ErrorKind, Result, Sealed}; +use crate::{FromDhall, Error, ErrorKind, Result, Sealed}; #[doc(hidden)] /// An arbitrary Dhall value. @@ -246,12 +246,12 @@ impl Sealed for Value {} impl Sealed for SimpleValue {} impl Sealed for SimpleType {} -impl Deserialize for Value { +impl FromDhall for Value { fn from_dhall(v: &Value) -> Result { Ok(v.clone()) } } -impl Deserialize for SimpleValue { +impl FromDhall for SimpleValue { fn from_dhall(v: &Value) -> Result { v.to_simple_value().ok_or_else(|| { Error(ErrorKind::Deserialize(format!( @@ -261,7 +261,7 @@ impl Deserialize for SimpleValue { }) } } -impl Deserialize for SimpleType { +impl FromDhall for SimpleType { fn from_dhall(v: &Value) -> Result { v.to_simple_type().ok_or_else(|| { Error(ErrorKind::Deserialize(format!( -- cgit v1.2.3 From 6dab8cb06e52efdb18b9dcf975e0a2d50454d704 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 5 Apr 2020 15:42:45 +0100 Subject: Document Deserializer methods --- serde_dhall/src/options.rs | 213 +++++++++++++++++++++++++++++++++++++++++---- serde_dhall/src/value.rs | 2 +- 2 files changed, 198 insertions(+), 17 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/options.rs b/serde_dhall/src/options.rs index d7a91a2..f3797ee 100644 --- a/serde_dhall/src/options.rs +++ b/serde_dhall/src/options.rs @@ -3,7 +3,7 @@ use std::path::{Path, PathBuf}; use dhall::Parsed; use crate::SimpleType; -use crate::{FromDhall, Error, ErrorKind, Result, StaticType, Value}; +use crate::{Error, ErrorKind, FromDhall, Result, StaticType, Value}; #[derive(Debug, Clone)] enum Source<'a> { @@ -15,12 +15,11 @@ enum Source<'a> { /// Controls how a dhall value is read. /// /// This builder exposes the ability to configure how a value is deserialized and what operations -/// are permitted during evaluation. The functions in the crate root are aliases for -/// commonly used options using this builder. +/// are permitted during evaluation. /// /// Generally speaking, when using `Deserializer`, you'll create it with `from_str` or `from_file`, then /// chain calls to methods to set each option, then call `parse`. This will give you a -/// `serde_dhall::Result` where `T` a deserializable type of your choice. +/// `serde_dhall::Result` where `T` is a deserializable type of your choice. /// /// # Examples /// @@ -48,8 +47,7 @@ enum Source<'a> { /// # Ok(()) /// # } /// ``` -/// TODO -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Deserializer<'a, T> { source: Source<'a>, annot: Option, @@ -70,11 +68,9 @@ impl<'a, T> Deserializer<'a, T> { target_type: std::marker::PhantomData, } } - /// TODO fn from_str(s: &'a str) -> Self { Self::default_with_source(Source::Str(s)) } - /// TODO fn from_file>(path: P) -> Self { Self::default_with_source(Source::File(path.as_ref().to_owned())) } @@ -82,11 +78,36 @@ impl<'a, T> Deserializer<'a, T> { // Self::default_with_source(Source::Url(url)) // } - /// TODO + /// Sets whether to enable imports. + /// + /// By default, imports are enabled. + /// + /// # Examples + /// + /// ``` + /// # fn main() -> serde_dhall::Result<()> { + /// use serde::Deserialize; + /// use serde_dhall::SimpleType; + /// + /// let data = "12 + ./other_file.dhall : Natural"; + /// assert!( + /// serde_dhall::options::from_str::(data) + /// .imports(false) + /// .parse() + /// .is_err() + /// ); + /// # Ok(()) + /// # } + /// ``` + /// + /// [`static_type_annotation`]: struct.Deserializer.html#method.static_type_annotation + /// [`StaticType`]: trait.StaticType.html pub fn imports(&mut self, imports: bool) -> &mut Self { self.allow_imports = imports; self } + + // /// TODO // pub fn remote_imports(&mut self, imports: bool) -> &mut Self { // self.allow_remote_imports = imports; // if imports { @@ -94,13 +115,98 @@ impl<'a, T> Deserializer<'a, T> { // } // self // } - // - /// TODO + + /// Ensures that the parsed value matches the provided type. + /// + /// In many cases the Dhall type that corresponds to a Rust type can be inferred automatically. + /// See the [`StaticType`] trait and the [`static_type_annotation`] method. + /// + /// # Examples + /// + /// ``` + /// # fn main() -> serde_dhall::Result<()> { + /// use serde::Deserialize; + /// use serde_dhall::SimpleType; + /// + /// #[derive(Deserialize)] + /// struct Point { + /// x: u64, + /// y: Option, + /// } + /// + /// // Parse a Dhall type + /// let point_type_str = "{ x: Natural, y: Optional Natural }"; + /// let point_type: SimpleType = serde_dhall::options::from_str(point_type_str).parse()?; + /// + /// // Parse some Dhall data to a Point. + /// let data = "{ x = 1, y = Some (1 + 1) }"; + /// let point: Point = serde_dhall::options::from_str(data) + /// .type_annotation(&point_type) + /// .parse()?; + /// assert_eq!(point.x, 1); + /// assert_eq!(point.y, Some(2)); + /// + /// // Invalid data fails the type validation; deserialization would have succeeded otherwise. + /// let invalid_data = "{ x = 1 }"; + /// assert!( + /// serde_dhall::options::from_str::(invalid_data) + /// .type_annotation(&point_type) + /// .parse() + /// .is_err() + /// ); + /// # Ok(()) + /// # } + /// ``` + /// + /// [`static_type_annotation`]: struct.Deserializer.html#method.static_type_annotation + /// [`StaticType`]: trait.StaticType.html pub fn type_annotation(&mut self, ty: &SimpleType) -> &mut Self { self.annot = Some(ty.clone()); self } - /// TODO + + /// Ensures that the parsed value matches the type of `T`. + /// + /// `T` must implement the [`StaticType`] trait. If it doesn't, you can use [`type_annotation`] + /// to provide a type manually. + /// + /// # Examples + /// + /// ``` + /// # fn main() -> serde_dhall::Result<()> { + /// use serde::Deserialize; + /// use serde_dhall::StaticType; + /// + /// #[derive(Deserialize, StaticType)] + /// struct Point { + /// x: u64, + /// y: Option, + /// } + /// + /// // Some Dhall data + /// let data = "{ x = 1, y = Some (1 + 1) }"; + /// + /// // Convert the Dhall string to a Point. + /// let point: Point = serde_dhall::options::from_str(data) + /// .static_type_annotation() + /// .parse()?; + /// assert_eq!(point.x, 1); + /// assert_eq!(point.y, Some(2)); + /// + /// // Invalid data fails the type validation; deserialization would have succeeded otherwise. + /// let invalid_data = "{ x = 1 }"; + /// assert!( + /// serde_dhall::options::from_str::(invalid_data) + /// .static_type_annotation() + /// .parse() + /// .is_err() + /// ); + /// # Ok(()) + /// # } + /// ``` + /// + /// [`type_annotation`]: struct.Deserializer.html#method.type_annotation + /// [`StaticType`]: trait.StaticType.html pub fn static_type_annotation(&mut self) -> &mut Self where T: StaticType, @@ -125,7 +231,20 @@ impl<'a, T> Deserializer<'a, T> { }; Ok(Value::from_nir(typed.normalize().as_nir())) } - /// TODO + + /// Parses the chosen dhall value with the options provided. + /// + /// # Examples + /// + /// Reading from a file: + /// + /// ```no_run + /// # fn main() -> serde_dhall::Result<()> { + /// use serde_dhall::options; + /// let data = options::from_file("foo.dhall").parse()?; + /// # Ok(()) + /// # } + /// ``` pub fn parse(&self) -> Result where T: FromDhall, @@ -138,15 +257,65 @@ impl<'a, T> Deserializer<'a, T> { /// Deserialize an instance of type `T` from a string of Dhall text. /// /// This returns a [`Deserializer`] object. Call the [`parse`] method to get the deserialized value, or -/// use other [`Deserializer`] methods to add type annotations or control import behavior beforehand. +/// use other [`Deserializer`] methods to e.g. add type annotations beforehand. +/// +/// # Example +/// +/// ```rust +/// # fn main() -> serde_dhall::Result<()> { +/// use serde::Deserialize; +/// +/// // We use serde's derive feature +/// #[derive(Deserialize)] +/// struct Point { +/// x: u64, +/// y: u64, +/// } +/// +/// // Some Dhall data +/// let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }"; +/// +/// // Parse the Dhall string as a Point. +/// let point: Point = serde_dhall::options::from_str(data).parse()?; +/// +/// assert_eq!(point.x, 1); +/// assert_eq!(point.y, 2); +/// # Ok(()) +/// # } +/// ``` /// /// [`Deserializer`]: struct.Deserializer.html /// [`parse`]: struct.Deserializer.html#method.parse -pub fn from_str<'a, T>(s: &'a str) -> Deserializer<'a, T> { +pub fn from_str(s: &str) -> Deserializer<'_, T> { Deserializer::from_str(s) } -/// TODO +/// Deserialize an instance of type `T` from a Dhall file. +/// +/// This returns a [`Deserializer`] object. Call the [`parse`] method to get the deserialized value, or +/// use other [`Deserializer`] methods to e.g. add type annotations beforehand. +/// +/// # Example +/// +/// ```no_run +/// # fn main() -> serde_dhall::Result<()> { +/// use serde::Deserialize; +/// +/// // We use serde's derive feature +/// #[derive(Deserialize)] +/// struct Point { +/// x: u64, +/// y: u64, +/// } +/// +/// // Parse the Dhall file as a Point. +/// let point: Point = serde_dhall::options::from_file("foo.dhall").parse()?; +/// # Ok(()) +/// # } +/// ``` +/// +/// [`Deserializer`]: struct.Deserializer.html +/// [`parse`]: struct.Deserializer.html#method.parse pub fn from_file<'a, T, P: AsRef>(path: P) -> Deserializer<'a, T> { Deserializer::from_file(path) } @@ -154,3 +323,15 @@ pub fn from_file<'a, T, P: AsRef>(path: P) -> Deserializer<'a, T> { // pub fn from_url<'a, T>(url: &'a str) -> Deserializer<'a, T> { // Deserializer::from_url(url) // } + +// Custom impl to not get a Clone bound on T +impl<'a, T> Clone for Deserializer<'a, T> { + fn clone(&self) -> Self { + Deserializer { + source: self.source.clone(), + annot: self.annot.clone(), + allow_imports: self.allow_imports.clone(), + target_type: std::marker::PhantomData, + } + } +} diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs index fee9d73..ea7c20a 100644 --- a/serde_dhall/src/value.rs +++ b/serde_dhall/src/value.rs @@ -3,7 +3,7 @@ use std::collections::{BTreeMap, HashMap}; use dhall::semantics::{Hir, HirKind, Nir, NirKind}; use dhall::syntax::{Builtin, Expr, ExprKind, NumKind, Span}; -use crate::{FromDhall, Error, ErrorKind, Result, Sealed}; +use crate::{Error, ErrorKind, FromDhall, Result, Sealed}; #[doc(hidden)] /// An arbitrary Dhall value. -- cgit v1.2.3 From 678d254a06dbb75f5398abaacee41d1712bf7194 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 5 Apr 2020 15:53:15 +0100 Subject: Make Deserializer functions the only functions --- serde_dhall/src/deserialize.rs | 2 +- serde_dhall/src/lib.rs | 25 ++-- serde_dhall/src/options.rs | 29 +++-- serde_dhall/src/shortcuts.rs | 272 ----------------------------------------- serde_dhall/src/static_type.rs | 4 +- serde_dhall/src/value.rs | 6 +- 6 files changed, 30 insertions(+), 308 deletions(-) delete mode 100644 serde_dhall/src/shortcuts.rs (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/deserialize.rs b/serde_dhall/src/deserialize.rs index 1be68c4..92be2e9 100644 --- a/serde_dhall/src/deserialize.rs +++ b/serde_dhall/src/deserialize.rs @@ -30,7 +30,7 @@ pub trait Sealed {} /// } /// /// // Convert a Dhall string to a Point. -/// let point: Point = serde_dhall::from_str("{ x = 1, y = 1 + 1 }")?; +/// let point: Point = serde_dhall::from_str("{ x = 1, y = 1 + 1 }").parse()?; /// # Ok(()) /// # } /// ``` diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 9f99adb..8ad7cb3 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -30,7 +30,7 @@ //! let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }"; //! //! // Deserialize it to a Rust type. -//! let deserialized_map: HashMap = serde_dhall::from_str(data)?; +//! let deserialized_map: HashMap = serde_dhall::from_str(data).parse()?; //! //! let mut expected_map = HashMap::new(); //! expected_map.insert("x".to_string(), 1); @@ -57,7 +57,7 @@ //! let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }"; //! //! // Convert the Dhall string to a Point. -//! let point: Point = serde_dhall::from_str(data)?; +//! let point: Point = serde_dhall::from_str(data).parse()?; //! assert_eq!(point.x, 1); //! assert_eq!(point.y, 2); //! @@ -68,7 +68,7 @@ //! # Replacing `serde_json` or `serde_yaml` //! //! If you used to consume JSON or YAML, you only need to replace [`serde_json::from_str`] or -//! [`serde_yaml::from_str`] with [`serde_dhall::from_str`](fn.from_str.html). +//! [`serde_yaml::from_str`] with [`serde_dhall::from_str(…).parse()`](fn.from_str.html). //! //! [`serde_json::from_str`]: https://docs.serde.rs/serde_json/fn.from_str.html //! [`serde_yaml::from_str`]: https://docs.serde.rs/serde_yaml/fn.from_str.html @@ -103,13 +103,13 @@ //! let data = "{ x = 1, y = 1 + 1 }"; //! //! // Convert the Dhall string to a Point. -//! let point: Point = serde_dhall::from_str_static_type(data)?; +//! let point: Point = serde_dhall::from_str(data).static_type_annotation().parse()?; //! assert_eq!(point.x, 1); //! assert_eq!(point.y, 2); //! //! // Invalid data fails the type validation //! let invalid_data = "{ x = 1, z = 0.3 }"; -//! assert!(serde_dhall::from_str_static_type::(invalid_data).is_err()); +//! assert!(serde_dhall::from_str::(invalid_data).static_type_annotation().parse().is_err()); //! # Ok(()) //! # } //! ``` @@ -124,7 +124,7 @@ //! //! // Parse a Dhall type //! let point_type_str = "{ x: Natural, y: Natural }"; -//! let point_type: SimpleType = serde_dhall::from_str(point_type_str)?; +//! let point_type: SimpleType = serde_dhall::from_str(point_type_str).parse()?; //! //! // Some Dhall data //! let point_data = "{ x = 1, y = 1 + 1 }"; @@ -132,7 +132,7 @@ //! // Deserialize the data to a Rust type. This checks that //! // the data matches the provided type. //! let deserialized_map: HashMap = -//! serde_dhall::from_str_manual_type(point_data, &point_type)?; +//! serde_dhall::from_str(point_data).type_annotation(&point_type).parse()?; //! //! let mut expected_map = HashMap::new(); //! expected_map.insert("x".to_string(), 1); @@ -158,13 +158,9 @@ mod test_readme { doc_comment::doctest!("../../README.md"); } -/// Finer-grained control over deserializing Dhall values -pub mod options; - +mod options; mod deserialize; mod error; -/// Common patterns made easier -mod shortcuts; mod static_type; /// Dhall values mod value; @@ -176,9 +172,8 @@ pub use deserialize::FromDhall; pub(crate) use deserialize::Sealed; pub(crate) use error::ErrorKind; pub use error::{Error, Result}; -pub use shortcuts::{ - from_file, from_file_manual_type, from_file_static_type, from_str, - from_str_manual_type, from_str_static_type, +pub use options::{ + from_file, from_str, Deserializer }; pub use static_type::StaticType; pub use value::{SimpleType, Value}; diff --git a/serde_dhall/src/options.rs b/serde_dhall/src/options.rs index f3797ee..c44de90 100644 --- a/serde_dhall/src/options.rs +++ b/serde_dhall/src/options.rs @@ -27,9 +27,9 @@ enum Source<'a> { /// /// ```no_run /// # fn main() -> serde_dhall::Result<()> { -/// use serde_dhall::options; +/// use serde_dhall::from_file; /// -/// let data = options::from_file("foo.dhall").parse()?; +/// let data = from_file("foo.dhall").parse()?; /// # Ok(()) /// # } /// ``` @@ -38,10 +38,10 @@ enum Source<'a> { /// /// ```no_run /// # fn main() -> serde_dhall::Result<()> { -/// use serde_dhall::options; +/// use serde_dhall::{from_file, from_str}; /// -/// let ty = options::from_str("{ x: Natural, y: Natural }").parse()?; -/// let data = options::from_file("foo.dhall") +/// let ty = from_str("{ x: Natural, y: Natural }").parse()?; +/// let data = from_file("foo.dhall") /// .type_annotation(&ty) /// .parse()?; /// # Ok(()) @@ -91,7 +91,7 @@ impl<'a, T> Deserializer<'a, T> { /// /// let data = "12 + ./other_file.dhall : Natural"; /// assert!( - /// serde_dhall::options::from_str::(data) + /// serde_dhall::from_str::(data) /// .imports(false) /// .parse() /// .is_err() @@ -136,11 +136,11 @@ impl<'a, T> Deserializer<'a, T> { /// /// // Parse a Dhall type /// let point_type_str = "{ x: Natural, y: Optional Natural }"; - /// let point_type: SimpleType = serde_dhall::options::from_str(point_type_str).parse()?; + /// let point_type: SimpleType = serde_dhall::from_str(point_type_str).parse()?; /// /// // Parse some Dhall data to a Point. /// let data = "{ x = 1, y = Some (1 + 1) }"; - /// let point: Point = serde_dhall::options::from_str(data) + /// let point: Point = serde_dhall::from_str(data) /// .type_annotation(&point_type) /// .parse()?; /// assert_eq!(point.x, 1); @@ -149,7 +149,7 @@ impl<'a, T> Deserializer<'a, T> { /// // Invalid data fails the type validation; deserialization would have succeeded otherwise. /// let invalid_data = "{ x = 1 }"; /// assert!( - /// serde_dhall::options::from_str::(invalid_data) + /// serde_dhall::from_str::(invalid_data) /// .type_annotation(&point_type) /// .parse() /// .is_err() @@ -187,7 +187,7 @@ impl<'a, T> Deserializer<'a, T> { /// let data = "{ x = 1, y = Some (1 + 1) }"; /// /// // Convert the Dhall string to a Point. - /// let point: Point = serde_dhall::options::from_str(data) + /// let point: Point = serde_dhall::from_str(data) /// .static_type_annotation() /// .parse()?; /// assert_eq!(point.x, 1); @@ -196,7 +196,7 @@ impl<'a, T> Deserializer<'a, T> { /// // Invalid data fails the type validation; deserialization would have succeeded otherwise. /// let invalid_data = "{ x = 1 }"; /// assert!( - /// serde_dhall::options::from_str::(invalid_data) + /// serde_dhall::from_str::(invalid_data) /// .static_type_annotation() /// .parse() /// .is_err() @@ -240,8 +240,7 @@ impl<'a, T> Deserializer<'a, T> { /// /// ```no_run /// # fn main() -> serde_dhall::Result<()> { - /// use serde_dhall::options; - /// let data = options::from_file("foo.dhall").parse()?; + /// let data = serde_dhall::from_file("foo.dhall").parse()?; /// # Ok(()) /// # } /// ``` @@ -276,7 +275,7 @@ impl<'a, T> Deserializer<'a, T> { /// let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }"; /// /// // Parse the Dhall string as a Point. -/// let point: Point = serde_dhall::options::from_str(data).parse()?; +/// let point: Point = serde_dhall::from_str(data).parse()?; /// /// assert_eq!(point.x, 1); /// assert_eq!(point.y, 2); @@ -309,7 +308,7 @@ pub fn from_str(s: &str) -> Deserializer<'_, T> { /// } /// /// // Parse the Dhall file as a Point. -/// let point: Point = serde_dhall::options::from_file("foo.dhall").parse()?; +/// let point: Point = serde_dhall::from_file("foo.dhall").parse()?; /// # Ok(()) /// # } /// ``` diff --git a/serde_dhall/src/shortcuts.rs b/serde_dhall/src/shortcuts.rs deleted file mode 100644 index 9c9ce9f..0000000 --- a/serde_dhall/src/shortcuts.rs +++ /dev/null @@ -1,272 +0,0 @@ -use doc_comment::doc_comment; -use std::path::Path; - -use crate::{options, FromDhall, Result, SimpleType, StaticType}; - -// Avoid copy-pasting documentation - -#[rustfmt::skip] -macro_rules! gen_doc { - (@source_desc, str) => {"a string of Dhall text"}; - (@source_desc, file) => {"a Dhall file"}; - (@source_desc, url) => {"a remote url"}; - - (@tck_info1, none) => {""}; - (@tck_info1, manual) => {", additionally checking that it matches the supplied type"}; - (@tck_info1, static) => {", additionally checking that it matches the type of `T`"}; - - (@tck_info2, none) => {""}; - (@tck_info2, manual) => {" against the supplied type"}; - (@tck_info2, static) => {" against the type of `T`"}; - - (@tck_req, $src:tt, none) => {""}; - (@tck_req, $src:tt, manual) => {""}; - (@tck_req, $src:tt, static) => { - concat!("`T` must implement the [`StaticType`] trait. Use [`from_", stringify!($src), - "_manual_type`] to provide a type manually.\n") - }; - - (@tck_comment, $src:tt, none) => { - concat!("For additional type safety, prefer [`from_", stringify!($src), "_static_type`] - or [`from_", stringify!($src), "_manual_type`].\n") - }; - (@tck_comment, $src:tt, manual) => {concat!("See also [`from_", stringify!($src), "_static_type`].\n")}; - (@tck_comment, $src:tt, static) => {""}; - - (@run_example, str) => {""}; - (@run_example, file) => {"no_run"}; - (@run_example, url) => {"no_run"}; - - ($src:tt, $ty:tt) => {concat!(" -Deserialize an instance of type `T` from ", gen_doc!(@source_desc, $src), gen_doc!(@tck_info1, $ty),". - -", gen_doc!(@tck_req, $src, $ty), " -This will recursively resolve all imports in the expression, and typecheck it", gen_doc!(@tck_info2, $ty)," -before deserialization. Relative imports will be resolved relative to the current directory. -See [`options`] for more control over this process. - -", gen_doc!(@tck_comment, $src, $ty), " - -# Example - -```", gen_doc!(@run_example, $src), " -# fn main() -> serde_dhall::Result<()> {", -gen_example!($src, $ty), " -# Ok(()) -# } -``` - -[`options`]: options/index.html -[`from_", stringify!($src), "_manual_type`]: fn.from_", stringify!($src), "_manual_type.html -[`from_", stringify!($src), "_static_type`]: fn.from_", stringify!($src), "_static_type.html -[`StaticType`]: trait.StaticType.html -")}; -} - -#[rustfmt::skip] -macro_rules! gen_example { - (str, none) => {concat!(r#" -use serde::Deserialize; - -// We use serde's derive feature -#[derive(Deserialize)] -struct Point { - x: u64, - y: u64, -} - -// Some Dhall data -let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }"; - -// Parse the Dhall string as a Point. -let point: Point = serde_dhall::from_str(data)?; - -assert_eq!(point.x, 1); -assert_eq!(point.y, 2); -"#)}; - - (str, manual) => {concat!(r#" -use std::collections::HashMap; -use serde_dhall::SimpleType; - -// Parse a Dhall type -let point_type_str = "{ x: Natural, y: Natural }"; -let point_type: SimpleType = serde_dhall::from_str(point_type_str)?; - -// Some Dhall data -let point_data = "{ x = 1, y = 1 + 1 }"; - -// Deserialize the data to a Rust type. This checks that -// the data matches the provided type. -let deserialized_map: HashMap = - serde_dhall::from_str_manual_type(point_data, &point_type)?; - -let mut expected_map = HashMap::new(); -expected_map.insert("x".to_string(), 1); -expected_map.insert("y".to_string(), 2); - -assert_eq!(deserialized_map, expected_map); -"#)}; - - (str, static) => {concat!(r#" -use serde::Deserialize; -use serde_dhall::StaticType; - -#[derive(Deserialize, StaticType)] -struct Point { - x: u64, - y: u64, -} - -// Some Dhall data -let data = "{ x = 1, y = 1 + 1 }"; - -// Convert the Dhall string to a Point. -let point: Point = serde_dhall::from_str_static_type(data)?; -assert_eq!(point.x, 1); -assert_eq!(point.y, 2); - -// Invalid data fails the type validation -let invalid_data = "{ x = 1, z = 0.3 }"; -assert!(serde_dhall::from_str_static_type::(invalid_data).is_err()); -"#)}; - - (file, none) => {concat!(r#" -use serde::Deserialize; - -// We use serde's derive feature -#[derive(Deserialize)] -struct Point { - x: u64, - y: u64, -} - -// Parse the Dhall file as a Point. -let point: Point = serde_dhall::from_file("foo.dhall")?; -"#)}; - - (file, manual) => {concat!(r#" -use std::collections::HashMap; -use serde_dhall::SimpleType; - -// Parse a Dhall type -let point_type_str = "{ x: Natural, y: Natural }"; -let point_type: SimpleType = serde_dhall::from_str(point_type_str)?; - -// Deserialize the data to a Rust type. This checks that -// the data matches the provided type. -let deserialized_map: HashMap = - serde_dhall::from_file_manual_type("foo.dhall", &point_type)?; -"#)}; - - (file, static) => {concat!(r#" -use serde::Deserialize; -use serde_dhall::StaticType; - -#[derive(Deserialize, StaticType)] -struct Point { - x: u64, - y: u64, -} - -// Convert the Dhall string to a Point. -let point: Point = serde_dhall::from_file_static_type("foo.dhall")?; -"#)}; - - ($src:tt, $ty:tt) => {""}; -} - -macro_rules! generate_fn { - (@generate_src, - str, $ty:tt, $name:ident, - ) => { - generate_fn!(@generate_ty, - str, $ty, $name, - (), - (s: &str), - (options::from_str(s)), - ); - }; - (@generate_src, - file, $ty:tt, $name:ident, - ) => { - generate_fn!(@generate_ty, - file, $ty, $name, - (P: AsRef), - (path: P), - (options::from_file(path)), - ); - }; - - (@generate_ty, - $src:tt, none, $name:ident, - ($($ty_params:tt)*), - ($($input_args:tt)*), - ($($create_options:tt)*), - ) => { - generate_fn!(@generate, - $src, none, $name, - ($($ty_params)*), - ($($input_args)*), - (), - ($($create_options)*), - ); - }; - (@generate_ty, - $src:tt, manual, $name:ident, - ($($ty_params:tt)*), - ($($input_args:tt)*), - ($($create_options:tt)*), - ) => { - generate_fn!(@generate, - $src, manual, $name, - ($($ty_params)*), - ($($input_args)*, ty: &SimpleType), - (), - ($($create_options)* .type_annotation(ty)), - ); - }; - (@generate_ty, - $src:tt, static, $name:ident, - ($($ty_params:tt)*), - ($($input_args:tt)*), - ($($create_options:tt)*), - ) => { - generate_fn!(@generate, - $src, static, $name, - ($($ty_params)*), - ($($input_args)*), - (+ StaticType), - ($($create_options)* .static_type_annotation()), - ); - }; - - (@generate, - $src:tt, $ty:tt, $name:ident, - ($($ty_params:tt)*), - ($($input_args:tt)*), - ($($extra_bounds:tt)*), - ($($create_options:tt)*), - ) => { - doc_comment! { - gen_doc!($src, $ty), - pub fn $name ($($input_args)*) -> Result - where - T: FromDhall $($extra_bounds)*, - { - $($create_options)* .parse() - } - } - }; - - ($src:tt, $ty:tt, $name:ident) => { - generate_fn!(@generate_src, $src, $ty, $name,); - }; -} - -generate_fn!(str, none, from_str); -generate_fn!(str, manual, from_str_manual_type); -generate_fn!(str, static, from_str_static_type); -generate_fn!(file, none, from_file); -generate_fn!(file, manual, from_file_manual_type); -generate_fn!(file, static, from_file_static_type); diff --git a/serde_dhall/src/static_type.rs b/serde_dhall/src/static_type.rs index 020dfce..6e76424 100644 --- a/serde_dhall/src/static_type.rs +++ b/serde_dhall/src/static_type.rs @@ -23,7 +23,7 @@ use crate::SimpleType; /// } /// /// let ty: SimpleType = -/// serde_dhall::from_str("{ x: Bool, y: List Natural }")?; +/// serde_dhall::from_str("{ x: Bool, y: List Natural }").parse()?; /// /// assert_eq!(Foo::static_type(), ty); /// # Ok(()) @@ -71,7 +71,7 @@ pub trait StaticType { /// } /// } /// - /// let foo: Foo = serde_dhall::from_str_static_type("[ 1, 2 ]")?; + /// let foo: Foo = serde_dhall::from_str("[ 1, 2 ]").static_type_annotation().parse()?; /// /// assert_eq!(foo.0, vec![1, 2]); /// # Ok(()) diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs index ea7c20a..f21e836 100644 --- a/serde_dhall/src/value.rs +++ b/serde_dhall/src/value.rs @@ -28,7 +28,7 @@ pub(crate) enum SimpleValue { Union(String, Option>), } -/// The type of a value that can be decoded by Serde, like `{ x: Bool, y: List Natural }`. +/// The type of a value that can be decoded by Serde, e.g. `{ x: Bool, y: List Natural }`. /// /// A `SimpleType` is used when deserializing values to ensure they are of the expected type. /// Rather than letting `serde` handle potential type mismatches, this uses the type-checking @@ -53,7 +53,7 @@ pub(crate) enum SimpleValue { /// use serde_dhall::SimpleType; /// /// let ty: SimpleType = -/// serde_dhall::from_str("{ x: Natural, y: Natural }")?; +/// serde_dhall::from_str("{ x: Natural, y: Natural }").parse()?; /// /// let mut map = HashMap::new(); /// map.insert("x".to_string(), SimpleType::Natural); @@ -74,7 +74,7 @@ pub(crate) enum SimpleValue { /// } /// /// let ty: SimpleType = -/// serde_dhall::from_str("{ x: Bool, y: List Natural }")?; +/// serde_dhall::from_str("{ x: Bool, y: List Natural }").parse()?; /// /// assert_eq!(Foo::static_type(), ty); /// # Ok(()) -- cgit v1.2.3 From 797241ebec5cec686056b5da73864db8eab03d48 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 5 Apr 2020 16:36:54 +0100 Subject: Rewrite builder with state machine to allow parse::<> --- serde_dhall/src/lib.rs | 24 ++++-- serde_dhall/src/options.rs | 203 ++++++++++++++++++++++++++------------------- 2 files changed, 132 insertions(+), 95 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 8ad7cb3..08ca4a5 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -103,13 +103,20 @@ //! let data = "{ x = 1, y = 1 + 1 }"; //! //! // Convert the Dhall string to a Point. -//! let point: Point = serde_dhall::from_str(data).static_type_annotation().parse()?; +//! let point = serde_dhall::from_str(data) +//! .static_type_annotation() +//! .parse::()?; //! assert_eq!(point.x, 1); //! assert_eq!(point.y, 2); //! //! // Invalid data fails the type validation //! let invalid_data = "{ x = 1, z = 0.3 }"; -//! assert!(serde_dhall::from_str::(invalid_data).static_type_annotation().parse().is_err()); +//! assert!( +//! serde_dhall::from_str(invalid_data) +//! .static_type_annotation() +//! .parse::() +//! .is_err() +//! ); //! # Ok(()) //! # } //! ``` @@ -124,15 +131,16 @@ //! //! // Parse a Dhall type //! let point_type_str = "{ x: Natural, y: Natural }"; -//! let point_type: SimpleType = serde_dhall::from_str(point_type_str).parse()?; +//! let point_type = serde_dhall::from_str(point_type_str).parse::()?; //! //! // Some Dhall data //! let point_data = "{ x = 1, y = 1 + 1 }"; //! //! // Deserialize the data to a Rust type. This checks that //! // the data matches the provided type. -//! let deserialized_map: HashMap = -//! serde_dhall::from_str(point_data).type_annotation(&point_type).parse()?; +//! let deserialized_map = serde_dhall::from_str(point_data) +//! .type_annotation(&point_type) +//! .parse::>()?; //! //! let mut expected_map = HashMap::new(); //! expected_map.insert("x".to_string(), 1); @@ -158,9 +166,9 @@ mod test_readme { doc_comment::doctest!("../../README.md"); } -mod options; mod deserialize; mod error; +mod options; mod static_type; /// Dhall values mod value; @@ -172,8 +180,6 @@ pub use deserialize::FromDhall; pub(crate) use deserialize::Sealed; pub(crate) use error::ErrorKind; pub use error::{Error, Result}; -pub use options::{ - from_file, from_str, Deserializer -}; +pub use options::{from_file, from_str, Deserializer}; pub use static_type::StaticType; pub use value::{SimpleType, Value}; diff --git a/serde_dhall/src/options.rs b/serde_dhall/src/options.rs index c44de90..a20891a 100644 --- a/serde_dhall/src/options.rs +++ b/serde_dhall/src/options.rs @@ -12,6 +12,32 @@ enum Source<'a> { // Url(&'a str), } +#[derive(Debug, Clone)] +pub struct NoAnnot; +#[derive(Debug, Clone)] +pub struct ManualAnnot(SimpleType); +#[derive(Debug, Clone)] +pub struct StaticAnnot; + +pub trait HasAnnot { + fn get_annot(a: &A) -> Option; +} +impl HasAnnot for T { + fn get_annot(_: &NoAnnot) -> Option { + None + } +} +impl HasAnnot for T { + fn get_annot(a: &ManualAnnot) -> Option { + Some(a.0.clone()) + } +} +impl HasAnnot for T { + fn get_annot(_: &StaticAnnot) -> Option { + Some(T::static_type()) + } +} + /// Controls how a dhall value is read. /// /// This builder exposes the ability to configure how a value is deserialized and what operations @@ -29,7 +55,7 @@ enum Source<'a> { /// # fn main() -> serde_dhall::Result<()> { /// use serde_dhall::from_file; /// -/// let data = from_file("foo.dhall").parse()?; +/// let data = from_file("foo.dhall").parse::()?; /// # Ok(()) /// # } /// ``` @@ -38,34 +64,33 @@ enum Source<'a> { /// /// ```no_run /// # fn main() -> serde_dhall::Result<()> { +/// use std::collections::HashMap; /// use serde_dhall::{from_file, from_str}; /// /// let ty = from_str("{ x: Natural, y: Natural }").parse()?; /// let data = from_file("foo.dhall") /// .type_annotation(&ty) -/// .parse()?; +/// .parse::>()?; /// # Ok(()) /// # } /// ``` -#[derive(Debug)] -pub struct Deserializer<'a, T> { +#[derive(Debug, Clone)] +pub struct Deserializer<'a, A> { source: Source<'a>, - annot: Option, + annot: A, allow_imports: bool, // allow_remote_imports: bool, // use_cache: bool, - target_type: std::marker::PhantomData, } -impl<'a, T> Deserializer<'a, T> { +impl<'a> Deserializer<'a, NoAnnot> { fn default_with_source(source: Source<'a>) -> Self { Deserializer { source, - annot: None, + annot: NoAnnot, allow_imports: true, // allow_remote_imports: true, // use_cache: true, - target_type: std::marker::PhantomData, } } fn from_str(s: &'a str) -> Self { @@ -77,45 +102,9 @@ impl<'a, T> Deserializer<'a, T> { // fn from_url(url: &'a str) -> Self { // Self::default_with_source(Source::Url(url)) // } +} - /// Sets whether to enable imports. - /// - /// By default, imports are enabled. - /// - /// # Examples - /// - /// ``` - /// # fn main() -> serde_dhall::Result<()> { - /// use serde::Deserialize; - /// use serde_dhall::SimpleType; - /// - /// let data = "12 + ./other_file.dhall : Natural"; - /// assert!( - /// serde_dhall::from_str::(data) - /// .imports(false) - /// .parse() - /// .is_err() - /// ); - /// # Ok(()) - /// # } - /// ``` - /// - /// [`static_type_annotation`]: struct.Deserializer.html#method.static_type_annotation - /// [`StaticType`]: trait.StaticType.html - pub fn imports(&mut self, imports: bool) -> &mut Self { - self.allow_imports = imports; - self - } - - // /// TODO - // pub fn remote_imports(&mut self, imports: bool) -> &mut Self { - // self.allow_remote_imports = imports; - // if imports { - // self.allow_imports = true; - // } - // self - // } - +impl<'a> Deserializer<'a, NoAnnot> { /// Ensures that the parsed value matches the provided type. /// /// In many cases the Dhall type that corresponds to a Rust type can be inferred automatically. @@ -136,22 +125,22 @@ impl<'a, T> Deserializer<'a, T> { /// /// // Parse a Dhall type /// let point_type_str = "{ x: Natural, y: Optional Natural }"; - /// let point_type: SimpleType = serde_dhall::from_str(point_type_str).parse()?; + /// let point_type = serde_dhall::from_str(point_type_str).parse::()?; /// /// // Parse some Dhall data to a Point. /// let data = "{ x = 1, y = Some (1 + 1) }"; - /// let point: Point = serde_dhall::from_str(data) + /// let point = serde_dhall::from_str(data) /// .type_annotation(&point_type) - /// .parse()?; + /// .parse::()?; /// assert_eq!(point.x, 1); /// assert_eq!(point.y, Some(2)); /// /// // Invalid data fails the type validation; deserialization would have succeeded otherwise. /// let invalid_data = "{ x = 1 }"; /// assert!( - /// serde_dhall::from_str::(invalid_data) + /// serde_dhall::from_str(invalid_data) /// .type_annotation(&point_type) - /// .parse() + /// .parse::() /// .is_err() /// ); /// # Ok(()) @@ -160,9 +149,15 @@ impl<'a, T> Deserializer<'a, T> { /// /// [`static_type_annotation`]: struct.Deserializer.html#method.static_type_annotation /// [`StaticType`]: trait.StaticType.html - pub fn type_annotation(&mut self, ty: &SimpleType) -> &mut Self { - self.annot = Some(ty.clone()); - self + pub fn type_annotation( + self, + ty: &SimpleType, + ) -> Deserializer<'a, ManualAnnot> { + Deserializer { + annot: ManualAnnot(ty.clone()), + source: self.source, + allow_imports: self.allow_imports, + } } /// Ensures that the parsed value matches the type of `T`. @@ -187,18 +182,18 @@ impl<'a, T> Deserializer<'a, T> { /// let data = "{ x = 1, y = Some (1 + 1) }"; /// /// // Convert the Dhall string to a Point. - /// let point: Point = serde_dhall::from_str(data) + /// let point = serde_dhall::from_str(data) /// .static_type_annotation() - /// .parse()?; + /// .parse::()?; /// assert_eq!(point.x, 1); /// assert_eq!(point.y, Some(2)); /// /// // Invalid data fails the type validation; deserialization would have succeeded otherwise. /// let invalid_data = "{ x = 1 }"; /// assert!( - /// serde_dhall::from_str::(invalid_data) + /// serde_dhall::from_str(invalid_data) /// .static_type_annotation() - /// .parse() + /// .parse::() /// .is_err() /// ); /// # Ok(()) @@ -207,15 +202,60 @@ impl<'a, T> Deserializer<'a, T> { /// /// [`type_annotation`]: struct.Deserializer.html#method.type_annotation /// [`StaticType`]: trait.StaticType.html - pub fn static_type_annotation(&mut self) -> &mut Self - where - T: StaticType, - { - self.annot = Some(T::static_type()); - self + pub fn static_type_annotation(self) -> Deserializer<'a, StaticAnnot> { + Deserializer { + annot: StaticAnnot, + source: self.source, + allow_imports: self.allow_imports, + } + } +} + +impl<'a, A> Deserializer<'a, A> { + /// Sets whether to enable imports. + /// + /// By default, imports are enabled. + /// + /// # Examples + /// + /// ``` + /// # fn main() -> serde_dhall::Result<()> { + /// use serde::Deserialize; + /// use serde_dhall::SimpleType; + /// + /// let data = "12 + ./other_file.dhall : Natural"; + /// assert!( + /// serde_dhall::from_str(data) + /// .imports(false) + /// .parse::() + /// .is_err() + /// ); + /// # Ok(()) + /// # } + /// ``` + /// + /// [`static_type_annotation`]: struct.Deserializer.html#method.static_type_annotation + /// [`StaticType`]: trait.StaticType.html + pub fn imports(self, imports: bool) -> Self { + Deserializer { + allow_imports: imports, + ..self + } } - fn _parse(&self) -> dhall::error::Result { + // /// TODO + // pub fn remote_imports(&mut self, imports: bool) -> &mut Self { + // self.allow_remote_imports = imports; + // if imports { + // self.allow_imports = true; + // } + // self + // } + + fn _parse(&self) -> dhall::error::Result + where + T: HasAnnot, + { let parsed = match &self.source { Source::Str(s) => Parsed::parse_str(s)?, Source::File(p) => Parsed::parse_file(p.as_ref())?, @@ -225,7 +265,7 @@ impl<'a, T> Deserializer<'a, T> { } else { parsed.skip_resolve()? }; - let typed = match &self.annot { + let typed = match &T::get_annot(&self.annot) { None => resolved.typecheck()?, Some(ty) => resolved.typecheck_with(ty.to_value().as_hir())?, }; @@ -240,15 +280,18 @@ impl<'a, T> Deserializer<'a, T> { /// /// ```no_run /// # fn main() -> serde_dhall::Result<()> { - /// let data = serde_dhall::from_file("foo.dhall").parse()?; + /// let data = serde_dhall::from_file("foo.dhall").parse::()?; /// # Ok(()) /// # } /// ``` - pub fn parse(&self) -> Result + pub fn parse(&self) -> Result where - T: FromDhall, + T: FromDhall + HasAnnot, { - let val = self._parse().map_err(ErrorKind::Dhall).map_err(Error)?; + let val = self + ._parse::() + .map_err(ErrorKind::Dhall) + .map_err(Error)?; T::from_dhall(&val) } } @@ -285,7 +328,7 @@ impl<'a, T> Deserializer<'a, T> { /// /// [`Deserializer`]: struct.Deserializer.html /// [`parse`]: struct.Deserializer.html#method.parse -pub fn from_str(s: &str) -> Deserializer<'_, T> { +pub fn from_str(s: &str) -> Deserializer<'_, NoAnnot> { Deserializer::from_str(s) } @@ -315,22 +358,10 @@ pub fn from_str(s: &str) -> Deserializer<'_, T> { /// /// [`Deserializer`]: struct.Deserializer.html /// [`parse`]: struct.Deserializer.html#method.parse -pub fn from_file<'a, T, P: AsRef>(path: P) -> Deserializer<'a, T> { +pub fn from_file<'a, P: AsRef>(path: P) -> Deserializer<'a, NoAnnot> { Deserializer::from_file(path) } -// pub fn from_url<'a, T>(url: &'a str) -> Deserializer<'a, T> { +// pub fn from_url(url: &str) -> Deserializer<'_, NoAnnot> { // Deserializer::from_url(url) // } - -// Custom impl to not get a Clone bound on T -impl<'a, T> Clone for Deserializer<'a, T> { - fn clone(&self) -> Self { - Deserializer { - source: self.source.clone(), - annot: self.annot.clone(), - allow_imports: self.allow_imports.clone(), - target_type: std::marker::PhantomData, - } - } -} -- cgit v1.2.3 From 118c02d330865fcbfb1f2b0028f9404d61b662d8 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 5 Apr 2020 16:42:04 +0100 Subject: Borrow type annotation --- serde_dhall/src/options.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/options.rs b/serde_dhall/src/options.rs index a20891a..0f18091 100644 --- a/serde_dhall/src/options.rs +++ b/serde_dhall/src/options.rs @@ -15,7 +15,7 @@ enum Source<'a> { #[derive(Debug, Clone)] pub struct NoAnnot; #[derive(Debug, Clone)] -pub struct ManualAnnot(SimpleType); +pub struct ManualAnnot<'ty>(&'ty SimpleType); #[derive(Debug, Clone)] pub struct StaticAnnot; @@ -27,8 +27,8 @@ impl HasAnnot for T { None } } -impl HasAnnot for T { - fn get_annot(a: &ManualAnnot) -> Option { +impl<'ty, T> HasAnnot> for T { + fn get_annot(a: &ManualAnnot<'ty>) -> Option { Some(a.0.clone()) } } @@ -38,7 +38,7 @@ impl HasAnnot for T { } } -/// Controls how a dhall value is read. +/// Controls how a Dhall value is read. /// /// This builder exposes the ability to configure how a value is deserialized and what operations /// are permitted during evaluation. @@ -102,9 +102,7 @@ impl<'a> Deserializer<'a, NoAnnot> { // fn from_url(url: &'a str) -> Self { // Self::default_with_source(Source::Url(url)) // } -} -impl<'a> Deserializer<'a, NoAnnot> { /// Ensures that the parsed value matches the provided type. /// /// In many cases the Dhall type that corresponds to a Rust type can be inferred automatically. @@ -149,12 +147,12 @@ impl<'a> Deserializer<'a, NoAnnot> { /// /// [`static_type_annotation`]: struct.Deserializer.html#method.static_type_annotation /// [`StaticType`]: trait.StaticType.html - pub fn type_annotation( + pub fn type_annotation<'ty>( self, - ty: &SimpleType, - ) -> Deserializer<'a, ManualAnnot> { + ty: &'ty SimpleType, + ) -> Deserializer<'a, ManualAnnot<'ty>> { Deserializer { - annot: ManualAnnot(ty.clone()), + annot: ManualAnnot(ty), source: self.source, allow_imports: self.allow_imports, } -- cgit v1.2.3 From 75c0f328c7b6d404353fd078ae12417766ef8a32 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 5 Apr 2020 16:49:15 +0100 Subject: Tweaks --- serde_dhall/src/options.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/options.rs b/serde_dhall/src/options.rs index 0f18091..ca1e58c 100644 --- a/serde_dhall/src/options.rs +++ b/serde_dhall/src/options.rs @@ -12,11 +12,11 @@ enum Source<'a> { // Url(&'a str), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct NoAnnot; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct ManualAnnot<'ty>(&'ty SimpleType); -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct StaticAnnot; pub trait HasAnnot { @@ -108,7 +108,7 @@ impl<'a> Deserializer<'a, NoAnnot> { /// In many cases the Dhall type that corresponds to a Rust type can be inferred automatically. /// See the [`StaticType`] trait and the [`static_type_annotation`] method. /// - /// # Examples + /// # Example /// /// ``` /// # fn main() -> serde_dhall::Result<()> { @@ -163,7 +163,7 @@ impl<'a> Deserializer<'a, NoAnnot> { /// `T` must implement the [`StaticType`] trait. If it doesn't, you can use [`type_annotation`] /// to provide a type manually. /// - /// # Examples + /// # Example /// /// ``` /// # fn main() -> serde_dhall::Result<()> { @@ -214,7 +214,7 @@ impl<'a, A> Deserializer<'a, A> { /// /// By default, imports are enabled. /// - /// # Examples + /// # Example /// /// ``` /// # fn main() -> serde_dhall::Result<()> { @@ -272,16 +272,19 @@ impl<'a, A> Deserializer<'a, A> { /// Parses the chosen dhall value with the options provided. /// - /// # Examples + /// If you enabled static annotations, `T` is additional required to implement [`StaticType`]. /// - /// Reading from a file: /// - /// ```no_run + /// # Example + /// + /// ``` /// # fn main() -> serde_dhall::Result<()> { - /// let data = serde_dhall::from_file("foo.dhall").parse::()?; + /// let data = serde_dhall::from_str("6 * 7").parse::()?; + /// assert_eq!(data, 42) /// # Ok(()) /// # } /// ``` + /// [`StaticType`]: trait.StaticType.html pub fn parse(&self) -> Result where T: FromDhall + HasAnnot, -- cgit v1.2.3 From c4d9e73126131d31e707822b0fd8b0710363c863 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 5 Apr 2020 17:12:08 +0100 Subject: Final doc tweaks --- serde_dhall/src/lib.rs | 4 +-- serde_dhall/src/options.rs | 64 +++++++++++++++++++++--------------------- serde_dhall/src/static_type.rs | 4 ++- serde_dhall/src/value.rs | 45 +++++++++++++++-------------- 4 files changed, 59 insertions(+), 58 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 08ca4a5..c478b2a 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -154,9 +154,9 @@ //! # Controlling deserialization //! //! If you need more control over the process of reading Dhall values, e.g. disabling -//! imports, see the [`options`] module. +//! imports, see the [`Deserializer`] methods. //! -//! [`options`]: options/index.html +//! [`Deserializer`]: struct.Deserializer.html //! [dhall]: https://dhall-lang.org/ //! [serde]: https://docs.serde.rs/serde/ //! [serde::Deserialize]: https://docs.serde.rs/serde/trait.Deserialize.html diff --git a/serde_dhall/src/options.rs b/serde_dhall/src/options.rs index ca1e58c..06a4368 100644 --- a/serde_dhall/src/options.rs +++ b/serde_dhall/src/options.rs @@ -43,9 +43,15 @@ impl HasAnnot for T { /// This builder exposes the ability to configure how a value is deserialized and what operations /// are permitted during evaluation. /// -/// Generally speaking, when using `Deserializer`, you'll create it with `from_str` or `from_file`, then -/// chain calls to methods to set each option, then call `parse`. This will give you a -/// `serde_dhall::Result` where `T` is a deserializable type of your choice. +/// Generally speaking, when using [`Deserializer`], you'll create it with [`from_str`] or [`from_file`], then +/// chain calls to methods to set each option, then call [`parse`]. This will give you a +/// [`Result`] where `T` is a deserializable type of your choice. +/// +/// [`Deserializer`]: struct.Deserializer.html +/// [`from_str`]: fn.from_str.html +/// [`from_file`]: fn.from_file.html +/// [`parse`]: struct.Deserializer.html#method.parse +/// [`Result`]: type.Result.html /// /// # Examples /// @@ -106,39 +112,33 @@ impl<'a> Deserializer<'a, NoAnnot> { /// Ensures that the parsed value matches the provided type. /// /// In many cases the Dhall type that corresponds to a Rust type can be inferred automatically. - /// See the [`StaticType`] trait and the [`static_type_annotation`] method. + /// See the [`StaticType`] trait and the [`static_type_annotation`] method for that. /// /// # Example /// /// ``` /// # fn main() -> serde_dhall::Result<()> { + /// use std::collections::HashMap; /// use serde::Deserialize; - /// use serde_dhall::SimpleType; - /// - /// #[derive(Deserialize)] - /// struct Point { - /// x: u64, - /// y: Option, - /// } + /// use serde_dhall::{from_str, SimpleType}; /// /// // Parse a Dhall type - /// let point_type_str = "{ x: Natural, y: Optional Natural }"; - /// let point_type = serde_dhall::from_str(point_type_str).parse::()?; + /// let type_str = "{ x: Natural, y: Natural }"; + /// let ty = from_str(type_str).parse::()?; /// - /// // Parse some Dhall data to a Point. - /// let data = "{ x = 1, y = Some (1 + 1) }"; - /// let point = serde_dhall::from_str(data) - /// .type_annotation(&point_type) - /// .parse::()?; - /// assert_eq!(point.x, 1); - /// assert_eq!(point.y, Some(2)); + /// // Parse some Dhall data. + /// let data = "{ x = 1, y = 1 + 1 }"; + /// let point = from_str(data) + /// .type_annotation(&ty) + /// .parse::>()?; + /// assert_eq!(point.get("y"), Some(&2)); /// /// // Invalid data fails the type validation; deserialization would have succeeded otherwise. - /// let invalid_data = "{ x = 1 }"; + /// let invalid_data = "{ x = 1, z = 3 }"; /// assert!( - /// serde_dhall::from_str(invalid_data) - /// .type_annotation(&point_type) - /// .parse::() + /// from_str(invalid_data) + /// .type_annotation(&ty) + /// .parse::>() /// .is_err() /// ); /// # Ok(()) @@ -272,7 +272,7 @@ impl<'a, A> Deserializer<'a, A> { /// Parses the chosen dhall value with the options provided. /// - /// If you enabled static annotations, `T` is additional required to implement [`StaticType`]. + /// If you enabled static annotations, `T` is required to implement [`StaticType`]. /// /// /// # Example @@ -280,7 +280,7 @@ impl<'a, A> Deserializer<'a, A> { /// ``` /// # fn main() -> serde_dhall::Result<()> { /// let data = serde_dhall::from_str("6 * 7").parse::()?; - /// assert_eq!(data, 42) + /// assert_eq!(data, 42); /// # Ok(()) /// # } /// ``` @@ -297,10 +297,10 @@ impl<'a, A> Deserializer<'a, A> { } } -/// Deserialize an instance of type `T` from a string of Dhall text. +/// Deserialize a value from a string of Dhall text. /// -/// This returns a [`Deserializer`] object. Call the [`parse`] method to get the deserialized value, or -/// use other [`Deserializer`] methods to e.g. add type annotations beforehand. +/// This returns a [`Deserializer`] object. Call the [`parse`] method to get the deserialized +/// value, or use other [`Deserializer`] methods to control the deserialization process. /// /// # Example /// @@ -333,10 +333,10 @@ pub fn from_str(s: &str) -> Deserializer<'_, NoAnnot> { Deserializer::from_str(s) } -/// Deserialize an instance of type `T` from a Dhall file. +/// Deserialize a value from a Dhall file. /// -/// This returns a [`Deserializer`] object. Call the [`parse`] method to get the deserialized value, or -/// use other [`Deserializer`] methods to e.g. add type annotations beforehand. +/// This returns a [`Deserializer`] object. Call the [`parse`] method to get the deserialized +/// value, or use other [`Deserializer`] methods to control the deserialization process. /// /// # Example /// diff --git a/serde_dhall/src/static_type.rs b/serde_dhall/src/static_type.rs index 6e76424..f7b72b5 100644 --- a/serde_dhall/src/static_type.rs +++ b/serde_dhall/src/static_type.rs @@ -71,7 +71,9 @@ pub trait StaticType { /// } /// } /// - /// let foo: Foo = serde_dhall::from_str("[ 1, 2 ]").static_type_annotation().parse()?; + /// let foo = serde_dhall::from_str("[ 1, 2 ]") + /// .static_type_annotation() + /// .parse::()?; /// /// assert_eq!(foo.0, vec![1, 2]); /// # Ok(()) diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs index f21e836..7baeee9 100644 --- a/serde_dhall/src/value.rs +++ b/serde_dhall/src/value.rs @@ -28,7 +28,7 @@ pub(crate) enum SimpleValue { Union(String, Option>), } -/// The type of a value that can be decoded by Serde, e.g. `{ x: Bool, y: List Natural }`. +/// The type of a value that can be decoded by `serde_dhall`, e.g. `{ x: Bool, y: List Natural }`. /// /// A `SimpleType` is used when deserializing values to ensure they are of the expected type. /// Rather than letting `serde` handle potential type mismatches, this uses the type-checking @@ -36,35 +36,17 @@ pub(crate) enum SimpleValue { /// mismatch happened. /// /// You would typically not manipulate `SimpleType`s by hand but rather let Rust infer it for your -/// datatype using the [`StaticType`] trait, and methods that require it like -/// [`from_file_static_type`] and [`Options::static_type_annotation`]. If you need to supply a -/// `SimpleType` manually however, you can deserialize it like any other Dhall value using the -/// functions provided by this crate. +/// datatype by deriving the [`StaticType`] trait, and using +/// [`Deserializer::static_type_annotation`]. If you need to supply a `SimpleType` manually, you +/// can either deserialize it like any other Dhall value, or construct it manually. /// /// [`StaticType`]: trait.StaticType.html -/// [`from_file_static_type`]: fn.from_file_static_type.html -/// [`Options::static_type_annotation`]: options/struct.Options.html#method.static_type_annotation +/// [`Deserializer::static_type_annotation`]: options/struct.Deserializer.html#method.static_type_annotation /// /// # Examples /// /// ```rust /// # fn main() -> serde_dhall::Result<()> { -/// use std::collections::HashMap; -/// use serde_dhall::SimpleType; -/// -/// let ty: SimpleType = -/// serde_dhall::from_str("{ x: Natural, y: Natural }").parse()?; -/// -/// let mut map = HashMap::new(); -/// map.insert("x".to_string(), SimpleType::Natural); -/// map.insert("y".to_string(), SimpleType::Natural); -/// assert_eq!(ty, SimpleType::Record(map)); -/// # Ok(()) -/// # } -/// ``` -/// -/// ```rust -/// # fn main() -> serde_dhall::Result<()> { /// use serde_dhall::{SimpleType, StaticType}; /// /// #[derive(StaticType)] @@ -80,6 +62,23 @@ pub(crate) enum SimpleValue { /// # Ok(()) /// # } /// ``` +/// +/// ```rust +/// # fn main() -> serde_dhall::Result<()> { +/// use std::collections::HashMap; +/// use serde_dhall::SimpleType; +/// +/// let ty: SimpleType = +/// serde_dhall::from_str("{ x: Natural, y: Natural }").parse()?; +/// +/// let mut map = HashMap::new(); +/// map.insert("x".to_string(), SimpleType::Natural); +/// map.insert("y".to_string(), SimpleType::Natural); +/// assert_eq!(ty, SimpleType::Record(map)); +/// # Ok(()) +/// # } +/// ``` +/// #[derive(Debug, Clone, PartialEq, Eq)] pub enum SimpleType { /// Corresponds to the Dhall type `Bool` -- cgit v1.2.3 From 820214615547101f8f2b5de209b5189968bddfee Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 5 Apr 2020 17:37:15 +0100 Subject: Fix clippy warnings --- serde_dhall/src/static_type.rs | 6 ++---- serde_dhall/src/value.rs | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'serde_dhall/src') diff --git a/serde_dhall/src/static_type.rs b/serde_dhall/src/static_type.rs index f7b72b5..26c70cd 100644 --- a/serde_dhall/src/static_type.rs +++ b/serde_dhall/src/static_type.rs @@ -117,7 +117,6 @@ where .into_iter() .collect(), ) - .into() } } @@ -135,7 +134,6 @@ where .into_iter() .collect(), ) - .into() } } @@ -144,7 +142,7 @@ where T: StaticType, { fn static_type() -> SimpleType { - SimpleType::Optional(Box::new(T::static_type())).into() + SimpleType::Optional(Box::new(T::static_type())) } } @@ -153,7 +151,7 @@ where T: StaticType, { fn static_type() -> SimpleType { - SimpleType::List(Box::new(T::static_type())).into() + SimpleType::List(Box::new(T::static_type())) } } diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs index 7baeee9..d6631da 100644 --- a/serde_dhall/src/value.rs +++ b/serde_dhall/src/value.rs @@ -226,12 +226,12 @@ impl SimpleType { ExprKind::App(hir(ExprKind::Builtin(Builtin::List)), t.to_hir()) } SimpleType::Record(kts) => ExprKind::RecordType( - kts.into_iter() + kts.iter() .map(|(k, t)| (k.as_str().into(), t.to_hir())) .collect(), ), SimpleType::Union(kts) => ExprKind::UnionType( - kts.into_iter() + kts.iter() .map(|(k, t)| { (k.as_str().into(), t.as_ref().map(|t| t.to_hir())) }) -- cgit v1.2.3