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 ++++++++++++++++------------------------------- serde_dhall/tests/de.rs | 2 + 3 files changed, 48 insertions(+), 73 deletions(-) (limited to 'serde_dhall') 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), } } diff --git a/serde_dhall/tests/de.rs b/serde_dhall/tests/de.rs index 74912dd..4042611 100644 --- a/serde_dhall/tests/de.rs +++ b/serde_dhall/tests/de.rs @@ -51,6 +51,8 @@ fn test_de_typed() { Y(i64), } assert_eq!(parse::("< X | Y: Integer >.X"), Baz::X); + + assert!(from_str_auto_type::("< X | Y: Integer >.Y").is_err()); } #[test] -- cgit v1.3.1 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 --- README.md | 4 ++ dhall_proc_macros/src/derive.rs | 6 +-- serde_dhall/src/lib.rs | 86 +++++++++++++++++++++++++++++------------ serde_dhall/src/static_type.rs | 26 ++++++------- serde_dhall/tests/traits.rs | 4 +- test.dhall | 4 ++ 6 files changed, 88 insertions(+), 42 deletions(-) create mode 100644 test.dhall (limited to 'serde_dhall') diff --git a/README.md b/README.md index ffd55ef..1bcf558 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,10 @@ same name as the corresponding test. ## Changelog +#### [???] + +- Breaking change: use `serde_dhall::Type` type for type-checking instead of `serde_dhall::Value` + #### [0.4.0] - `dhall` now uses the stable Rust toolchain ! diff --git a/dhall_proc_macros/src/derive.rs b/dhall_proc_macros/src/derive.rs index 48626a0..3a4f1aa 100644 --- a/dhall_proc_macros/src/derive.rs +++ b/dhall_proc_macros/src/derive.rs @@ -52,7 +52,7 @@ fn derive_for_struct( let ty = static_type(ty); quote!( (#name.to_owned(), #ty) ) }); - Ok(quote! { ::serde_dhall::value::Value::make_record_type( + Ok(quote! { ::serde_dhall::Type::make_record_type( vec![ #(#entries),* ].into_iter() ) }) } @@ -89,7 +89,7 @@ fn derive_for_enum( }) .collect::>()?; - Ok(quote! { ::serde_dhall::value::Value::make_union_type( + Ok(quote! { ::serde_dhall::Type::make_union_type( vec![ #(#entries),* ].into_iter() ) }) } @@ -165,7 +165,7 @@ pub fn derive_static_type_inner( for #ident #ty_generics #where_clause { fn static_type() -> - ::serde_dhall::value::Value { + ::serde_dhall::Type { #(#assertions)* #get_type } 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() } } diff --git a/serde_dhall/tests/traits.rs b/serde_dhall/tests/traits.rs index 15a91ed..b2fe6e5 100644 --- a/serde_dhall/tests/traits.rs +++ b/serde_dhall/tests/traits.rs @@ -1,8 +1,8 @@ -use serde_dhall::{from_str, StaticType, Value}; +use serde_dhall::{from_str, StaticType, Type}; #[test] fn test_static_type() { - fn parse(s: &str) -> Value { + fn parse(s: &str) -> Type { from_str(s).unwrap() } diff --git a/test.dhall b/test.dhall new file mode 100644 index 0000000..641c219 --- /dev/null +++ b/test.dhall @@ -0,0 +1,4 @@ +let Prelude = + https://prelude.dhall-lang.org/package.dhall sha256:c1b3fc613aabfb64a9e17f6c0d70fe82016a030beedd79851730993e9083fde2 + +in Prelude -- cgit v1.3.1 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 --- dhall/src/lib.rs | 6 ++++- dhall/src/semantics/nze/nir.rs | 48 ++++++++++++++++++++++++++++++++- dhall/src/simple.rs | 52 ++++++++++++++++++++++++++++++++++- serde_dhall/src/lib.rs | 61 +++++++++++++++++++++--------------------- serde_dhall/src/static_type.rs | 8 +++--- 5 files changed, 138 insertions(+), 37 deletions(-) (limited to 'serde_dhall') diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index 0be6db3..25196ba 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -30,7 +30,7 @@ pub type ParsedExpr = Expr; pub type DecodedExpr = Expr; pub type ResolvedExpr = Expr; pub type NormalizedExpr = Expr; -pub use crate::simple::{SValKind, SimpleValue}; +pub use crate::simple::{STyKind, SValKind, SimpleType, SimpleValue}; #[derive(Debug, Clone)] pub struct Parsed(ParsedExpr, ImportLocation); @@ -146,6 +146,10 @@ impl Normalized { pub fn to_simple_value(&self) -> Result { self.0.to_simple_value().ok_or_else(|| self.to_expr()) } + /// Converts a value into a SimpleType. + pub fn to_simple_type(&self) -> Result { + self.0.to_simple_type().ok_or_else(|| self.to_expr()) + } /// Converts a value back to the corresponding Hir expression. pub(crate) fn to_hir(&self) -> Hir { self.0.to_hir_noenv() diff --git a/dhall/src/semantics/nze/nir.rs b/dhall/src/semantics/nze/nir.rs index 9e3644c..4615b39 100644 --- a/dhall/src/semantics/nze/nir.rs +++ b/dhall/src/semantics/nze/nir.rs @@ -11,7 +11,7 @@ use crate::syntax::{ Span, }; use crate::{NormalizedExpr, ToExprOptions}; -use crate::{SValKind, SimpleValue}; +use crate::{STyKind, SValKind, SimpleType, SimpleValue}; /// Stores a possibly unevaluated value. Gets (partially) normalized on-demand, sharing computation /// automatically. Uses a Rc to share computation. @@ -176,6 +176,52 @@ impl Nir { _ => return None, })) } + pub(crate) fn to_simple_type(&self) -> Option { + Some(SimpleType::new(match self.kind() { + NirKind::AppliedBuiltin(BuiltinClosure { b, args, .. }) + if args.is_empty() => + { + match b { + Builtin::Bool => STyKind::Bool, + Builtin::Natural => STyKind::Natural, + Builtin::Integer => STyKind::Integer, + Builtin::Double => STyKind::Double, + Builtin::Text => STyKind::Text, + _ => return None, + } + } + NirKind::AppliedBuiltin(BuiltinClosure { + b: Builtin::Optional, + args, + .. + }) if args.len() == 1 => { + STyKind::Optional(args[0].to_simple_type()?) + } + NirKind::AppliedBuiltin(BuiltinClosure { + b: Builtin::List, + args, + .. + }) if args.len() == 1 => STyKind::List(args[0].to_simple_type()?), + NirKind::RecordType(kts) => STyKind::Record( + kts.iter() + .map(|(k, v)| Some((k.into(), v.to_simple_type()?))) + .collect::>()?, + ), + NirKind::UnionType(kts) => STyKind::Union( + kts.iter() + .map(|(k, v)| { + Some(( + k.into(), + v.as_ref() + .map(|v| Ok(v.to_simple_type()?)) + .transpose()?, + )) + }) + .collect::>()?, + ), + _ => return None, + })) + } pub(crate) fn normalize(&self) { self.0.normalize() diff --git a/dhall/src/simple.rs b/dhall/src/simple.rs index 6457291..9d3846e 100644 --- a/dhall/src/simple.rs +++ b/dhall/src/simple.rs @@ -1,6 +1,7 @@ use std::collections::BTreeMap; -use crate::syntax::LitKind; +use crate::syntax::{Builtin, LitKind}; +use crate::Normalized; #[derive(Debug, Clone, PartialEq, Eq)] pub struct SimpleValue { @@ -17,6 +18,24 @@ pub enum SValKind { Text(String), } +#[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>), +} + impl SimpleValue { pub(crate) fn new(kind: SValKind) -> Self { SimpleValue { @@ -27,3 +46,34 @@ impl SimpleValue { self.kind.as_ref() } } + +impl SimpleType { + pub fn new(kind: STyKind) -> Self { + SimpleType { + kind: Box::new(kind), + } + } + pub fn kind(&self) -> &STyKind { + self.kind.as_ref() + } + pub fn into_normalized(self) -> Normalized { + match *self.kind { + STyKind::Bool => Normalized::make_builtin_type(Builtin::Bool), + STyKind::Natural => Normalized::make_builtin_type(Builtin::Natural), + STyKind::Integer => Normalized::make_builtin_type(Builtin::Integer), + STyKind::Double => Normalized::make_builtin_type(Builtin::Double), + STyKind::Text => Normalized::make_builtin_type(Builtin::Text), + STyKind::Optional(t) => { + Normalized::make_optional_type(t.into_normalized()) + } + STyKind::List(t) => Normalized::make_list_type(t.into_normalized()), + STyKind::Record(kts) => Normalized::make_record_type( + kts.into_iter().map(|(k, t)| (k, t.into_normalized())), + ), + STyKind::Union(kts) => Normalized::make_union_type( + kts.into_iter() + .map(|(k, t)| (k, t.map(|t| t.into_normalized()))), + ), + } + } +} 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.3.1 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 --- dhall/src/lib.rs | 64 +++++++++++++++++++++++++++++++++--- dhall/src/semantics/tck/typecheck.rs | 4 +-- serde_dhall/src/lib.rs | 47 +++++++++++++------------- serde_dhall/src/serde.rs | 4 +-- 4 files changed, 86 insertions(+), 33 deletions(-) (limited to 'serde_dhall') diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index 25196ba..60b7a0e 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -49,11 +49,18 @@ pub struct Typed { } /// A normalized expression. -/// -/// Invariant: the contained expression must be in normal form, #[derive(Debug, Clone)] pub struct Normalized(Nir); +/// A Dhall value. +#[derive(Debug, Clone)] +pub struct Value { + /// Invariant: in normal form + pub(crate) hir: Hir, + simple_val: Option, + simple_ty: Option, +} + /// Controls conversion from `Nir` to `Expr` #[derive(Copy, Clone, Default)] pub(crate) struct ToExprOptions { @@ -99,8 +106,8 @@ impl Resolved { pub fn typecheck(&self) -> Result { Ok(Typed::from_tir(typecheck(&self.0)?)) } - pub fn typecheck_with(self, ty: &Normalized) -> Result { - Ok(Typed::from_tir(typecheck_with(&self.0, ty.to_hir())?)) + pub(crate) fn typecheck_with(self, ty: &Hir) -> Result { + Ok(Typed::from_tir(typecheck_with(&self.0, ty)?)) } /// Converts a value back to the corresponding AST expression. pub fn to_expr(&self) -> ResolvedExpr { @@ -137,6 +144,13 @@ impl Normalized { pub fn encode(&self) -> Result, EncodeError> { binary::encode(&self.to_expr()) } + pub fn to_value(&self) -> Value { + Value { + hir: self.to_hir(), + simple_val: self.0.to_simple_value(), + simple_ty: self.0.to_simple_type(), + } + } /// Converts a value back to the corresponding AST expression. pub fn to_expr(&self) -> NormalizedExpr { @@ -200,6 +214,36 @@ impl Normalized { } } +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 { + let resolved = Parsed::parse_str(s)?.resolve()?; + let typed = match ty { + None => resolved.typecheck()?, + Some(ty) => resolved.typecheck_with(&ty.hir)?, + }; + Ok(typed.normalize().to_value()) + } + + /// Converts a Value into a SimpleValue. + pub fn to_simple_value(&self) -> Option { + self.simple_val.clone() + } + /// Converts a Value into a SimpleType. + pub fn to_simple_type(&self) -> Option { + self.simple_ty.clone() + } + + /// Converts a value back to the corresponding AST expression. + pub(crate) fn to_expr(&self) -> NormalizedExpr { + self.hir.to_expr(ToExprOptions::default()) + } +} + macro_rules! derive_traits_for_wrapper_struct { ($ty:ident) => { impl std::cmp::PartialEq for $ty { @@ -274,3 +318,15 @@ impl Display for Normalized { self.to_expr().fmt(f) } } + +impl Eq for Value {} +impl PartialEq for Value { + fn eq(&self, other: &Self) -> bool { + self.hir == other.hir + } +} +impl Display for Value { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + self.to_expr().fmt(f) + } +} diff --git a/dhall/src/semantics/tck/typecheck.rs b/dhall/src/semantics/tck/typecheck.rs index 173b76d..9fa33f2 100644 --- a/dhall/src/semantics/tck/typecheck.rs +++ b/dhall/src/semantics/tck/typecheck.rs @@ -808,8 +808,8 @@ pub(crate) fn typecheck<'hir>(hir: &'hir Hir) -> Result, TypeError> { /// Like `typecheck`, but additionally checks that the expression's type matches the provided type. pub(crate) fn typecheck_with<'hir>( hir: &'hir Hir, - ty: Hir, + ty: &Hir, ) -> Result, TypeError> { - let ty = typecheck(&ty)?.eval_to_type(&TyEnv::new())?; + let ty = typecheck(ty)?.eval_to_type(&TyEnv::new())?; type_with(&TyEnv::new(), hir, Some(ty)) } 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.3.1 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 --- dhall/src/lib.rs | 42 ++++++++++++++---------------------------- dhall/src/simple.rs | 7 +++++-- dhall/src/tests.rs | 2 +- serde_dhall/src/lib.rs | 2 +- 4 files changed, 21 insertions(+), 32 deletions(-) (limited to 'serde_dhall') diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index 60b7a0e..7300d88 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -26,31 +26,31 @@ use crate::semantics::{ use crate::syntax::binary; use crate::syntax::{Builtin, Expr}; -pub type ParsedExpr = Expr; -pub type DecodedExpr = Expr; -pub type ResolvedExpr = Expr; -pub type NormalizedExpr = Expr; +pub(crate) type ParsedExpr = Expr; +pub(crate) type DecodedExpr = Expr; +pub(crate) type ResolvedExpr = Expr; +pub(crate) type NormalizedExpr = Expr; pub use crate::simple::{STyKind, SValKind, SimpleType, SimpleValue}; #[derive(Debug, Clone)] -pub struct Parsed(ParsedExpr, ImportLocation); +pub(crate) struct Parsed(ParsedExpr, ImportLocation); /// An expression where all imports have been resolved /// /// Invariant: there must be no `Import` nodes or `ImportAlt` operations left. #[derive(Debug, Clone)] -pub struct Resolved(Hir); +pub(crate) struct Resolved(Hir); /// A typed expression #[derive(Debug, Clone)] -pub struct Typed { +pub(crate) struct Typed { hir: Hir, ty: Type, } /// A normalized expression. #[derive(Debug, Clone)] -pub struct Normalized(Nir); +pub(crate) struct Normalized(Nir); /// A Dhall value. #[derive(Debug, Clone)] @@ -81,6 +81,7 @@ impl Parsed { pub fn parse_binary_file(f: &Path) -> Result { parse::parse_binary_file(f) } + #[allow(dead_code)] pub fn parse_binary(data: &[u8]) -> Result { parse::parse_binary(data) } @@ -88,13 +89,6 @@ impl Parsed { pub fn resolve(self) -> Result { resolve::resolve(self) } - pub fn skip_resolve(self) -> Result { - Ok(Resolved(resolve::skip_resolve(&self.0)?)) - } - - pub fn encode(&self) -> Result, EncodeError> { - binary::encode(&self.0) - } /// Converts a value back to the corresponding AST expression. pub fn to_expr(&self) -> ParsedExpr { @@ -156,14 +150,6 @@ impl Normalized { pub fn to_expr(&self) -> NormalizedExpr { self.0.to_expr(ToExprOptions::default()) } - /// Converts a value into a SimpleValue. - pub fn to_simple_value(&self) -> Result { - self.0.to_simple_value().ok_or_else(|| self.to_expr()) - } - /// Converts a value into a SimpleType. - pub fn to_simple_type(&self) -> Result { - self.0.to_simple_type().ok_or_else(|| self.to_expr()) - } /// Converts a value back to the corresponding Hir expression. pub(crate) fn to_hir(&self) -> Hir { self.0.to_hir_noenv() @@ -186,25 +172,25 @@ impl Normalized { Normalized(th) } - pub fn make_builtin_type(b: Builtin) -> Self { + pub(crate) fn make_builtin_type(b: Builtin) -> Self { Normalized::from_nir(Nir::from_builtin(b)) } - pub fn make_optional_type(t: Normalized) -> Self { + pub(crate) fn make_optional_type(t: Normalized) -> Self { Normalized::from_nir( Nir::from_builtin(Builtin::Optional).app(t.to_nir()), ) } - pub fn make_list_type(t: Normalized) -> Self { + pub(crate) fn make_list_type(t: Normalized) -> Self { Normalized::from_nir(Nir::from_builtin(Builtin::List).app(t.to_nir())) } - pub fn make_record_type( + pub(crate) fn make_record_type( kts: impl Iterator, ) -> Self { Normalized::from_kind(NirKind::RecordType( kts.map(|(k, t)| (k.into(), t.into_nir())).collect(), )) } - pub fn make_union_type( + pub(crate) fn make_union_type( kts: impl Iterator)>, ) -> Self { Normalized::from_kind(NirKind::UnionType( diff --git a/dhall/src/simple.rs b/dhall/src/simple.rs index 9d3846e..014053e 100644 --- a/dhall/src/simple.rs +++ b/dhall/src/simple.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use crate::syntax::{Builtin, LitKind}; -use crate::Normalized; +use crate::{Normalized, Value}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct SimpleValue { @@ -56,7 +56,10 @@ impl SimpleType { pub fn kind(&self) -> &STyKind { self.kind.as_ref() } - pub fn into_normalized(self) -> Normalized { + pub fn to_value(&self) -> Value { + self.clone().into_normalized().to_value() + } + fn into_normalized(self) -> Normalized { match *self.kind { STyKind::Bool => Normalized::make_builtin_type(Builtin::Bool), STyKind::Natural => Normalized::make_builtin_type(Builtin::Natural), diff --git a/dhall/src/tests.rs b/dhall/src/tests.rs index a5a278c..659f6f7 100644 --- a/dhall/src/tests.rs +++ b/dhall/src/tests.rs @@ -302,7 +302,7 @@ fn run_test(test: Test) -> Result<()> { expected.compare(expr)?; } ImportFailure(expr, expected) => { - let err = expr.parse()?.resolve().unwrap_err(); + let err = expr.resolve().unwrap_err(); expected.compare_ui(err)?; } TypeInferenceSuccess(expr, expected) => { 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.3.1 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 --- dhall/src/semantics/builtins.rs | 54 ++++++++++++++-------------- dhall/src/semantics/nze/nir.rs | 10 +++--- dhall/src/semantics/nze/normalize.rs | 70 ++++++++++++++++++------------------ dhall/src/semantics/tck/typecheck.rs | 10 +++--- dhall/src/simple.rs | 4 +-- dhall/src/syntax/ast/expr.rs | 6 ++-- dhall/src/syntax/ast/visitor.rs | 2 +- dhall/src/syntax/binary/decode.rs | 16 ++++----- dhall/src/syntax/binary/encode.rs | 10 +++--- dhall/src/syntax/text/parser.rs | 14 ++++---- dhall/src/syntax/text/printer.rs | 6 ++-- serde_dhall/src/serde.rs | 12 +++---- 12 files changed, 107 insertions(+), 107 deletions(-) (limited to 'serde_dhall') diff --git a/dhall/src/semantics/builtins.rs b/dhall/src/semantics/builtins.rs index 803630b..8d17aed 100644 --- a/dhall/src/semantics/builtins.rs +++ b/dhall/src/semantics/builtins.rs @@ -5,7 +5,7 @@ use crate::syntax::map::DupTreeMap; use crate::syntax::Const::Type; use crate::syntax::{ BinOp, Builtin, Const, Expr, ExprKind, InterpolatedText, - InterpolatedTextContents, Label, LitKind, NaiveDouble, Span, UnspannedExpr, + InterpolatedTextContents, Label, NaiveDouble, NumKind, Span, UnspannedExpr, V, }; use crate::Normalized; @@ -241,7 +241,7 @@ macro_rules! make_closure { rc(ExprKind::BinOp( BinOp::NaturalPlus, make_closure!($($v)*), - rc(ExprKind::Lit(LitKind::Natural(1))) + rc(ExprKind::Num(NumKind::Natural(1))) )) }; ([ $($head:tt)* ] # $($tail:tt)*) => {{ @@ -257,8 +257,8 @@ macro_rules! make_closure { #[allow(clippy::cognitive_complexity)] fn apply_builtin(b: Builtin, args: Vec, env: NzEnv) -> NirKind { - use LitKind::{Bool, Double, Integer, Natural}; use NirKind::*; + use NumKind::{Bool, Double, Integer, Natural}; // Small helper enum enum Ret { @@ -277,36 +277,36 @@ fn apply_builtin(b: Builtin, args: Vec, env: NzEnv) -> NirKind { Ret::NirKind(EmptyOptionalLit(t.clone())) } (Builtin::NaturalIsZero, [n]) => match &*n.kind() { - Lit(Natural(n)) => Ret::NirKind(Lit(Bool(*n == 0))), + Num(Natural(n)) => Ret::NirKind(Num(Bool(*n == 0))), _ => Ret::DoneAsIs, }, (Builtin::NaturalEven, [n]) => match &*n.kind() { - Lit(Natural(n)) => Ret::NirKind(Lit(Bool(*n % 2 == 0))), + Num(Natural(n)) => Ret::NirKind(Num(Bool(*n % 2 == 0))), _ => Ret::DoneAsIs, }, (Builtin::NaturalOdd, [n]) => match &*n.kind() { - Lit(Natural(n)) => Ret::NirKind(Lit(Bool(*n % 2 != 0))), + Num(Natural(n)) => Ret::NirKind(Num(Bool(*n % 2 != 0))), _ => Ret::DoneAsIs, }, (Builtin::NaturalToInteger, [n]) => match &*n.kind() { - Lit(Natural(n)) => Ret::NirKind(Lit(Integer(*n as isize))), + Num(Natural(n)) => Ret::NirKind(Num(Integer(*n as isize))), _ => Ret::DoneAsIs, }, (Builtin::NaturalShow, [n]) => match &*n.kind() { - Lit(Natural(n)) => Ret::Nir(Nir::from_text(n)), + Num(Natural(n)) => Ret::Nir(Nir::from_text(n)), _ => Ret::DoneAsIs, }, (Builtin::NaturalSubtract, [a, b]) => match (&*a.kind(), &*b.kind()) { - (Lit(Natural(a)), Lit(Natural(b))) => { - Ret::NirKind(Lit(Natural(if b > a { b - a } else { 0 }))) + (Num(Natural(a)), Num(Natural(b))) => { + Ret::NirKind(Num(Natural(if b > a { b - a } else { 0 }))) } - (Lit(Natural(0)), _) => Ret::Nir(b.clone()), - (_, Lit(Natural(0))) => Ret::NirKind(Lit(Natural(0))), - _ if a == b => Ret::NirKind(Lit(Natural(0))), + (Num(Natural(0)), _) => Ret::Nir(b.clone()), + (_, Num(Natural(0))) => Ret::NirKind(Num(Natural(0))), + _ if a == b => Ret::NirKind(Num(Natural(0))), _ => Ret::DoneAsIs, }, (Builtin::IntegerShow, [n]) => match &*n.kind() { - Lit(Integer(n)) => { + Num(Integer(n)) => { let s = if *n < 0 { n.to_string() } else { @@ -317,23 +317,23 @@ fn apply_builtin(b: Builtin, args: Vec, env: NzEnv) -> NirKind { _ => Ret::DoneAsIs, }, (Builtin::IntegerToDouble, [n]) => match &*n.kind() { - Lit(Integer(n)) => { - Ret::NirKind(Lit(Double(NaiveDouble::from(*n as f64)))) + Num(Integer(n)) => { + Ret::NirKind(Num(Double(NaiveDouble::from(*n as f64)))) } _ => Ret::DoneAsIs, }, (Builtin::IntegerNegate, [n]) => match &*n.kind() { - Lit(Integer(n)) => Ret::NirKind(Lit(Integer(-n))), + Num(Integer(n)) => Ret::NirKind(Num(Integer(-n))), _ => Ret::DoneAsIs, }, (Builtin::IntegerClamp, [n]) => match &*n.kind() { - Lit(Integer(n)) => { - Ret::NirKind(Lit(Natural((*n).try_into().unwrap_or(0)))) + Num(Integer(n)) => { + Ret::NirKind(Num(Natural((*n).try_into().unwrap_or(0)))) } _ => Ret::DoneAsIs, }, (Builtin::DoubleShow, [n]) => match &*n.kind() { - Lit(Double(n)) => Ret::Nir(Nir::from_text(n)), + Num(Double(n)) => Ret::Nir(Nir::from_text(n)), _ => Ret::DoneAsIs, }, (Builtin::TextShow, [v]) => match &*v.kind() { @@ -351,8 +351,8 @@ fn apply_builtin(b: Builtin, args: Vec, env: NzEnv) -> NirKind { _ => Ret::DoneAsIs, }, (Builtin::ListLength, [_, l]) => match &*l.kind() { - EmptyListLit(_) => Ret::NirKind(Lit(Natural(0))), - NEListLit(xs) => Ret::NirKind(Lit(Natural(xs.len()))), + EmptyListLit(_) => Ret::NirKind(Num(Natural(0))), + NEListLit(xs) => Ret::NirKind(Num(Natural(xs.len()))), _ => Ret::DoneAsIs, }, (Builtin::ListHead, [_, l]) => match &*l.kind() { @@ -398,7 +398,7 @@ fn apply_builtin(b: Builtin, args: Vec, env: NzEnv) -> NirKind { let mut kvs = HashMap::new(); kvs.insert( "index".into(), - Nir::from_kind(Lit(Natural(i))), + Nir::from_kind(Num(Natural(i))), ); kvs.insert("value".into(), e.clone()); Nir::from_kind(RecordLit(kvs)) @@ -466,14 +466,14 @@ fn apply_builtin(b: Builtin, args: Vec, env: NzEnv) -> NirKind { λ(x : Natural) -> 1 + var(x) ))) - .app(Lit(Natural(0)).into_nir()), + .app(Num(Natural(0)).into_nir()), ), (Builtin::NaturalFold, [n, t, succ, zero]) => match &*n.kind() { - Lit(Natural(0)) => Ret::Nir(zero.clone()), - Lit(Natural(n)) => { + Num(Natural(0)) => Ret::Nir(zero.clone()), + Num(Natural(n)) => { let fold = Nir::from_builtin(Builtin::NaturalFold) - .app(Lit(Natural(n - 1)).into_nir()) + .app(Num(Natural(n - 1)).into_nir()) .app(t.clone()) .app(succ.clone()) .app(zero.clone()); diff --git a/dhall/src/semantics/nze/nir.rs b/dhall/src/semantics/nze/nir.rs index 4615b39..6f482a8 100644 --- a/dhall/src/semantics/nze/nir.rs +++ b/dhall/src/semantics/nze/nir.rs @@ -7,7 +7,7 @@ use crate::semantics::{ BuiltinClosure, Hir, HirKind, NzEnv, NzVar, TyEnv, Type, Universe, VarEnv, }; use crate::syntax::{ - BinOp, Builtin, Const, ExprKind, InterpolatedTextContents, Label, LitKind, + BinOp, Builtin, Const, ExprKind, InterpolatedTextContents, Label, NumKind, Span, }; use crate::{NormalizedExpr, ToExprOptions}; @@ -75,7 +75,7 @@ pub(crate) enum NirKind { Var(NzVar), Const(Const), - Lit(LitKind), + Num(NumKind), EmptyOptionalLit(Nir), NEOptionalLit(Nir), // EmptyListLit(t) means `[] : List t`, not `[] : t` @@ -145,7 +145,7 @@ impl Nir { } pub(crate) fn to_simple_value(&self) -> Option { Some(SimpleValue::new(match self.kind() { - NirKind::Lit(lit) => SValKind::Lit(lit.clone()), + 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"), @@ -268,7 +268,7 @@ impl Nir { closure.to_hir(venv), ), NirKind::Const(c) => ExprKind::Const(*c), - NirKind::Lit(l) => ExprKind::Lit(l.clone()), + NirKind::Num(l) => ExprKind::Num(l.clone()), NirKind::EmptyOptionalLit(n) => ExprKind::App( Nir::from_builtin(Builtin::OptionalNone).to_hir(venv), n.to_hir(venv), @@ -366,7 +366,7 @@ impl NirKind { pub(crate) fn normalize(&self) { match self { - NirKind::Var(..) | NirKind::Const(_) | NirKind::Lit(_) => {} + NirKind::Var(..) | NirKind::Const(_) | NirKind::Num(_) => {} NirKind::EmptyOptionalLit(tth) | NirKind::EmptyListLit(tth) => { tth.normalize(); diff --git a/dhall/src/semantics/nze/normalize.rs b/dhall/src/semantics/nze/normalize.rs index 79d55e8..c5e66a1 100644 --- a/dhall/src/semantics/nze/normalize.rs +++ b/dhall/src/semantics/nze/normalize.rs @@ -6,7 +6,7 @@ use crate::semantics::{ Binder, BuiltinClosure, Closure, Hir, HirKind, Nir, NirKind, TextLit, }; use crate::syntax::{ - BinOp, Builtin, ExprKind, InterpolatedTextContents, LitKind, + BinOp, Builtin, ExprKind, InterpolatedTextContents, NumKind, }; pub(crate) fn apply_any(f: Nir, a: Nir) -> NirKind { @@ -99,40 +99,40 @@ fn apply_binop<'a>(o: BinOp, x: &'a Nir, y: &'a Nir) -> Option> { NaturalTimes, RecursiveRecordMerge, RecursiveRecordTypeMerge, RightBiasedRecordMerge, TextAppend, }; - use LitKind::{Bool, Natural}; - use NirKind::{EmptyListLit, Lit, NEListLit, RecordLit, RecordType}; + use NirKind::{EmptyListLit, NEListLit, Num, RecordLit, RecordType}; + use NumKind::{Bool, Natural}; Some(match (o, x.kind(), y.kind()) { - (BoolAnd, Lit(Bool(true)), _) => Ret::NirRef(y), - (BoolAnd, _, Lit(Bool(true))) => Ret::NirRef(x), - (BoolAnd, Lit(Bool(false)), _) => Ret::NirKind(Lit(Bool(false))), - (BoolAnd, _, Lit(Bool(false))) => Ret::NirKind(Lit(Bool(false))), + (BoolAnd, Num(Bool(true)), _) => Ret::NirRef(y), + (BoolAnd, _, Num(Bool(true))) => Ret::NirRef(x), + (BoolAnd, Num(Bool(false)), _) => Ret::NirKind(Num(Bool(false))), + (BoolAnd, _, Num(Bool(false))) => Ret::NirKind(Num(Bool(false))), (BoolAnd, _, _) if x == y => Ret::NirRef(x), - (BoolOr, Lit(Bool(true)), _) => Ret::NirKind(Lit(Bool(true))), - (BoolOr, _, Lit(Bool(true))) => Ret::NirKind(Lit(Bool(true))), - (BoolOr, Lit(Bool(false)), _) => Ret::NirRef(y), - (BoolOr, _, Lit(Bool(false))) => Ret::NirRef(x), + (BoolOr, Num(Bool(true)), _) => Ret::NirKind(Num(Bool(true))), + (BoolOr, _, Num(Bool(true))) => Ret::NirKind(Num(Bool(true))), + (BoolOr, Num(Bool(false)), _) => Ret::NirRef(y), + (BoolOr, _, Num(Bool(false))) => Ret::NirRef(x), (BoolOr, _, _) if x == y => Ret::NirRef(x), - (BoolEQ, Lit(Bool(true)), _) => Ret::NirRef(y), - (BoolEQ, _, Lit(Bool(true))) => Ret::NirRef(x), - (BoolEQ, Lit(Bool(x)), Lit(Bool(y))) => Ret::NirKind(Lit(Bool(x == y))), - (BoolEQ, _, _) if x == y => Ret::NirKind(Lit(Bool(true))), - (BoolNE, Lit(Bool(false)), _) => Ret::NirRef(y), - (BoolNE, _, Lit(Bool(false))) => Ret::NirRef(x), - (BoolNE, Lit(Bool(x)), Lit(Bool(y))) => Ret::NirKind(Lit(Bool(x != y))), - (BoolNE, _, _) if x == y => Ret::NirKind(Lit(Bool(false))), + (BoolEQ, Num(Bool(true)), _) => Ret::NirRef(y), + (BoolEQ, _, Num(Bool(true))) => Ret::NirRef(x), + (BoolEQ, Num(Bool(x)), Num(Bool(y))) => Ret::NirKind(Num(Bool(x == y))), + (BoolEQ, _, _) if x == y => Ret::NirKind(Num(Bool(true))), + (BoolNE, Num(Bool(false)), _) => Ret::NirRef(y), + (BoolNE, _, Num(Bool(false))) => Ret::NirRef(x), + (BoolNE, Num(Bool(x)), Num(Bool(y))) => Ret::NirKind(Num(Bool(x != y))), + (BoolNE, _, _) if x == y => Ret::NirKind(Num(Bool(false))), - (NaturalPlus, Lit(Natural(0)), _) => Ret::NirRef(y), - (NaturalPlus, _, Lit(Natural(0))) => Ret::NirRef(x), - (NaturalPlus, Lit(Natural(x)), Lit(Natural(y))) => { - Ret::NirKind(Lit(Natural(x + y))) + (NaturalPlus, Num(Natural(0)), _) => Ret::NirRef(y), + (NaturalPlus, _, Num(Natural(0))) => Ret::NirRef(x), + (NaturalPlus, Num(Natural(x)), Num(Natural(y))) => { + Ret::NirKind(Num(Natural(x + y))) } - (NaturalTimes, Lit(Natural(0)), _) => Ret::NirKind(Lit(Natural(0))), - (NaturalTimes, _, Lit(Natural(0))) => Ret::NirKind(Lit(Natural(0))), - (NaturalTimes, Lit(Natural(1)), _) => Ret::NirRef(y), - (NaturalTimes, _, Lit(Natural(1))) => Ret::NirRef(x), - (NaturalTimes, Lit(Natural(x)), Lit(Natural(y))) => { - Ret::NirKind(Lit(Natural(x * y))) + (NaturalTimes, Num(Natural(0)), _) => Ret::NirKind(Num(Natural(0))), + (NaturalTimes, _, Num(Natural(0))) => Ret::NirKind(Num(Natural(0))), + (NaturalTimes, Num(Natural(1)), _) => Ret::NirRef(y), + (NaturalTimes, _, Num(Natural(1))) => Ret::NirRef(x), + (NaturalTimes, Num(Natural(x)), Num(Natural(y))) => { + Ret::NirKind(Num(Natural(x * y))) } (ListAppend, EmptyListLit(_), _) => Ret::NirRef(y), @@ -212,12 +212,12 @@ fn apply_binop<'a>(o: BinOp, x: &'a Nir, y: &'a Nir) -> Option> { #[allow(clippy::cognitive_complexity)] pub(crate) fn normalize_one_layer(expr: ExprKind, env: &NzEnv) -> NirKind { - use LitKind::Bool; use NirKind::{ - EmptyListLit, EmptyOptionalLit, Lit, NEListLit, NEOptionalLit, + EmptyListLit, EmptyOptionalLit, NEListLit, NEOptionalLit, Num, PartialExpr, RecordLit, RecordType, UnionConstructor, UnionLit, UnionType, }; + use NumKind::Bool; let ret = match expr { ExprKind::Import(..) | ExprKind::Completion(..) => { @@ -235,7 +235,7 @@ pub(crate) fn normalize_one_layer(expr: ExprKind, env: &NzEnv) -> NirKind { ExprKind::Builtin(b) => Ret::Nir(Nir::from_builtin_env(b, env)), ExprKind::Assert(_) => Ret::Expr(expr), ExprKind::App(v, a) => Ret::Nir(v.app(a)), - ExprKind::Lit(l) => Ret::NirKind(Lit(l)), + ExprKind::Num(l) => Ret::NirKind(Num(l)), ExprKind::SomeLit(e) => Ret::NirKind(NEOptionalLit(e)), ExprKind::EmptyListLit(t) => { let arg = match t.kind() { @@ -271,12 +271,12 @@ pub(crate) fn normalize_one_layer(expr: ExprKind, env: &NzEnv) -> NirKind { } ExprKind::BoolIf(ref b, ref e1, ref e2) => { match b.kind() { - Lit(Bool(true)) => Ret::NirRef(e1), - Lit(Bool(false)) => Ret::NirRef(e2), + Num(Bool(true)) => Ret::NirRef(e1), + Num(Bool(false)) => Ret::NirRef(e2), _ => { match (e1.kind(), e2.kind()) { // Simplify `if b then True else False` - (Lit(Bool(true)), Lit(Bool(false))) => Ret::NirRef(b), + (Num(Bool(true)), Num(Bool(false))) => Ret::NirRef(b), _ if e1 == e2 => Ret::NirRef(e1), _ => Ret::Expr(expr), } diff --git a/dhall/src/semantics/tck/typecheck.rs b/dhall/src/semantics/tck/typecheck.rs index 9fa33f2..6951d62 100644 --- a/dhall/src/semantics/tck/typecheck.rs +++ b/dhall/src/semantics/tck/typecheck.rs @@ -9,7 +9,7 @@ use crate::semantics::{ NirKind, Tir, TyEnv, Type, }; use crate::syntax::{ - BinOp, Builtin, Const, ExprKind, InterpolatedTextContents, LitKind, Span, + BinOp, Builtin, Const, ExprKind, InterpolatedTextContents, NumKind, Span, }; fn check_rectymerge( @@ -96,14 +96,14 @@ fn type_one_layer( let t_hir = type_of_builtin(*b); typecheck(&t_hir)?.eval_to_type(env)? } - ExprKind::Lit(LitKind::Bool(_)) => Type::from_builtin(Builtin::Bool), - ExprKind::Lit(LitKind::Natural(_)) => { + ExprKind::Num(NumKind::Bool(_)) => Type::from_builtin(Builtin::Bool), + ExprKind::Num(NumKind::Natural(_)) => { Type::from_builtin(Builtin::Natural) } - ExprKind::Lit(LitKind::Integer(_)) => { + ExprKind::Num(NumKind::Integer(_)) => { Type::from_builtin(Builtin::Integer) } - ExprKind::Lit(LitKind::Double(_)) => { + ExprKind::Num(NumKind::Double(_)) => { Type::from_builtin(Builtin::Double) } ExprKind::TextLit(interpolated) => { diff --git a/dhall/src/simple.rs b/dhall/src/simple.rs index 7e7e459..9c4d6ba 100644 --- a/dhall/src/simple.rs +++ b/dhall/src/simple.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use crate::semantics::{Hir, HirKind}; -use crate::syntax::{Builtin, ExprKind, LitKind, Span}; +use crate::syntax::{Builtin, ExprKind, NumKind, Span}; use crate::Value; #[derive(Debug, Clone, PartialEq, Eq)] @@ -11,7 +11,7 @@ pub struct SimpleValue { #[derive(Debug, Clone, PartialEq, Eq)] pub enum SValKind { - Lit(LitKind), + Num(NumKind), Optional(Option), List(Vec), Record(BTreeMap), diff --git a/dhall/src/syntax/ast/expr.rs b/dhall/src/syntax/ast/expr.rs index b53e6cb..2c1860e 100644 --- a/dhall/src/syntax/ast/expr.rs +++ b/dhall/src/syntax/ast/expr.rs @@ -112,9 +112,9 @@ pub struct Expr { pub type UnspannedExpr = ExprKind; -/// Simple literals +/// Numeric literals #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum LitKind { +pub enum NumKind { /// `True` Bool(bool), /// `1` @@ -132,7 +132,7 @@ pub enum LitKind { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ExprKind { Const(Const), - Lit(LitKind), + Num(NumKind), /// `x` /// `x@n` Var(V), diff --git a/dhall/src/syntax/ast/visitor.rs b/dhall/src/syntax/ast/visitor.rs index c361bc1..0743af4 100644 --- a/dhall/src/syntax/ast/visitor.rs +++ b/dhall/src/syntax/ast/visitor.rs @@ -91,7 +91,7 @@ where Annot(x, t) => Annot(expr!(x)?, expr!(t)?), Const(k) => Const(*k), Builtin(v) => Builtin(*v), - Lit(l) => Lit(l.clone()), + Num(n) => Num(n.clone()), TextLit(t) => TextLit(t.traverse_ref(|e| expr!(e))?), BinOp(o, x, y) => BinOp(*o, expr!(x)?, expr!(y)?), BoolIf(b, t, f) => BoolIf(expr!(b)?, expr!(t)?, expr!(f)?), diff --git a/dhall/src/syntax/binary/decode.rs b/dhall/src/syntax/binary/decode.rs index 2ecd7e0..05d5921 100644 --- a/dhall/src/syntax/binary/decode.rs +++ b/dhall/src/syntax/binary/decode.rs @@ -6,7 +6,7 @@ use crate::error::DecodeError; use crate::syntax; use crate::syntax::{ Expr, ExprKind, FilePath, FilePrefix, Hash, ImportMode, ImportTarget, - Integer, InterpolatedText, Label, LitKind, Natural, Scheme, Span, + Integer, InterpolatedText, Label, Natural, NumKind, Scheme, Span, UnspannedExpr, URL, V, }; use crate::DecodedExpr; @@ -31,8 +31,8 @@ fn cbor_value_to_dhall(data: &cbor::Value) -> Result { String(s) => match Builtin::parse(s) { Some(b) => ExprKind::Builtin(b), None => match s.as_str() { - "True" => Lit(LitKind::Bool(true)), - "False" => Lit(LitKind::Bool(false)), + "True" => Num(NumKind::Bool(true)), + "False" => Num(NumKind::Bool(false)), "Type" => Const(Const::Type), "Kind" => Const(Const::Kind), "Sort" => Const(Const::Sort), @@ -44,8 +44,8 @@ fn cbor_value_to_dhall(data: &cbor::Value) -> Result { }, }, U64(n) => Var(V(Label::from("_"), *n as usize)), - F64(x) => Lit(LitKind::Double((*x).into())), - Bool(b) => Lit(LitKind::Bool(*b)), + F64(x) => Num(NumKind::Double((*x).into())), + Bool(b) => Num(NumKind::Bool(*b)), Array(vec) => match vec.as_slice() { [String(l), U64(n)] => { if l.as_str() == "_" { @@ -224,9 +224,9 @@ fn cbor_value_to_dhall(data: &cbor::Value) -> Result { let z = cbor_value_to_dhall(&z)?; BoolIf(x, y, z) } - [U64(15), U64(x)] => Lit(LitKind::Natural(*x as Natural)), - [U64(16), U64(x)] => Lit(LitKind::Integer(*x as Integer)), - [U64(16), I64(x)] => Lit(LitKind::Integer(*x as Integer)), + [U64(15), U64(x)] => Num(NumKind::Natural(*x as Natural)), + [U64(16), U64(x)] => Num(NumKind::Integer(*x as Integer)), + [U64(16), I64(x)] => Num(NumKind::Integer(*x as Integer)), [U64(18), String(first), rest @ ..] => { TextLit(InterpolatedText::from(( first.clone(), diff --git a/dhall/src/syntax/binary/encode.rs b/dhall/src/syntax/binary/encode.rs index 9e6948e..5974f47 100644 --- a/dhall/src/syntax/binary/encode.rs +++ b/dhall/src/syntax/binary/encode.rs @@ -48,7 +48,7 @@ where use std::iter::once; use syntax::Builtin; use syntax::ExprKind::*; - use syntax::LitKind::*; + use syntax::NumKind::*; use self::Serialize::{RecordDupMap, RecordMap, UnionMap}; fn expr(x: &Expr) -> self::Serialize<'_> { @@ -63,10 +63,10 @@ where match e.as_ref() { Const(c) => ser.serialize_str(&c.to_string()), Builtin(b) => ser.serialize_str(&b.to_string()), - Lit(Bool(b)) => ser.serialize_bool(*b), - Lit(Natural(n)) => ser_seq!(ser; tag(15), U64(*n as u64)), - Lit(Integer(n)) => ser_seq!(ser; tag(16), I64(*n as i64)), - Lit(Double(n)) => { + Num(Bool(b)) => ser.serialize_bool(*b), + Num(Natural(n)) => ser_seq!(ser; tag(15), U64(*n as u64)), + Num(Integer(n)) => ser_seq!(ser; tag(16), I64(*n as i64)), + Num(Double(n)) => { let n: f64 = (*n).into(); ser.serialize_f64(n) } diff --git a/dhall/src/syntax/text/parser.rs b/dhall/src/syntax/text/parser.rs index 03211c7..6f5949f 100644 --- a/dhall/src/syntax/text/parser.rs +++ b/dhall/src/syntax/text/parser.rs @@ -9,7 +9,7 @@ use pest_consume::{match_nodes, Parser}; use crate::syntax::map::{DupTreeMap, DupTreeSet}; use crate::syntax::ExprKind::*; -use crate::syntax::LitKind::*; +use crate::syntax::NumKind::*; use crate::syntax::{ Double, Expr, FilePath, FilePrefix, Hash, ImportMode, ImportTarget, Integer, InterpolatedText, InterpolatedTextContents, Label, NaiveDouble, @@ -135,7 +135,7 @@ fn insert_recordlit_entry(map: &mut BTreeMap, l: Label, e: Expr) { entry.insert(e); } Entry::Occupied(mut entry) => { - let dummy = Expr::new(Lit(Bool(false)), Span::Artificial); + let dummy = Expr::new(Num(Bool(false)), Span::Artificial); let other = entry.insert(dummy); entry.insert(Expr::new( BinOp(RecursiveRecordMerge, other, e), @@ -390,8 +390,8 @@ impl DhallParser { let e = match crate::syntax::Builtin::parse(s) { Some(b) => Builtin(b), None => match s { - "True" => Lit(Bool(true)), - "False" => Lit(Bool(false)), + "True" => Num(Bool(true)), + "False" => Num(Bool(false)), "Type" => Const(crate::syntax::Const::Type), "Kind" => Const(crate::syntax::Const::Kind), "Sort" => Const(crate::syntax::Const::Sort), @@ -924,9 +924,9 @@ impl DhallParser { #[alias(expression, shortcut = true)] fn primitive_expression(input: ParseInput) -> ParseResult { Ok(match_nodes!(input.children(); - [double_literal(n)] => spanned(input, Lit(Double(n))), - [natural_literal(n)] => spanned(input, Lit(Natural(n))), - [integer_literal(n)] => spanned(input, Lit(Integer(n))), + [double_literal(n)] => spanned(input, Num(Double(n))), + [natural_literal(n)] => spanned(input, Num(Natural(n))), + [integer_literal(n)] => spanned(input, Num(Integer(n))), [double_quote_literal(s)] => spanned(input, TextLit(s)), [single_quote_literal(s)] => spanned(input, TextLit(s)), [record_type_or_literal(e)] => spanned(input, e), diff --git a/dhall/src/syntax/text/printer.rs b/dhall/src/syntax/text/printer.rs index 2b7bc2e..378f408 100644 --- a/dhall/src/syntax/text/printer.rs +++ b/dhall/src/syntax/text/printer.rs @@ -201,7 +201,7 @@ impl Display for ExprKind { Var(a) => a.fmt(f)?, Const(k) => k.fmt(f)?, Builtin(v) => v.fmt(f)?, - Lit(a) => a.fmt(f)?, + Num(a) => a.fmt(f)?, TextLit(a) => a.fmt(f)?, RecordType(a) if a.is_empty() => f.write_str("{}")?, RecordType(a) => fmt_list("{ ", ", ", " }", a, f, |(k, t), f| { @@ -240,9 +240,9 @@ impl Display for Expr { } } -impl Display for LitKind { +impl Display for NumKind { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - use LitKind::*; + use NumKind::*; match self { Bool(true) => f.write_str("True")?, Bool(false) => f.write_str("False")?, 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.3.1 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 --- dhall/src/simple.rs | 2 +- serde_dhall/src/lib.rs | 65 ++++++++++++++++++++++++------------------------ serde_dhall/src/serde.rs | 3 +-- 3 files changed, 34 insertions(+), 36 deletions(-) (limited to 'serde_dhall') diff --git a/dhall/src/simple.rs b/dhall/src/simple.rs index 9c4d6ba..32c9c79 100644 --- a/dhall/src/simple.rs +++ b/dhall/src/simple.rs @@ -12,11 +12,11 @@ pub struct SimpleValue { #[derive(Debug, Clone, PartialEq, Eq)] pub enum SValKind { Num(NumKind), + Text(String), Optional(Option), List(Vec), Record(BTreeMap), Union(String, Option), - Text(String), } #[derive(Debug, Clone, PartialEq, Eq)] 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.3.1 From 1a0929b52af57d5963dd9da9e5cf85ffbed3a8f7 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 13 Mar 2020 20:53:23 +0000 Subject: Reorganize serde modules --- dhall_proc_macros/src/derive.rs | 6 +- serde_dhall/src/error.rs | 30 +++++ serde_dhall/src/lib.rs | 264 ++++++++++------------------------------ serde_dhall/src/serde.rs | 4 +- serde_dhall/src/simple.rs | 81 ++++++++++++ serde_dhall/tests/de.rs | 4 +- serde_dhall/tests/traits.rs | 2 +- 7 files changed, 182 insertions(+), 209 deletions(-) create mode 100644 serde_dhall/src/error.rs create mode 100644 serde_dhall/src/simple.rs (limited to 'serde_dhall') diff --git a/dhall_proc_macros/src/derive.rs b/dhall_proc_macros/src/derive.rs index 3a4f1aa..4885b49 100644 --- a/dhall_proc_macros/src/derive.rs +++ b/dhall_proc_macros/src/derive.rs @@ -52,7 +52,7 @@ fn derive_for_struct( let ty = static_type(ty); quote!( (#name.to_owned(), #ty) ) }); - Ok(quote! { ::serde_dhall::Type::make_record_type( + Ok(quote! { ::serde_dhall::simple::Type::make_record_type( vec![ #(#entries),* ].into_iter() ) }) } @@ -89,7 +89,7 @@ fn derive_for_enum( }) .collect::>()?; - Ok(quote! { ::serde_dhall::Type::make_union_type( + Ok(quote! { ::serde_dhall::simple::Type::make_union_type( vec![ #(#entries),* ].into_iter() ) }) } @@ -165,7 +165,7 @@ pub fn derive_static_type_inner( for #ident #ty_generics #where_clause { fn static_type() -> - ::serde_dhall::Type { + ::serde_dhall::simple::Type { #(#assertions)* #get_type } 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)) + } +} diff --git a/serde_dhall/tests/de.rs b/serde_dhall/tests/de.rs index 4042611..b201e4f 100644 --- a/serde_dhall/tests/de.rs +++ b/serde_dhall/tests/de.rs @@ -3,7 +3,7 @@ use serde_dhall::{from_str, from_str_auto_type, StaticType}; #[test] fn test_de_typed() { - fn parse(s: &str) -> T { + fn parse(s: &str) -> T { from_str_auto_type(s).unwrap() } @@ -57,7 +57,7 @@ fn test_de_typed() { #[test] fn test_de_untyped() { - fn parse(s: &str) -> T { + fn parse(s: &str) -> T { from_str(s).unwrap() } diff --git a/serde_dhall/tests/traits.rs b/serde_dhall/tests/traits.rs index b2fe6e5..f3c6f05 100644 --- a/serde_dhall/tests/traits.rs +++ b/serde_dhall/tests/traits.rs @@ -1,4 +1,4 @@ -use serde_dhall::{from_str, StaticType, Type}; +use serde_dhall::{from_str, simple::Type, StaticType}; #[test] fn test_static_type() { -- cgit v1.3.1 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 --- dhall/src/lib.rs | 66 +------------ dhall/src/semantics/tck/typecheck.rs | 5 +- dhall/src/simple.rs | 163 ------------------------------- 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 ++++++++++++++ 8 files changed, 257 insertions(+), 248 deletions(-) delete mode 100644 dhall/src/simple.rs create mode 100644 serde_dhall/src/value.rs (limited to 'serde_dhall') diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index 34a9bac..0f4b623 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -9,7 +9,6 @@ mod tests; pub mod error; pub mod semantics; -pub mod simple; pub mod syntax; use std::fmt::Display; @@ -23,8 +22,6 @@ use crate::semantics::resolve::ImportLocation; use crate::semantics::{typecheck, typecheck_with, Hir, Nir, Tir, Type}; use crate::syntax::Expr; -pub use crate::simple::{STyKind, SValKind, SimpleType, SimpleValue}; - #[derive(Debug, Clone)] pub struct Parsed(Expr, ImportLocation); @@ -47,16 +44,6 @@ pub struct Typed { #[derive(Debug, Clone)] pub struct Normalized(Nir); -/// A Dhall value. -#[derive(Debug, Clone)] -pub struct Value { - /// Invariant: in normal form - pub hir: Hir, - /// Cached conversions because they are annoying to construct from Hir. - pub as_simple_val: Option, - pub as_simple_ty: Option, -} - /// Controls conversion from `Nir` to `Expr` #[derive(Copy, Clone, Default)] pub struct ToExprOptions { @@ -131,14 +118,6 @@ impl Typed { } impl Normalized { - pub fn to_value(&self) -> Value { - Value { - hir: self.to_hir(), - as_simple_val: SimpleValue::from_nir(&self.0), - as_simple_ty: SimpleType::from_nir(&self.0), - } - } - /// Converts a value back to the corresponding AST expression. pub fn to_expr(&self) -> Expr { self.0.to_expr(ToExprOptions::default()) @@ -147,42 +126,15 @@ impl Normalized { pub fn to_hir(&self) -> Hir { self.0.to_hir_noenv() } + pub fn as_nir(&self) -> &Nir { + &self.0 + } /// Converts a value back to the corresponding AST expression, alpha-normalizing in the process. pub fn to_expr_alpha(&self) -> Expr { self.0.to_expr(ToExprOptions { alpha: true }) } } -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 { - let resolved = Parsed::parse_str(s)?.resolve()?; - let typed = match ty { - None => resolved.typecheck()?, - Some(ty) => resolved.typecheck_with(&ty.hir)?, - }; - Ok(typed.normalize().to_value()) - } - - /// 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(ToExprOptions::default()) - } -} - macro_rules! derive_traits_for_wrapper_struct { ($ty:ident) => { impl std::cmp::PartialEq for $ty { @@ -246,15 +198,3 @@ impl Display for Normalized { self.to_expr().fmt(f) } } - -impl Eq for Value {} -impl PartialEq for Value { - fn eq(&self, other: &Self) -> bool { - self.hir == other.hir - } -} -impl Display for Value { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - self.to_expr().fmt(f) - } -} diff --git a/dhall/src/semantics/tck/typecheck.rs b/dhall/src/semantics/tck/typecheck.rs index 205185f..c3334b5 100644 --- a/dhall/src/semantics/tck/typecheck.rs +++ b/dhall/src/semantics/tck/typecheck.rs @@ -57,10 +57,7 @@ pub fn mkerr(msg: S) -> Result { Err(TypeError::new(TypeMessage::Custom(msg.to_string()))) } -pub fn mk_span_err( - span: Span, - msg: S, -) -> Result { +pub fn mk_span_err(span: Span, msg: S) -> Result { mkerr( ErrorBuilder::new(msg.to_string()) .span_err(span, msg.to_string()) diff --git a/dhall/src/simple.rs b/dhall/src/simple.rs deleted file mode 100644 index c00380c..0000000 --- a/dhall/src/simple.rs +++ /dev/null @@ -1,163 +0,0 @@ -use std::collections::BTreeMap; - -use crate::semantics::{Hir, HirKind, Nir, NirKind}; -use crate::syntax::{Builtin, ExprKind, NumKind, Span}; -use crate::Value; - -#[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>), -} - -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) -> Value { - 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(), - ), - }) - } -} 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.3.1 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 --- README.md | 2 +- dhall_proc_macros/src/derive.rs | 19 ++-- 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 +++--- 7 files changed, 132 insertions(+), 169 deletions(-) (limited to 'serde_dhall') diff --git a/README.md b/README.md index 1bcf558..2cb3c0b 100644 --- a/README.md +++ b/README.md @@ -169,7 +169,7 @@ same name as the corresponding test. #### [???] -- Breaking change: use `serde_dhall::Type` type for type-checking instead of `serde_dhall::Value` +- Breaking change: use `serde_dhall::simple::Type` type for type-checking instead of `serde_dhall::Value` #### [0.4.0] diff --git a/dhall_proc_macros/src/derive.rs b/dhall_proc_macros/src/derive.rs index 4885b49..f7edf3a 100644 --- a/dhall_proc_macros/src/derive.rs +++ b/dhall_proc_macros/src/derive.rs @@ -52,9 +52,11 @@ fn derive_for_struct( let ty = static_type(ty); quote!( (#name.to_owned(), #ty) ) }); - Ok(quote! { ::serde_dhall::simple::Type::make_record_type( - vec![ #(#entries),* ].into_iter() - ) }) + Ok(quote! { + ::serde_dhall::simple::TyKind::Record( + vec![ #(#entries),* ].into_iter().collect() + ).into() + }) } fn derive_for_enum( @@ -89,9 +91,11 @@ fn derive_for_enum( }) .collect::>()?; - Ok(quote! { ::serde_dhall::simple::Type::make_union_type( - vec![ #(#entries),* ].into_iter() - ) }) + Ok(quote! { + ::serde_dhall::simple::TyKind::Union( + vec![ #(#entries),* ].into_iter().collect() + ).into() + }) } pub fn derive_static_type_inner( @@ -164,8 +168,7 @@ pub fn derive_static_type_inner( impl #impl_generics ::serde_dhall::StaticType for #ident #ty_generics #where_clause { - fn static_type() -> - ::serde_dhall::simple::Type { + fn static_type() -> ::serde_dhall::simple::Type { #(#assertions)* #get_type } 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.3.1 From 110bfda418b7f1444a2d32b28c515c172a6572a2 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 19 Mar 2020 17:43:34 +0000 Subject: Fix versions of dependent local crates --- serde_dhall/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'serde_dhall') diff --git a/serde_dhall/Cargo.toml b/serde_dhall/Cargo.toml index b0030e9..0ec792a 100644 --- a/serde_dhall/Cargo.toml +++ b/serde_dhall/Cargo.toml @@ -11,5 +11,5 @@ edition = "2018" [dependencies] serde = { version = "1.0", features = ["derive"] } -dhall = { version = "0.4.0", path = "../dhall" } -dhall_proc_macros = { version = "0.4.0", path = "../dhall_proc_macros" } +dhall = { version = "=0.4.0", path = "../dhall" } +dhall_proc_macros = { version = "=0.4.0", path = "../dhall_proc_macros" } -- cgit v1.3.1 From 85e2e8ee5e83dadd05b6974ba6c951350cb97a61 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 21 Mar 2020 21:30:30 +0000 Subject: Introduce option builder --- Cargo.lock | 2 + README.md | 1 + dhall/Cargo.toml | 2 +- dhall/src/lib.rs | 3 + dhall/src/semantics/builtins.rs | 6 +- dhall/src/semantics/resolve/resolve.rs | 10 ++- serde_dhall/Cargo.toml | 2 + serde_dhall/src/lib.rs | 16 ++-- serde_dhall/src/options.rs | 138 +++++++++++++++++++++++++++++++++ serde_dhall/src/value.rs | 32 +++----- 10 files changed, 173 insertions(+), 39 deletions(-) create mode 100644 serde_dhall/src/options.rs (limited to 'serde_dhall') diff --git a/Cargo.lock b/Cargo.lock index b4230ff..31638a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -964,7 +964,9 @@ version = "0.4.0" dependencies = [ "dhall 0.4.0", "dhall_proc_macros 0.4.0", + "reqwest 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/README.md b/README.md index 2cb3c0b..cce65e3 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,7 @@ same name as the corresponding test. #### [???] +- Add `serde_dhall::options` for finer control over Dhall behavior - Breaking change: use `serde_dhall::simple::Type` type for type-checking instead of `serde_dhall::Value` #### [0.4.0] diff --git a/dhall/Cargo.toml b/dhall/Cargo.toml index 0ffe78b..02ec1e7 100644 --- a/dhall/Cargo.toml +++ b/dhall/Cargo.toml @@ -22,7 +22,7 @@ reqwest = { version = "0.10", features = ["blocking"] } serde = "1.0" serde_cbor = "0.9.0" smallvec = "1.0.0" -url = "2.1.1" +url = "2.1" [dev-dependencies] pretty_assertions = "0.6.1" diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index 0f4b623..adca2de 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -72,6 +72,9 @@ impl Parsed { pub fn resolve(self) -> Result { resolve::resolve(self) } + pub fn skip_resolve(self) -> Result { + resolve::skip_resolve(self) + } /// Converts a value back to the corresponding AST expression. pub fn to_expr(&self) -> Expr { diff --git a/dhall/src/semantics/builtins.rs b/dhall/src/semantics/builtins.rs index fa22bd0..6007a92 100644 --- a/dhall/src/semantics/builtins.rs +++ b/dhall/src/semantics/builtins.rs @@ -1,5 +1,5 @@ use crate::semantics::{ - skip_resolve, typecheck, Hir, HirKind, Nir, NirKind, NzEnv, VarEnv, + skip_resolve_expr, typecheck, Hir, HirKind, Nir, NirKind, NzEnv, VarEnv, }; use crate::syntax::map::DupTreeMap; use crate::syntax::Const::Type; @@ -189,7 +189,7 @@ pub fn type_of_builtin(b: Builtin) -> Hir { forall (A: Type) -> Optional A ), }; - skip_resolve(&expr).unwrap() + skip_resolve_expr(&expr).unwrap() } // Ad-hoc macro to help construct closures @@ -254,7 +254,7 @@ fn apply_builtin(b: Builtin, args: Vec, env: NzEnv) -> NirKind { DoneAsIs, } let make_closure = |e| { - typecheck(&skip_resolve(&e).unwrap()) + typecheck(&skip_resolve_expr(&e).unwrap()) .unwrap() .eval(env.clone()) }; diff --git a/dhall/src/semantics/resolve/resolve.rs b/dhall/src/semantics/resolve/resolve.rs index 07bd06c..fe3f3a9 100644 --- a/dhall/src/semantics/resolve/resolve.rs +++ b/dhall/src/semantics/resolve/resolve.rs @@ -227,7 +227,7 @@ fn resolve_one_import( } ImportMode::Location => { let expr = location.into_location(); - let hir = skip_resolve(&expr)?; + let hir = skip_resolve_expr(&expr)?; let ty = hir.typecheck_noenv()?.ty().clone(); Ok((hir, ty)) } @@ -333,12 +333,18 @@ pub fn resolve(parsed: Parsed) -> Result { resolve_with_env(&mut ImportEnv::new(), parsed) } -pub fn skip_resolve(expr: &Expr) -> Result { +pub fn skip_resolve_expr(expr: &Expr) -> Result { traverse_resolve_expr(&mut NameEnv::new(), expr, &mut |import| { Err(ImportError::UnexpectedImport(import).into()) }) } +pub fn skip_resolve(parsed: Parsed) -> Result { + let Parsed(expr, _) = parsed; + let resolved =skip_resolve_expr(&expr)?; + Ok(Resolved(resolved)) +} + pub trait Canonicalize { fn canonicalize(&self) -> Self; } diff --git a/serde_dhall/Cargo.toml b/serde_dhall/Cargo.toml index 0ec792a..71a9b02 100644 --- a/serde_dhall/Cargo.toml +++ b/serde_dhall/Cargo.toml @@ -13,3 +13,5 @@ edition = "2018" serde = { version = "1.0", features = ["derive"] } dhall = { version = "=0.4.0", path = "../dhall" } dhall_proc_macros = { version = "=0.4.0", path = "../dhall_proc_macros" } +reqwest = { version = "0.10", features = ["blocking"] } +url = "2.1" 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.3.1 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 --- Cargo.lock | 7 +++++++ README.md | 2 +- serde_dhall/Cargo.toml | 3 +++ serde_dhall/src/lib.rs | 3 +++ 4 files changed, 14 insertions(+), 1 deletion(-) (limited to 'serde_dhall') diff --git a/Cargo.lock b/Cargo.lock index 31638a3..4b80022 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -173,6 +173,11 @@ dependencies = [ "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "dtoa" version = "0.4.5" @@ -964,6 +969,7 @@ version = "0.4.0" dependencies = [ "dhall 0.4.0", "dhall_proc_macros 0.4.0", + "doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1331,6 +1337,7 @@ dependencies = [ "checksum ctor 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "47c5e5ac752e18207b12e16b10631ae5f7f68f8805f335f9b817ead83d9ffce1" "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +"checksum doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" "checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28" diff --git a/README.md b/README.md index cce65e3..8afcac6 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ use std::collections::BTreeMap; let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }"; // Deserialize it to a Rust type. -let deserialized_map: BTreeMap = serde_dhall::from_str(data)?; +let deserialized_map: BTreeMap = serde_dhall::from_str(data).unwrap(); let mut expected_map = BTreeMap::new(); expected_map.insert("x".to_string(), 1); diff --git a/serde_dhall/Cargo.toml b/serde_dhall/Cargo.toml index 71a9b02..4e3cce7 100644 --- a/serde_dhall/Cargo.toml +++ b/serde_dhall/Cargo.toml @@ -15,3 +15,6 @@ dhall = { version = "=0.4.0", path = "../dhall" } dhall_proc_macros = { version = "=0.4.0", path = "../dhall_proc_macros" } reqwest = { version = "0.10", features = ["blocking"] } url = "2.1" + +[dev-dependencies] +doc-comment = "0.3" 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.3.1 From 196491a37a9ab2869ab6d76e1417a85a9e033dd4 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 21 Mar 2020 21:56:09 +0000 Subject: Ensure version numbers are kept in sync --- Cargo.lock | 144 +++++++++++++++++++++++++++++ dhall/Cargo.toml | 1 + dhall/tests/version_numbers.rs | 4 + dhall_proc_macros/Cargo.toml | 3 + dhall_proc_macros/tests/version_numbers.rs | 4 + serde_dhall/Cargo.toml | 1 + serde_dhall/tests/version_numbers.rs | 14 +++ 7 files changed, 171 insertions(+) create mode 100644 dhall/tests/version_numbers.rs create mode 100644 dhall_proc_macros/tests/version_numbers.rs create mode 100644 serde_dhall/tests/version_numbers.rs (limited to 'serde_dhall') diff --git a/Cargo.lock b/Cargo.lock index 4b80022..f5cb0cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,14 @@ dependencies = [ "pretty 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "aho-corasick" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "annotate-snippets" version = "0.7.0" @@ -147,6 +155,7 @@ dependencies = [ "serde_cbor 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "version-sync 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -158,6 +167,7 @@ dependencies = [ "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "version-sync 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -387,6 +397,16 @@ dependencies = [ "tokio-tls 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "idna" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "idna" version = "0.2.0" @@ -639,6 +659,11 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "percent-encoding" version = "2.1.0" @@ -772,6 +797,14 @@ dependencies = [ "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "proc-macro2" version = "1.0.9" @@ -780,6 +813,24 @@ dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pulldown-cmark" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quote" version = "1.0.3" @@ -830,6 +881,22 @@ name = "redox_syscall" version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "regex" +version = "1.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.17" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "remove_dir_all" version = "0.5.2" @@ -935,6 +1002,11 @@ name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "semver-parser" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "serde" version = "1.0.104" @@ -973,6 +1045,7 @@ dependencies = [ "reqwest 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "version-sync 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1022,6 +1095,16 @@ name = "static_assertions" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "1.0.16" @@ -1045,6 +1128,14 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "time" version = "0.1.42" @@ -1093,6 +1184,14 @@ dependencies = [ "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "toml" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tower-service" version = "0.3.0" @@ -1142,11 +1241,26 @@ dependencies = [ "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "url" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "url" version = "2.1.1" @@ -1162,6 +1276,21 @@ name = "vcpkg" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "version-sync" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "pulldown-cmark 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "semver-parser 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "version_check" version = "0.9.1" @@ -1318,6 +1447,7 @@ dependencies = [ [metadata] "checksum abnf 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "47feb9fbcef700639ef28e04ca2a87eab8161a01a075ee227b15c90143805462" +"checksum aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" "checksum annotate-snippets 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aba2d96b8c8b5e656ad7ffb0d09f57772f10a1db74c8d23fca0ec695b38a4047" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" @@ -1364,6 +1494,7 @@ dependencies = [ "checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" "checksum hyper 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e7b15203263d1faa615f9337d79c1d37959439dc46c2b4faab33286fadc2a1c5" "checksum hyper-tls 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3adcd308402b9553630734e9c36b77a7e48b3821251ca2493e8cd596763aafaa" +"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" "checksum indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" "checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" @@ -1394,6 +1525,7 @@ dependencies = [ "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" "checksum openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)" = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986" "checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" +"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" "checksum pest_consume 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3f016ccda869edc16802ba5feb6c4467038132067fbf914117f851ec6ac5e3a1" @@ -1410,13 +1542,18 @@ dependencies = [ "checksum pretty 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f60c0d9f6fc88ecdd245d90c1920ff76a430ab34303fc778d33b1d0a4c3bf6d3" "checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" "checksum proc-macro-hack 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)" = "f918f2b601f93baa836c1c2945faef682ba5b6d4828ecb45eeb7cc3c71b811b4" +"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" +"checksum pulldown-cmark 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d1b74cc784b038a9921fd1a48310cc2e238101aa8ae0b94201e2d85121dd68b5" +"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" "checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" "checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" "checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" "checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum regex 1.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3" +"checksum regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum reqwest 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b81e49ddec5109a9dcfc5f2a317ff53377c915e9ae9d4f2fb50914b85614e2" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" @@ -1427,6 +1564,7 @@ dependencies = [ "checksum security-framework-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "06fd2f23e31ef68dd2328cc383bd493142e46107a3a0e24f7d734e3f3b80fe4c" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum semver-parser 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b46e1121e8180c12ff69a742aabc4f310542b6ccb69f1691689ac17fdf8618aa" "checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" "checksum serde_cbor 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45cd6d95391b16cd57e88b68be41d504183b7faae22030c0cc3b3f73dd57b2fd" "checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" @@ -1436,12 +1574,15 @@ dependencies = [ "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" "checksum static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3" +"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" "checksum syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "0fa5e81d6bc4e67fe889d5783bd2a128ab2e0cfa487e0be16b6a8d177b101616" "checksum tokio-tls 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7bde02a3a5291395f59b06ec6945a3077602fac2b07eeeaf0dee2122f3619828" "checksum tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930" +"checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" "checksum tower-service 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" "checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" "checksum typed-arena 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d" @@ -1450,9 +1591,12 @@ dependencies = [ "checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" "checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" "checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" +"checksum version-sync 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "844f3d3a2467f15cb999f5af7775f6e108ac546d4f42365832ed4c755404f806" "checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" "checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" "checksum want 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" diff --git a/dhall/Cargo.toml b/dhall/Cargo.toml index 02ec1e7..bb60d9e 100644 --- a/dhall/Cargo.toml +++ b/dhall/Cargo.toml @@ -26,6 +26,7 @@ url = "2.1" [dev-dependencies] pretty_assertions = "0.6.1" +version-sync = "0.8" [build-dependencies] walkdir = "2" diff --git a/dhall/tests/version_numbers.rs b/dhall/tests/version_numbers.rs new file mode 100644 index 0000000..9f1d04a --- /dev/null +++ b/dhall/tests/version_numbers.rs @@ -0,0 +1,4 @@ +#[test] +fn test_html_root_url() { + version_sync::assert_html_root_url_updated!("src/lib.rs"); +} diff --git a/dhall_proc_macros/Cargo.toml b/dhall_proc_macros/Cargo.toml index 6de2850..48b55e8 100644 --- a/dhall_proc_macros/Cargo.toml +++ b/dhall_proc_macros/Cargo.toml @@ -17,3 +17,6 @@ itertools = "0.9.0" quote = "1.0" proc-macro2 = "1.0" syn = "1.0" + +[dev-dependencies] +version-sync = "0.8" diff --git a/dhall_proc_macros/tests/version_numbers.rs b/dhall_proc_macros/tests/version_numbers.rs new file mode 100644 index 0000000..9f1d04a --- /dev/null +++ b/dhall_proc_macros/tests/version_numbers.rs @@ -0,0 +1,4 @@ +#[test] +fn test_html_root_url() { + version_sync::assert_html_root_url_updated!("src/lib.rs"); +} diff --git a/serde_dhall/Cargo.toml b/serde_dhall/Cargo.toml index 4e3cce7..7fd7b3e 100644 --- a/serde_dhall/Cargo.toml +++ b/serde_dhall/Cargo.toml @@ -18,3 +18,4 @@ url = "2.1" [dev-dependencies] doc-comment = "0.3" +version-sync = "0.8" diff --git a/serde_dhall/tests/version_numbers.rs b/serde_dhall/tests/version_numbers.rs new file mode 100644 index 0000000..97254a9 --- /dev/null +++ b/serde_dhall/tests/version_numbers.rs @@ -0,0 +1,14 @@ +#[test] +fn test_readme_deps() { + version_sync::assert_markdown_deps_updated!("../README.md"); +} + +#[test] +fn test_html_root_url() { + version_sync::assert_html_root_url_updated!("src/lib.rs"); +} + +#[test] +fn test_readme_mentions_version() { + version_sync::assert_contains_regex!("../README.md", "^#### \\[{version}\\]"); +} -- cgit v1.3.1 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 --- Cargo.lock | 2 +- README.md | 2 +- abnf_to_pest/Cargo.toml | 2 +- dhall/src/semantics/resolve/resolve.rs | 2 +- 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 +- serde_dhall/tests/de.rs | 6 +- serde_dhall/tests/version_numbers.rs | 5 +- tests_buffer | 4 ++ 13 files changed, 259 insertions(+), 183 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') diff --git a/Cargo.lock b/Cargo.lock index f5cb0cc..e5a25e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,7 +14,7 @@ version = "0.2.0" dependencies = [ "abnf 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "pretty 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/README.md b/README.md index 8afcac6..efe4c91 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ same name as the corresponding test. #### [???] - Add `serde_dhall::options` for finer control over Dhall behavior -- Breaking change: use `serde_dhall::simple::Type` type for type-checking instead of `serde_dhall::Value` +- Breaking change: reworked most of the `serde_dhall` api #### [0.4.0] diff --git a/abnf_to_pest/Cargo.toml b/abnf_to_pest/Cargo.toml index 780010e..1567512 100644 --- a/abnf_to_pest/Cargo.toml +++ b/abnf_to_pest/Cargo.toml @@ -14,5 +14,5 @@ doctest = false [dependencies] abnf = "0.6.0" indexmap = "1.0.2" -itertools = "0.8.0" +itertools = "0.9.0" pretty = "0.5.2" diff --git a/dhall/src/semantics/resolve/resolve.rs b/dhall/src/semantics/resolve/resolve.rs index fe3f3a9..7745e0b 100644 --- a/dhall/src/semantics/resolve/resolve.rs +++ b/dhall/src/semantics/resolve/resolve.rs @@ -341,7 +341,7 @@ pub fn skip_resolve_expr(expr: &Expr) -> Result { pub fn skip_resolve(parsed: Parsed) -> Result { let Parsed(expr, _) = parsed; - let resolved =skip_resolve_expr(&expr)?; + let resolved = skip_resolve_expr(&expr)?; Ok(Resolved(resolved)) } 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)] diff --git a/serde_dhall/tests/de.rs b/serde_dhall/tests/de.rs index b201e4f..f28b265 100644 --- a/serde_dhall/tests/de.rs +++ b/serde_dhall/tests/de.rs @@ -1,10 +1,10 @@ use serde::Deserialize; -use serde_dhall::{from_str, from_str_auto_type, StaticType}; +use serde_dhall::{from_str, from_str_static_type, StaticType}; #[test] fn test_de_typed() { fn parse(s: &str) -> T { - from_str_auto_type(s).unwrap() + from_str_static_type(s).unwrap() } assert_eq!(parse::("True"), true); @@ -52,7 +52,7 @@ fn test_de_typed() { } assert_eq!(parse::("< X | Y: Integer >.X"), Baz::X); - assert!(from_str_auto_type::("< X | Y: Integer >.Y").is_err()); + assert!(from_str_static_type::("< X | Y: Integer >.Y").is_err()); } #[test] diff --git a/serde_dhall/tests/version_numbers.rs b/serde_dhall/tests/version_numbers.rs index 97254a9..8307e47 100644 --- a/serde_dhall/tests/version_numbers.rs +++ b/serde_dhall/tests/version_numbers.rs @@ -10,5 +10,8 @@ fn test_html_root_url() { #[test] fn test_readme_mentions_version() { - version_sync::assert_contains_regex!("../README.md", "^#### \\[{version}\\]"); + version_sync::assert_contains_regex!( + "../README.md", + "^#### \\[{version}\\]" + ); } diff --git a/tests_buffer b/tests_buffer index 74e76bc..d07ddbd 100644 --- a/tests_buffer +++ b/tests_buffer @@ -7,6 +7,10 @@ x.({ a : Bool, b }) x.({ a }) x.{ a : Bool } s/QuotedVariable/VariableQuoted/ +From https://github.com/dhall-lang/dhall-lang/issues/280 : + "${ not_really_an_expression ;-) }" + ''${ not_an_expression ;-) }'' + {- {- -} 1 import: failure/ -- cgit v1.3.1 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') 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.3.1 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') 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.3.1 From a70922c6c6beb58a45da80f15576b54fb915ec28 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 22 Mar 2020 22:11:00 +0000 Subject: Rework SimpleType --- dhall_proc_macros/src/derive.rs | 10 +-- 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 +- serde_dhall/tests/traits.rs | 4 +- 8 files changed, 125 insertions(+), 89 deletions(-) (limited to 'serde_dhall') diff --git a/dhall_proc_macros/src/derive.rs b/dhall_proc_macros/src/derive.rs index f7edf3a..e484ec6 100644 --- a/dhall_proc_macros/src/derive.rs +++ b/dhall_proc_macros/src/derive.rs @@ -53,9 +53,9 @@ fn derive_for_struct( quote!( (#name.to_owned(), #ty) ) }); Ok(quote! { - ::serde_dhall::simple::TyKind::Record( + ::serde_dhall::SimpleType::Record( vec![ #(#entries),* ].into_iter().collect() - ).into() + ) }) } @@ -92,9 +92,9 @@ fn derive_for_enum( .collect::>()?; Ok(quote! { - ::serde_dhall::simple::TyKind::Union( + ::serde_dhall::SimpleType::Union( vec![ #(#entries),* ].into_iter().collect() - ).into() + ) }) } @@ -168,7 +168,7 @@ pub fn derive_static_type_inner( impl #impl_generics ::serde_dhall::StaticType for #ident #ty_generics #where_clause { - fn static_type() -> ::serde_dhall::simple::Type { + fn static_type() -> ::serde_dhall::SimpleType { #(#assertions)* #get_type } 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. diff --git a/serde_dhall/tests/traits.rs b/serde_dhall/tests/traits.rs index f3c6f05..608e6ed 100644 --- a/serde_dhall/tests/traits.rs +++ b/serde_dhall/tests/traits.rs @@ -1,8 +1,8 @@ -use serde_dhall::{from_str, simple::Type, StaticType}; +use serde_dhall::{from_str, SimpleType, StaticType}; #[test] fn test_static_type() { - fn parse(s: &str) -> Type { + fn parse(s: &str) -> SimpleType { from_str(s).unwrap() } -- cgit v1.3.1 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') 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.3.1 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') 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.3.1 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') 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.3.1 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') 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.3.1 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/Cargo.toml | 2 +- serde_dhall/src/lib.rs | 5 +- serde_dhall/src/shortcuts.rs | 366 +++++++++++++++++++++++++++++++------------ 3 files changed, 267 insertions(+), 106 deletions(-) (limited to 'serde_dhall') diff --git a/serde_dhall/Cargo.toml b/serde_dhall/Cargo.toml index 7fd7b3e..6af883a 100644 --- a/serde_dhall/Cargo.toml +++ b/serde_dhall/Cargo.toml @@ -13,9 +13,9 @@ edition = "2018" serde = { version = "1.0", features = ["derive"] } dhall = { version = "=0.4.0", path = "../dhall" } dhall_proc_macros = { version = "=0.4.0", path = "../dhall_proc_macros" } +doc-comment = "0.3" reqwest = { version = "0.10", features = ["blocking"] } url = "2.1" [dev-dependencies] -doc-comment = "0.3" version-sync = "0.8" 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.3.1 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') 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.3.1 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') 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.3.1 From 46cb68809cdda92114adb2593b13a43687306786 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 5 Apr 2020 11:38:10 +0100 Subject: Add a test --- serde_dhall/tests/de.rs | 8 ++++++++ test.dhall | 5 +---- tests_buffer | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) (limited to 'serde_dhall') diff --git a/serde_dhall/tests/de.rs b/serde_dhall/tests/de.rs index f28b265..653613a 100644 --- a/serde_dhall/tests/de.rs +++ b/serde_dhall/tests/de.rs @@ -85,6 +85,14 @@ fn test_de_untyped() { expected_map ); + #[derive(Debug, PartialEq, Eq, Deserialize)] + struct Foo { + x: u64, + y: Option, + } + // Omit optional field + assert_eq!(parse::("{ x = 1 }"), Foo { x: 1, y: None }); + // https://github.com/Nadrieril/dhall-rust/issues/155 assert!(from_str::("List/length [True, 42]").is_err()); } diff --git a/test.dhall b/test.dhall index 641c219..c91ac54 100644 --- a/test.dhall +++ b/test.dhall @@ -1,4 +1 @@ -let Prelude = - https://prelude.dhall-lang.org/package.dhall sha256:c1b3fc613aabfb64a9e17f6c0d70fe82016a030beedd79851730993e9083fde2 - -in Prelude +{ x = 0 } with x = (1 + 1) diff --git a/tests_buffer b/tests_buffer index d07ddbd..6f5aae5 100644 --- a/tests_buffer +++ b/tests_buffer @@ -11,6 +11,7 @@ From https://github.com/dhall-lang/dhall-lang/issues/280 : "${ not_really_an_expression ;-) }" ''${ not_an_expression ;-) }'' {- {- -} 1 +{ x = 0 } with x = 1 + 1 import: failure/ -- cgit v1.3.1 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') 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.3.1 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 ++++---- serde_dhall/tests/de.rs | 6 +++--- 6 files changed, 14 insertions(+), 14 deletions(-) (limited to 'serde_dhall') 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!( diff --git a/serde_dhall/tests/de.rs b/serde_dhall/tests/de.rs index 653613a..8afba6e 100644 --- a/serde_dhall/tests/de.rs +++ b/serde_dhall/tests/de.rs @@ -1,9 +1,9 @@ use serde::Deserialize; -use serde_dhall::{from_str, from_str_static_type, StaticType}; +use serde_dhall::{from_str, FromDhall, from_str_static_type, StaticType}; #[test] fn test_de_typed() { - fn parse(s: &str) -> T { + fn parse(s: &str) -> T { from_str_static_type(s).unwrap() } @@ -57,7 +57,7 @@ fn test_de_typed() { #[test] fn test_de_untyped() { - fn parse(s: &str) -> T { + fn parse(s: &str) -> T { from_str(s).unwrap() } -- cgit v1.3.1 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 +- serde_dhall/tests/de.rs | 2 +- 3 files changed, 199 insertions(+), 18 deletions(-) (limited to 'serde_dhall') 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. diff --git a/serde_dhall/tests/de.rs b/serde_dhall/tests/de.rs index 8afba6e..b9a504a 100644 --- a/serde_dhall/tests/de.rs +++ b/serde_dhall/tests/de.rs @@ -1,5 +1,5 @@ use serde::Deserialize; -use serde_dhall::{from_str, FromDhall, from_str_static_type, StaticType}; +use serde_dhall::{from_str, from_str_static_type, FromDhall, StaticType}; #[test] fn test_de_typed() { -- cgit v1.3.1 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 --- README.md | 2 +- 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 +- serde_dhall/tests/de.rs | 10 +- serde_dhall/tests/traits.rs | 2 +- 9 files changed, 37 insertions(+), 315 deletions(-) delete mode 100644 serde_dhall/src/shortcuts.rs (limited to 'serde_dhall') diff --git a/README.md b/README.md index efe4c91..ceb33cd 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ use std::collections::BTreeMap; let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }"; // Deserialize it to a Rust type. -let deserialized_map: BTreeMap = serde_dhall::from_str(data).unwrap(); +let deserialized_map: BTreeMap = serde_dhall::from_str(data).parse().unwrap(); let mut expected_map = BTreeMap::new(); expected_map.insert("x".to_string(), 1); 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(()) diff --git a/serde_dhall/tests/de.rs b/serde_dhall/tests/de.rs index b9a504a..970234b 100644 --- a/serde_dhall/tests/de.rs +++ b/serde_dhall/tests/de.rs @@ -1,10 +1,10 @@ use serde::Deserialize; -use serde_dhall::{from_str, from_str_static_type, FromDhall, StaticType}; +use serde_dhall::{from_str, FromDhall, StaticType}; #[test] fn test_de_typed() { fn parse(s: &str) -> T { - from_str_static_type(s).unwrap() + from_str(s).static_type_annotation().parse().unwrap() } assert_eq!(parse::("True"), true); @@ -52,13 +52,13 @@ fn test_de_typed() { } assert_eq!(parse::("< X | Y: Integer >.X"), Baz::X); - assert!(from_str_static_type::("< X | Y: Integer >.Y").is_err()); + assert!(from_str::("< X | Y: Integer >.Y").static_type_annotation().parse().is_err()); } #[test] fn test_de_untyped() { fn parse(s: &str) -> T { - from_str(s).unwrap() + from_str(s).parse().unwrap() } // Test tuples on record of wrong type @@ -94,5 +94,5 @@ fn test_de_untyped() { assert_eq!(parse::("{ x = 1 }"), Foo { x: 1, y: None }); // https://github.com/Nadrieril/dhall-rust/issues/155 - assert!(from_str::("List/length [True, 42]").is_err()); + assert!(from_str::("List/length [True, 42]").parse().is_err()); } diff --git a/serde_dhall/tests/traits.rs b/serde_dhall/tests/traits.rs index 608e6ed..3c6fbfe 100644 --- a/serde_dhall/tests/traits.rs +++ b/serde_dhall/tests/traits.rs @@ -3,7 +3,7 @@ use serde_dhall::{from_str, SimpleType, StaticType}; #[test] fn test_static_type() { fn parse(s: &str) -> SimpleType { - from_str(s).unwrap() + from_str(s).parse().unwrap() } assert_eq!(bool::static_type(), parse("Bool")); -- cgit v1.3.1 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 ++++++++++++++++++++++++++------------------- serde_dhall/tests/de.rs | 10 ++- 3 files changed, 140 insertions(+), 97 deletions(-) (limited to 'serde_dhall') 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, - } - } -} diff --git a/serde_dhall/tests/de.rs b/serde_dhall/tests/de.rs index 970234b..a5c42fd 100644 --- a/serde_dhall/tests/de.rs +++ b/serde_dhall/tests/de.rs @@ -52,7 +52,10 @@ fn test_de_typed() { } assert_eq!(parse::("< X | Y: Integer >.X"), Baz::X); - assert!(from_str::("< X | Y: Integer >.Y").static_type_annotation().parse().is_err()); + assert!(from_str("< X | Y: Integer >.Y") + .static_type_annotation() + .parse::() + .is_err()); } #[test] @@ -94,5 +97,8 @@ fn test_de_untyped() { assert_eq!(parse::("{ x = 1 }"), Foo { x: 1, y: None }); // https://github.com/Nadrieril/dhall-rust/issues/155 - assert!(from_str::("List/length [True, 42]").parse().is_err()); + assert!(from_str("List/length [True, 42]").parse::().is_err()); } + +// TODO: test various builder configurations +// In particular test cloning and reusing builder -- cgit v1.3.1 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') 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.3.1 From 75c0f328c7b6d404353fd078ae12417766ef8a32 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 5 Apr 2020 16:49:15 +0100 Subject: Tweaks --- README.md | 1 - serde_dhall/src/options.rs | 23 +++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) (limited to 'serde_dhall') diff --git a/README.md b/README.md index ceb33cd..1520b7d 100644 --- a/README.md +++ b/README.md @@ -169,7 +169,6 @@ same name as the corresponding test. #### [???] -- Add `serde_dhall::options` for finer control over Dhall behavior - Breaking change: reworked most of the `serde_dhall` api #### [0.4.0] 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.3.1 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') 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.3.1 From 820214615547101f8f2b5de209b5189968bddfee Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 5 Apr 2020 17:37:15 +0100 Subject: Fix clippy warnings --- dhall/src/lib.rs | 3 +++ dhall/src/semantics/resolve/env.rs | 11 ++++------- dhall/src/semantics/tck/env.rs | 4 ++-- serde_dhall/src/static_type.rs | 6 ++---- serde_dhall/src/value.rs | 4 ++-- 5 files changed, 13 insertions(+), 15 deletions(-) (limited to 'serde_dhall') diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index adca2de..392c344 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -1,7 +1,10 @@ #![doc(html_root_url = "https://docs.rs/dhall/0.4.0")] #![allow( + clippy::implicit_hasher, clippy::module_inception, clippy::needless_lifetimes, + clippy::new_ret_no_self, + clippy::new_without_default, clippy::useless_format )] diff --git a/dhall/src/semantics/resolve/env.rs b/dhall/src/semantics/resolve/env.rs index d5bb15b..d7ff0ae 100644 --- a/dhall/src/semantics/resolve/env.rs +++ b/dhall/src/semantics/resolve/env.rs @@ -5,7 +5,7 @@ use crate::semantics::{AlphaVar, ImportLocation, TypedHir, VarEnv}; use crate::syntax::{Label, V}; /// Environment for resolving names. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct NameEnv { names: Vec