summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNadrieril Feneanar2019-08-13 16:47:10 +0200
committerGitHub2019-08-13 16:47:10 +0200
commitfc6c42f9a80902f7183c297cd8f9dcdbe5376ec5 (patch)
tree48d04a37f15612fba2efb88c15149ed39731d96e
parent7d17d39005531cb77d8eaf32ed7de8938c66f874 (diff)
parentbf5d33f3ba991afe398d58fb4fed38ec72d6f4c7 (diff)
Merge pull request #100 from Nadrieril/api
Rework API
-rw-r--r--Cargo.lock11
-rw-r--r--Cargo.toml1
-rw-r--r--dhall/Cargo.toml3
-rw-r--r--dhall/src/api/mod.rs159
-rw-r--r--dhall/src/core/mod.rs8
-rw-r--r--dhall/src/error/mod.rs2
-rw-r--r--dhall/src/lib.rs112
-rw-r--r--dhall_proc_macros/src/derive.rs14
-rw-r--r--serde_dhall/Cargo.toml12
-rw-r--r--serde_dhall/src/lib.rs301
-rw-r--r--serde_dhall/src/serde.rs (renamed from dhall/src/api/serde.rs)13
-rw-r--r--serde_dhall/src/static_type.rs (renamed from dhall/src/api/static_type.rs)26
-rw-r--r--serde_dhall/tests/traits.rs (renamed from dhall/tests/traits.rs)10
13 files changed, 358 insertions, 314 deletions
diff --git a/Cargo.lock b/Cargo.lock
index dca96f7..72b5423 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -69,7 +69,6 @@ name = "dhall"
version = "0.1.0"
dependencies = [
"bytecount 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "dhall_proc_macros 0.1.0",
"dhall_syntax 0.1.0",
"improved_slice_patterns 2.0.0",
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -316,6 +315,16 @@ dependencies = [
]
[[package]]
+name = "serde_dhall"
+version = "0.1.0"
+dependencies = [
+ "dhall 0.1.0",
+ "dhall_proc_macros 0.1.0",
+ "dhall_syntax 0.1.0",
+ "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "sha-1"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index f48e33a..1e40748 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,6 +8,7 @@ members = [
"dhall_syntax",
"dhall_proc_macros",
"improved_slice_patterns",
+ "serde_dhall"
]
# # Parser is super slow when not optimized
diff --git a/dhall/Cargo.toml b/dhall/Cargo.toml
index d08627d..0a257e4 100644
--- a/dhall/Cargo.toml
+++ b/dhall/Cargo.toml
@@ -10,11 +10,10 @@ build = "build.rs"
bytecount = "0.5.1"
itertools = "0.8.0"
term-painter = "0.2.3"
-serde = { version = "1.0", features = ["derive"] }
+serde = { version = "1.0" }
serde_cbor = "0.9.0"
improved_slice_patterns = { version = "2.0.0", path = "../improved_slice_patterns" }
dhall_syntax = { path = "../dhall_syntax" }
-dhall_proc_macros = { path = "../dhall_proc_macros" }
[dev-dependencies]
pretty_assertions = "0.6.1"
diff --git a/dhall/src/api/mod.rs b/dhall/src/api/mod.rs
deleted file mode 100644
index 188b6c0..0000000
--- a/dhall/src/api/mod.rs
+++ /dev/null
@@ -1,159 +0,0 @@
-mod serde;
-pub(crate) mod static_type;
-
-pub use value::Value;
-
-mod value {
- use super::Type;
- use crate::error::Result;
- use crate::phase::{NormalizedSubExpr, Parsed, Typed};
-
- // A Dhall value
- pub struct Value(Typed);
-
- impl Value {
- pub fn from_str(s: &str, ty: Option<&Type>) -> Result<Self> {
- let resolved = Parsed::parse_str(s)?.resolve()?;
- let typed = match ty {
- None => resolved.typecheck()?,
- Some(t) => resolved.typecheck_with(&t.to_type())?,
- };
- Ok(Value(typed))
- }
- pub(crate) fn to_expr(&self) -> NormalizedSubExpr {
- self.0.to_expr()
- }
- pub(crate) fn to_typed(&self) -> Typed {
- self.0.clone()
- }
- }
-}
-
-pub use typ::Type;
-
-mod typ {
- use dhall_syntax::Builtin;
-
- use crate::core::thunk::{Thunk, TypeThunk};
- use crate::core::value::Value;
- use crate::error::Result;
- use crate::phase::{NormalizedSubExpr, Typed};
-
- /// A Dhall expression representing a type.
- ///
- /// This captures what is usually simply called a "type", like
- /// `Bool`, `{ x: Integer }` or `Natural -> Text`.
- #[derive(Debug, Clone, PartialEq, Eq)]
- pub struct Type(Typed);
-
- impl Type {
- pub(crate) fn from_value(v: Value) -> Self {
- Type(Typed::from_value_untyped(v))
- }
- pub(crate) fn make_builtin_type(b: Builtin) -> Self {
- Self::from_value(Value::from_builtin(b))
- }
- pub(crate) fn make_optional_type(t: Type) -> Self {
- Self::from_value(Value::AppliedBuiltin(
- Builtin::Optional,
- vec![t.to_thunk()],
- ))
- }
- pub(crate) fn make_list_type(t: Type) -> Self {
- Self::from_value(Value::AppliedBuiltin(
- Builtin::List,
- vec![t.to_thunk()],
- ))
- }
- #[doc(hidden)]
- pub fn make_record_type(
- kts: impl Iterator<Item = (String, Type)>,
- ) -> Self {
- Self::from_value(Value::RecordType(
- kts.map(|(k, t)| {
- (k.into(), TypeThunk::from_thunk(t.to_thunk()))
- })
- .collect(),
- ))
- }
- #[doc(hidden)]
- pub fn make_union_type(
- kts: impl Iterator<Item = (String, Option<Type>)>,
- ) -> Self {
- Self::from_value(Value::UnionType(
- kts.map(|(k, t)| {
- (k.into(), t.map(|t| TypeThunk::from_thunk(t.to_thunk())))
- })
- .collect(),
- ))
- }
-
- pub(crate) fn to_thunk(&self) -> Thunk {
- self.0.to_thunk()
- }
- #[allow(dead_code)]
- pub(crate) fn to_expr(&self) -> NormalizedSubExpr {
- self.0.to_expr()
- }
- pub(crate) fn to_type(&self) -> crate::phase::Type {
- self.0.to_type()
- }
- }
-
- impl crate::de::Deserialize for Type {
- fn from_dhall(v: &crate::api::Value) -> Result<Self> {
- Ok(Type(v.to_typed()))
- }
- }
-}
-
-/// Deserialization of Dhall expressions into Rust
-pub mod de {
- pub use super::static_type::StaticType;
- pub use super::{Type, Value};
- use crate::error::Result;
- #[doc(hidden)]
- pub use dhall_proc_macros::StaticType;
-
- /// 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.
- // TODO: seal trait
- pub trait Deserialize: Sized {
- /// See [dhall::de::from_str][crate::de::from_str]
- fn from_dhall(v: &Value) -> Result<Self>;
- }
-
- /// 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.
- ///
- /// If a type is provided, this additionally checks that the provided
- /// expression has that type.
- pub fn from_str<T>(s: &str, ty: Option<&Type>) -> Result<T>
- where
- T: Deserialize,
- {
- T::from_dhall(&Value::from_str(s, ty)?)
- }
-
- /// Deserialize an instance of type T from a string of Dhall text,
- /// additionally checking that it matches the type of T.
- ///
- /// 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_auto_type<T>(s: &str) -> Result<T>
- where
- T: Deserialize + StaticType,
- {
- from_str(s, Some(&<T as StaticType>::static_type()))
- }
-}
diff --git a/dhall/src/core/mod.rs b/dhall/src/core/mod.rs
index a202e72..0667df8 100644
--- a/dhall/src/core/mod.rs
+++ b/dhall/src/core/mod.rs
@@ -1,4 +1,4 @@
-pub(crate) mod context;
-pub(crate) mod thunk;
-pub(crate) mod value;
-pub(crate) mod var;
+pub mod context;
+pub mod thunk;
+pub mod value;
+pub mod var;
diff --git a/dhall/src/error/mod.rs b/dhall/src/error/mod.rs
index 3c00017..3626d96 100644
--- a/dhall/src/error/mod.rs
+++ b/dhall/src/error/mod.rs
@@ -17,7 +17,6 @@ pub enum Error {
Encode(EncodeError),
Resolve(ImportError),
Typecheck(TypeError),
- Deserialize(String),
}
#[derive(Debug)]
@@ -156,7 +155,6 @@ impl std::fmt::Display for Error {
Error::Encode(err) => write!(f, "{:?}", err),
Error::Resolve(err) => write!(f, "{:?}", err),
Error::Typecheck(err) => write!(f, "{:?}", err),
- Error::Deserialize(err) => write!(f, "{}", err),
}
}
}
diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs
index ea29869..c360323 100644
--- a/dhall/src/lib.rs
+++ b/dhall/src/lib.rs
@@ -17,118 +17,10 @@
clippy::ptr_arg
)]
-//! [Dhall][dhall] is a programmable configuration language that provides a non-repetitive
-//! alternative to JSON and YAML.
-//!
-//! You can think of Dhall as: JSON + types + imports + functions
-//!
-//! For a description of the dhall language, examples, tutorials, and more, see the [language
-//! website][dhall].
-//!
-//! This crate provides support for consuming dhall files the same way you would consume JSON or
-//! YAML. It uses the [Serde][serde] serialization library to provide drop-in support for dhall
-//! for any datatype that supports serde (and that's a lot of them !).
-//!
-//! This library is limited to deserializing (reading) dhall values; serializing (writing)
-//! values to dhall is not supported for now.
-//!
-//! # Examples
-//!
-//! ### Custom datatype
-//!
-//! If you have a custom datatype for which you derived [serde::Deserialize], chances are
-//! you will be able to derive [StaticType][de::StaticType] for it as well.
-//! This gives you access to a dhall representation of your datatype that can be outputted
-//! to users, and allows easy type-safe deserializing.
-//!
-//! ```edition2018
-//! use serde::Deserialize;
-//! use dhall::de::StaticType;
-//!
-//! #[derive(Debug, Deserialize, StaticType)]
-//! struct Point {
-//! x: u64,
-//! y: u64,
-//! }
-//!
-//! fn main() {
-//! // Some dhall data
-//! let data = "{ x = 1, y = 1 + 1 }";
-//!
-//! // Convert the dhall string to a Point.
-//! let point: Point =
-//! dhall::de::from_str_auto_type(&data)
-//! .expect("An error ocurred !");
-//!
-//! // Prints "point = Point { x: 1, y: 2 }"
-//! println!("point = {:?}", point);
-//! }
-//! ```
-//!
-//! ### Loosely typed
-//!
-//! If you used to consume JSON or YAML in a loosely typed way, you can continue to do so
-//! with dhall. You only need to replace [serde_json::from_str] or [serde_yaml::from_str]
-//! with [dhall::de::from_str][de::from_str].
-//! More generally, if the [StaticType][de::StaticType] derive doesn't suit your
-//! needs, you can still deserialize any valid dhall file that serde can handle.
-//!
-//! [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
-//!
-//! ```edition2018
-//! use std::collections::BTreeMap;
-//!
-//! let mut map = BTreeMap::new();
-//! map.insert("x".to_string(), 1);
-//! map.insert("y".to_string(), 2);
-//!
-//! // Some dhall data
-//! let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }";
-//!
-//! // Deserialize it to a Rust type.
-//! let deserialized_map: BTreeMap<String, usize> =
-//! dhall::de::from_str(&data, None)
-//! .expect("Failed reading the data !");
-//! assert_eq!(map, deserialized_map);
-//! ```
-//!
-//! You can of course specify a dhall type that the input should match.
-//!
-//! ```edition2018
-//! use std::collections::BTreeMap;
-//!
-//! let mut map = BTreeMap::new();
-//! map.insert("x".to_string(), 1);
-//! map.insert("y".to_string(), 2);
-//!
-//! // Some dhall data
-//! let point_data = "{ x = 1, y = 1 + 1 }";
-//! let point_type_data = "{ x: Natural, y: Natural }";
-//!
-//! // Construct a type
-//! let point_type =
-//! dhall::de::from_str(point_type_data, None)
-//! .expect("Could not parse the Point type");
-//!
-//! // Deserialize it to a Rust type.
-//! let deserialized_map: BTreeMap<String, usize> =
-//! dhall::de::from_str(&point_data, Some(&point_type))
-//! .expect("Failed reading the data !");
-//! assert_eq!(map, deserialized_map);
-//! ```
-//!
-//! [dhall]: https://dhall-lang.org/
-//! [serde]: https://docs.serde.rs/serde/
-//! [serde::Deserialize]: https://docs.serde.rs/serde/trait.Deserialize.html
-
#[cfg(test)]
#[macro_use]
mod tests;
-pub(crate) mod api;
-pub(crate) mod core;
+pub mod core;
pub mod error;
-pub(crate) mod phase;
-
-pub use api::*;
+pub mod phase;
diff --git a/dhall_proc_macros/src/derive.rs b/dhall_proc_macros/src/derive.rs
index 725cdfb..79b553b 100644
--- a/dhall_proc_macros/src/derive.rs
+++ b/dhall_proc_macros/src/derive.rs
@@ -18,7 +18,7 @@ where
T: quote::ToTokens,
{
quote!(
- <#ty as ::dhall::de::StaticType>::static_type()
+ <#ty as ::serde_dhall::StaticType>::static_type()
)
}
@@ -53,7 +53,7 @@ fn derive_for_struct(
let ty = static_type(ty);
quote!( (#name.to_owned(), #ty) )
});
- Ok(quote! { ::dhall::de::Type::make_record_type(
+ Ok(quote! { ::serde_dhall::value::Value::make_record_type(
vec![ #(#entries),* ].into_iter()
) })
}
@@ -90,7 +90,7 @@ fn derive_for_enum(
})
.collect::<Result<_, Error>>()?;
- Ok(quote! { ::dhall::de::Type::make_union_type(
+ Ok(quote! { ::serde_dhall::value::Value::make_union_type(
vec![ #(#entries),* ].into_iter()
) })
}
@@ -134,7 +134,7 @@ pub fn derive_static_type_inner(
let mut local_where_clause = orig_where_clause.clone();
local_where_clause
.predicates
- .push(parse_quote!(#ty: ::dhall::de::StaticType));
+ .push(parse_quote!(#ty: ::serde_dhall::StaticType));
let phantoms = generics.params.iter().map(|param| match param {
syn::GenericParam::Type(syn::TypeParam { ident, .. }) => {
quote!(#ident)
@@ -156,16 +156,16 @@ pub fn derive_static_type_inner(
for ty in constraints.iter() {
where_clause
.predicates
- .push(parse_quote!(#ty: ::dhall::de::StaticType));
+ .push(parse_quote!(#ty: ::serde_dhall::StaticType));
}
let ident = &input.ident;
let tokens = quote! {
- impl #impl_generics ::dhall::de::StaticType
+ impl #impl_generics ::serde_dhall::StaticType
for #ident #ty_generics
#where_clause {
fn static_type() ->
- ::dhall::de::Type {
+ ::serde_dhall::value::Value {
#(#assertions)*
#get_type
}
diff --git a/serde_dhall/Cargo.toml b/serde_dhall/Cargo.toml
new file mode 100644
index 0000000..c61ddcd
--- /dev/null
+++ b/serde_dhall/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "serde_dhall"
+version = "0.1.0"
+authors = ["Nadrieril <nadrieril@users.noreply.github.com>"]
+license = "BSD-2-Clause"
+edition = "2018"
+
+[dependencies]
+serde = { version = "1.0", features = ["derive"] }
+dhall = { path = "../dhall" }
+dhall_syntax = { path = "../dhall_syntax" }
+dhall_proc_macros = { path = "../dhall_proc_macros" }
diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs
new file mode 100644
index 0000000..19aabec
--- /dev/null
+++ b/serde_dhall/src/lib.rs
@@ -0,0 +1,301 @@
+#![feature(non_exhaustive)]
+
+//! [Dhall][dhall] is a programmable configuration language that provides a non-repetitive
+//! alternative to JSON and YAML.
+//!
+//! You can think of Dhall as: JSON + types + imports + functions
+//!
+//! For a description of the Dhall language, examples, tutorials, and more, see the [language
+//! website][dhall].
+//!
+//! This crate provides support for consuming Dhall files the same way you would consume JSON or
+//! YAML. It uses the [Serde][serde] serialization library to provide drop-in support for Dhall
+//! for any datatype that supports serde (and that's a lot of them !).
+//!
+//! This library is limited to deserializing (reading) Dhall values; serializing (writing)
+//! values to Dhall is not supported for now.
+//!
+//! # Examples
+//!
+//! ### Custom datatype
+//!
+//! If you have a custom datatype for which you derived [serde::Deserialize], chances are
+//! you will be able to derive [StaticType] for it as well.
+//! This allows easy type-safe deserializing.
+//!
+//! ```edition2018
+//! use serde::Deserialize;
+//! use serde_dhall::{de::Error, StaticType};
+//!
+//! #[derive(Debug, Deserialize, StaticType)]
+//! struct Point {
+//! x: u64,
+//! y: u64,
+//! }
+//!
+//! fn main() -> Result<(), Error> {
+//! // Some Dhall data
+//! let data = "{ x = 1, y = 1 + 1 }";
+//!
+//! // Convert the Dhall string to a Point.
+//! let point: Point = serde_dhall::from_str_auto_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::<Point>(invalid_data).is_err());
+//!
+//! Ok(())
+//! }
+//! ```
+//!
+//! ### Loosely typed
+//!
+//! If you used to consume JSON or YAML in a loosely typed way, you can continue to do so
+//! with Dhall. You only need to replace [serde_json::from_str] or [serde_yaml::from_str]
+//! with [serde_dhall::from_str][from_str].
+//! More generally, if the [StaticType] derive doesn't suit your
+//! needs, you can still deserialize any valid Dhall file that serde can handle.
+//!
+//! [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
+//!
+//! ```edition2018
+//! # fn main() -> serde_dhall::de::Result<()> {
+//! use std::collections::BTreeMap;
+//!
+//! // Some Dhall data
+//! let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }";
+//!
+//! // Deserialize it to a Rust type.
+//! let deserialized_map: BTreeMap<String, usize> = serde_dhall::from_str(data)?;
+//!
+//! let mut expected_map = BTreeMap::new();
+//! expected_map.insert("x".to_string(), 1);
+//! expected_map.insert("y".to_string(), 2);
+//!
+//! assert_eq!(deserialized_map, expected_map);
+//! # Ok(())
+//! # }
+//! ```
+//!
+//! You can alternatively specify a Dhall type that the input should match.
+//!
+//! ```edition2018
+//! # fn main() -> serde_dhall::de::Result<()> {
+//! use std::collections::BTreeMap;
+//!
+//! // Parse a Dhall type
+//! let point_type_str = "{ x: Natural, y: Natural }";
+//! let point_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 ensures that
+//! // the data matches the point type.
+//! let deserialized_map: BTreeMap<String, usize> =
+//! serde_dhall::from_str_check_type(point_data, &point_type)?;
+//!
+//! let mut expected_map = BTreeMap::new();
+//! expected_map.insert("x".to_string(), 1);
+//! expected_map.insert("y".to_string(), 2);
+//!
+//! assert_eq!(deserialized_map, expected_map);
+//! # Ok(())
+//! # }
+//! ```
+//!
+//! [dhall]: https://dhall-lang.org/
+//! [serde]: https://docs.serde.rs/serde/
+//! [serde::Deserialize]: https://docs.serde.rs/serde/trait.Deserialize.html
+
+mod serde;
+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 static_type::StaticType;
+#[doc(inline)]
+pub use value::Value;
+
+// A Dhall value.
+pub mod value {
+ use dhall::core::thunk::{Thunk, TypeThunk};
+ use dhall::core::value::Value as DhallValue;
+ use dhall::phase::{NormalizedSubExpr, Parsed, Type, Typed};
+ use dhall_syntax::Builtin;
+
+ use super::de::{Error, Result};
+
+ /// A Dhall value
+ #[derive(Debug, Clone, PartialEq, Eq)]
+ pub struct Value(Typed);
+
+ impl Value {
+ pub fn from_str(s: &str, ty: Option<&Value>) -> Result<Self> {
+ 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>,
+ ) -> dhall::error::Result<Self> {
+ let resolved = Parsed::parse_str(s)?.resolve()?;
+ let typed = match ty {
+ None => resolved.typecheck()?,
+ Some(t) => resolved.typecheck_with(&t.to_type())?,
+ };
+ Ok(Value(typed))
+ }
+ pub(crate) fn to_expr(&self) -> NormalizedSubExpr {
+ self.0.to_expr()
+ }
+ pub(crate) fn to_thunk(&self) -> Thunk {
+ self.0.to_thunk()
+ }
+ pub(crate) fn to_type(&self) -> Type {
+ self.0.to_type()
+ }
+
+ pub(crate) fn from_dhall_value(v: DhallValue) -> Self {
+ Value(Typed::from_value_untyped(v))
+ }
+ pub(crate) fn make_builtin_type(b: Builtin) -> Self {
+ Self::from_dhall_value(DhallValue::from_builtin(b))
+ }
+ pub(crate) fn make_optional_type(t: Value) -> Self {
+ Self::from_dhall_value(DhallValue::AppliedBuiltin(
+ Builtin::Optional,
+ vec![t.to_thunk()],
+ ))
+ }
+ pub(crate) fn make_list_type(t: Value) -> Self {
+ Self::from_dhall_value(DhallValue::AppliedBuiltin(
+ Builtin::List,
+ vec![t.to_thunk()],
+ ))
+ }
+ // Made public for the StaticType derive macro
+ #[doc(hidden)]
+ pub fn make_record_type(
+ kts: impl Iterator<Item = (String, Value)>,
+ ) -> Self {
+ Self::from_dhall_value(DhallValue::RecordType(
+ kts.map(|(k, t)| {
+ (k.into(), TypeThunk::from_thunk(t.to_thunk()))
+ })
+ .collect(),
+ ))
+ }
+ #[doc(hidden)]
+ pub fn make_union_type(
+ kts: impl Iterator<Item = (String, Option<Value>)>,
+ ) -> Self {
+ Self::from_dhall_value(DhallValue::UnionType(
+ kts.map(|(k, t)| {
+ (k.into(), t.map(|t| TypeThunk::from_thunk(t.to_thunk())))
+ })
+ .collect(),
+ ))
+ }
+ }
+
+ impl super::de::Deserialize for Value {
+ fn from_dhall(v: &Value) -> Result<Self> {
+ Ok(v.clone())
+ }
+ }
+}
+
+/// Deserialize Dhall data to a Rust data structure.
+pub mod de {
+ use super::StaticType;
+ use super::Value;
+ pub use error::{Error, Result};
+
+ mod error {
+ use dhall::error::Error as DhallError;
+
+ pub type Result<T> = std::result::Result<T, Error>;
+
+ #[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<T>(msg: T) -> Self
+ where
+ T: std::fmt::Display,
+ {
+ Error::Deserialize(msg.to_string())
+ }
+ }
+ }
+
+ /// 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.
+ // TODO: seal trait
+ pub trait Deserialize: Sized {
+ /// See [serde_dhall::from_str][crate::from_str]
+ fn from_dhall(v: &Value) -> Result<Self>;
+ }
+
+ /// 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<T>(s: &str) -> Result<T>
+ where
+ T: Deserialize,
+ {
+ T::from_dhall(&Value::from_str(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<T>(s: &str, ty: &Value) -> Result<T>
+ where
+ T: Deserialize,
+ {
+ T::from_dhall(&Value::from_str(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<T>(s: &str) -> Result<T>
+ where
+ T: Deserialize + StaticType,
+ {
+ from_str_check_type(s, &<T as StaticType>::static_type())
+ }
+}
diff --git a/dhall/src/api/serde.rs b/serde_dhall/src/serde.rs
index e1c8eef..10d5a17 100644
--- a/dhall/src/api/serde.rs
+++ b/serde_dhall/src/serde.rs
@@ -1,5 +1,5 @@
-use crate::api::de::{Deserialize, Value};
-use crate::error::{Error, Result};
+use crate::de::{Deserialize, Error, Result};
+use crate::Value;
use dhall_syntax::{ExprF, SubExpr, X};
use std::borrow::Cow;
@@ -14,15 +14,6 @@ where
struct Deserializer<'a>(Cow<'a, SubExpr<X, X>>);
-impl serde::de::Error for Error {
- fn custom<T>(msg: T) -> Self
- where
- T: std::fmt::Display,
- {
- Error::Deserialize(msg.to_string())
- }
-}
-
impl<'de: 'a, 'a> serde::de::IntoDeserializer<'de, Error> for Deserializer<'a> {
type Deserializer = Deserializer<'a>;
fn into_deserializer(self) -> Self::Deserializer {
diff --git a/dhall/src/api/static_type.rs b/serde_dhall/src/static_type.rs
index 906bcef..67a7bc4 100644
--- a/dhall/src/api/static_type.rs
+++ b/serde_dhall/src/static_type.rs
@@ -1,6 +1,6 @@
use dhall_syntax::{Builtin, Integer, Natural};
-use crate::api::Type;
+use crate::Value;
/// A Rust type that can be represented as a Dhall type.
///
@@ -14,14 +14,14 @@ use crate::api::Type;
/// [StaticType] because each different value would
/// have a different Dhall record type.
pub trait StaticType {
- fn static_type() -> Type;
+ fn static_type() -> Value;
}
macro_rules! derive_builtin {
($ty:ty, $builtin:ident) => {
impl StaticType for $ty {
- fn static_type() -> Type {
- Type::make_builtin_type(Builtin::$builtin)
+ fn static_type() -> Value {
+ Value::make_builtin_type(Builtin::$builtin)
}
}
};
@@ -38,8 +38,8 @@ where
A: StaticType,
B: StaticType,
{
- fn static_type() -> Type {
- Type::make_record_type(
+ fn static_type() -> Value {
+ Value::make_record_type(
vec![
("_1".to_owned(), A::static_type()),
("_2".to_owned(), B::static_type()),
@@ -54,8 +54,8 @@ where
T: StaticType,
E: StaticType,
{
- fn static_type() -> Type {
- Type::make_union_type(
+ fn static_type() -> Value {
+ Value::make_union_type(
vec![
("Ok".to_owned(), Some(T::static_type())),
("Err".to_owned(), Some(E::static_type())),
@@ -69,8 +69,8 @@ impl<T> StaticType for Option<T>
where
T: StaticType,
{
- fn static_type() -> Type {
- Type::make_optional_type(T::static_type())
+ fn static_type() -> Value {
+ Value::make_optional_type(T::static_type())
}
}
@@ -78,8 +78,8 @@ impl<T> StaticType for Vec<T>
where
T: StaticType,
{
- fn static_type() -> Type {
- Type::make_list_type(T::static_type())
+ fn static_type() -> Value {
+ Value::make_list_type(T::static_type())
}
}
@@ -87,7 +87,7 @@ impl<'a, T> StaticType for &'a T
where
T: StaticType,
{
- fn static_type() -> Type {
+ fn static_type() -> Value {
T::static_type()
}
}
diff --git a/dhall/tests/traits.rs b/serde_dhall/tests/traits.rs
index 0f75553..55be63b 100644
--- a/dhall/tests/traits.rs
+++ b/serde_dhall/tests/traits.rs
@@ -1,10 +1,10 @@
#![feature(proc_macro_hygiene)]
-use dhall::de::{from_str, StaticType, Type};
+use serde_dhall::{from_str, StaticType, Value};
#[test]
fn test_static_type() {
- fn parse(s: &str) -> Type {
- from_str(s, None).unwrap()
+ fn parse(s: &str) -> Value {
+ from_str(s).unwrap()
}
assert_eq!(bool::static_type(), parse("Bool"));
@@ -15,14 +15,14 @@ fn test_static_type() {
parse("{ _1: Bool, _2: List Text }")
);
- #[derive(dhall::de::StaticType)]
+ #[derive(serde_dhall::StaticType)]
#[allow(dead_code)]
struct A {
field1: bool,
field2: Option<bool>,
}
assert_eq!(
- <A as dhall::de::StaticType>::static_type(),
+ <A as serde_dhall::StaticType>::static_type(),
parse("{ field1: Bool, field2: Optional Bool }")
);