diff options
author | Nadrieril Feneanar | 2019-08-13 16:47:10 +0200 |
---|---|---|
committer | GitHub | 2019-08-13 16:47:10 +0200 |
commit | fc6c42f9a80902f7183c297cd8f9dcdbe5376ec5 (patch) | |
tree | 48d04a37f15612fba2efb88c15149ed39731d96e | |
parent | 7d17d39005531cb77d8eaf32ed7de8938c66f874 (diff) | |
parent | bf5d33f3ba991afe398d58fb4fed38ec72d6f4c7 (diff) |
Merge pull request #100 from Nadrieril/api
Rework API
Diffstat (limited to '')
-rw-r--r-- | Cargo.lock | 11 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | dhall/Cargo.toml | 3 | ||||
-rw-r--r-- | dhall/src/api/mod.rs | 159 | ||||
-rw-r--r-- | dhall/src/core/mod.rs | 8 | ||||
-rw-r--r-- | dhall/src/error/mod.rs | 2 | ||||
-rw-r--r-- | dhall/src/lib.rs | 112 | ||||
-rw-r--r-- | dhall_proc_macros/src/derive.rs | 14 | ||||
-rw-r--r-- | serde_dhall/Cargo.toml | 12 | ||||
-rw-r--r-- | serde_dhall/src/lib.rs | 301 | ||||
-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
@@ -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" @@ -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 }") ); |