From 1a98b506055779e1a60558d9c5a56b071b3d61a0 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 22 Mar 2020 21:20:58 +0000 Subject: Reorganize API and internals of serde_dhall a bit --- serde_dhall/src/deserialize.rs | 124 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 serde_dhall/src/deserialize.rs (limited to 'serde_dhall/src/deserialize.rs') 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 + } +} -- 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/src/deserialize.rs') 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 f598e3612a854076b570bbc9c88abcdc1da528bd Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 22 Mar 2020 22:17:22 +0000 Subject: Rework SimpleValue --- serde_dhall/src/deserialize.rs | 10 +++--- serde_dhall/src/simple.rs | 73 +++++++++++++++++------------------------- serde_dhall/src/value.rs | 2 +- 3 files changed, 35 insertions(+), 50 deletions(-) (limited to 'serde_dhall/src/deserialize.rs') 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 3a5f2044954aae7278f16e30561a81626dba6923 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 22 Mar 2020 22:27:56 +0000 Subject: Move mod simple into value --- serde_dhall/src/deserialize.rs | 2 +- serde_dhall/src/lib.rs | 5 +- serde_dhall/src/options.rs | 2 +- serde_dhall/src/simple.rs | 224 ------------------------------------ serde_dhall/src/value.rs | 252 ++++++++++++++++++++++++++++++++++++++--- 5 files changed, 240 insertions(+), 245 deletions(-) delete mode 100644 serde_dhall/src/simple.rs (limited to 'serde_dhall/src/deserialize.rs') 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 f9848b54fe2e64901042fba66fb471999f415ff1 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 24 Mar 2020 18:53:25 +0000 Subject: Hide serde Error internals --- serde_dhall/src/deserialize.rs | 6 +++--- serde_dhall/src/error.rs | 24 ++++++++++++++++-------- serde_dhall/src/lib.rs | 1 + serde_dhall/src/options.rs | 4 ++-- serde_dhall/src/value.rs | 10 +++++----- 5 files changed, 27 insertions(+), 18 deletions(-) (limited to 'serde_dhall/src/deserialize.rs') 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/src/deserialize.rs') 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 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/src/deserialize.rs') 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 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/src/deserialize.rs') 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