From 52f9ecfc4dac65d305fd920e8c7f748889a0804f Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 12 Aug 2019 23:24:48 +0200 Subject: Move api into its own crate --- dhall/Cargo.toml | 1 - dhall/src/api/mod.rs | 159 ------------------------------------------- dhall/src/api/serde.rs | 70 ------------------- dhall/src/api/static_type.rs | 93 ------------------------- dhall/src/core/mod.rs | 8 +-- dhall/src/error/mod.rs | 10 +++ dhall/src/lib.rs | 112 +----------------------------- dhall/tests/traits.rs | 68 ------------------ 8 files changed, 16 insertions(+), 505 deletions(-) delete mode 100644 dhall/src/api/mod.rs delete mode 100644 dhall/src/api/serde.rs delete mode 100644 dhall/src/api/static_type.rs delete mode 100644 dhall/tests/traits.rs (limited to 'dhall') diff --git a/dhall/Cargo.toml b/dhall/Cargo.toml index d08627d..aa52d58 100644 --- a/dhall/Cargo.toml +++ b/dhall/Cargo.toml @@ -14,7 +14,6 @@ serde = { version = "1.0", features = ["derive"] } 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 { - 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, - ) -> 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)>, - ) -> 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 { - 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; - } - - /// 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(s: &str, ty: Option<&Type>) -> Result - 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(s: &str) -> Result - where - T: Deserialize + StaticType, - { - from_str(s, Some(&::static_type())) - } -} diff --git a/dhall/src/api/serde.rs b/dhall/src/api/serde.rs deleted file mode 100644 index e1c8eef..0000000 --- a/dhall/src/api/serde.rs +++ /dev/null @@ -1,70 +0,0 @@ -use crate::api::de::{Deserialize, Value}; -use crate::error::{Error, Result}; -use dhall_syntax::{ExprF, SubExpr, X}; -use std::borrow::Cow; - -impl<'a, T> Deserialize for T -where - T: serde::Deserialize<'a>, -{ - fn from_dhall(v: &Value) -> Result { - T::deserialize(Deserializer(Cow::Owned(v.to_expr()))) - } -} - -struct Deserializer<'a>(Cow<'a, SubExpr>); - -impl serde::de::Error for Error { - fn custom(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 { - 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 ExprF::*; - match self.0.as_ref().as_ref() { - NaturalLit(n) => match (*n).try_into() { - Ok(n64) => visitor.visit_u64(n64), - Err(_) => match (*n).try_into() { - Ok(n32) => visitor.visit_u32(n32), - Err(_) => unimplemented!(), - }, - }, - IntegerLit(n) => match (*n).try_into() { - Ok(n64) => visitor.visit_i64(n64), - Err(_) => match (*n).try_into() { - Ok(n32) => visitor.visit_i32(n32), - Err(_) => unimplemented!(), - }, - }, - RecordLit(m) => visitor.visit_map( - serde::de::value::MapDeserializer::new(m.iter().map( - |(k, v)| (k.as_ref(), Deserializer(Cow::Borrowed(v))), - )), - ), - _ => unimplemented!(), - } - } - - 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 - tuple_struct map struct enum identifier ignored_any - } -} diff --git a/dhall/src/api/static_type.rs b/dhall/src/api/static_type.rs deleted file mode 100644 index 906bcef..0000000 --- a/dhall/src/api/static_type.rs +++ /dev/null @@ -1,93 +0,0 @@ -use dhall_syntax::{Builtin, Integer, Natural}; - -use crate::api::Type; - -/// A Rust type that can be represented as a Dhall type. -/// -/// A typical example is `Option`, -/// represented by the dhall expression `Optional Bool`. -/// -/// This trait can and should be automatically derived. -/// -/// The representation needs to be independent of the value. -/// For this reason, something like `HashMap` cannot implement -/// [StaticType] because each different value would -/// have a different Dhall record type. -pub trait StaticType { - fn static_type() -> Type; -} - -macro_rules! derive_builtin { - ($ty:ty, $builtin:ident) => { - impl StaticType for $ty { - fn static_type() -> Type { - Type::make_builtin_type(Builtin::$builtin) - } - } - }; -} - -derive_builtin!(bool, Bool); -derive_builtin!(Natural, Natural); -derive_builtin!(u64, Natural); -derive_builtin!(Integer, Integer); -derive_builtin!(String, Text); - -impl StaticType for (A, B) -where - A: StaticType, - B: StaticType, -{ - fn static_type() -> Type { - Type::make_record_type( - vec![ - ("_1".to_owned(), A::static_type()), - ("_2".to_owned(), B::static_type()), - ] - .into_iter(), - ) - } -} - -impl StaticType for std::result::Result -where - T: StaticType, - E: StaticType, -{ - fn static_type() -> Type { - Type::make_union_type( - vec![ - ("Ok".to_owned(), Some(T::static_type())), - ("Err".to_owned(), Some(E::static_type())), - ] - .into_iter(), - ) - } -} - -impl StaticType for Option -where - T: StaticType, -{ - fn static_type() -> Type { - Type::make_optional_type(T::static_type()) - } -} - -impl StaticType for Vec -where - T: StaticType, -{ - fn static_type() -> Type { - Type::make_list_type(T::static_type()) - } -} - -impl<'a, T> StaticType for &'a T -where - T: StaticType, -{ - fn static_type() -> Type { - T::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..ecf3510 100644 --- a/dhall/src/error/mod.rs +++ b/dhall/src/error/mod.rs @@ -192,3 +192,13 @@ impl From for Error { Error::Typecheck(err) } } + +impl serde::de::Error for Error { + fn custom(msg: T) -> Self + where + T: std::fmt::Display, + { + Error::Deserialize(msg.to_string()) + } +} + 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 = -//! 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 = -//! 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/tests/traits.rs b/dhall/tests/traits.rs deleted file mode 100644 index 0f75553..0000000 --- a/dhall/tests/traits.rs +++ /dev/null @@ -1,68 +0,0 @@ -#![feature(proc_macro_hygiene)] -use dhall::de::{from_str, StaticType, Type}; - -#[test] -fn test_static_type() { - fn parse(s: &str) -> Type { - from_str(s, None).unwrap() - } - - assert_eq!(bool::static_type(), parse("Bool")); - assert_eq!(String::static_type(), parse("Text")); - assert_eq!(>::static_type(), parse("Optional Bool")); - assert_eq!( - <(bool, Vec)>::static_type(), - parse("{ _1: Bool, _2: List Text }") - ); - - #[derive(dhall::de::StaticType)] - #[allow(dead_code)] - struct A { - field1: bool, - field2: Option, - } - assert_eq!( - ::static_type(), - parse("{ field1: Bool, field2: Optional Bool }") - ); - - #[derive(StaticType)] - #[allow(dead_code)] - struct B<'a, T: 'a> { - field1: &'a T, - field2: Option, - } - assert_eq!(>::static_type(), A::static_type()); - - #[derive(StaticType)] - #[allow(dead_code)] - struct C(T, Option); - assert_eq!( - >::static_type(), - <(bool, Option)>::static_type() - ); - - #[derive(StaticType)] - #[allow(dead_code)] - struct D(); - assert_eq!( - >::static_type(), - parse("{ _1: {}, _2: Optional Text }") - ); - - #[derive(StaticType)] - #[allow(dead_code)] - enum E { - A(T), - B(String), - }; - assert_eq!(>::static_type(), parse("< A: Bool | B: Text >")); - - #[derive(StaticType)] - #[allow(dead_code)] - enum F { - A, - B(bool), - }; - assert_eq!(F::static_type(), parse("< A | B: Bool >")); -} -- cgit v1.2.3