summaryrefslogtreecommitdiff
path: root/dhall/src/api
diff options
context:
space:
mode:
Diffstat (limited to 'dhall/src/api')
-rw-r--r--dhall/src/api/mod.rs38
-rw-r--r--dhall/src/api/serde.rs69
-rw-r--r--dhall/src/api/traits/deserialize.rs53
-rw-r--r--dhall/src/api/traits/dynamic_type.rs32
-rw-r--r--dhall/src/api/traits/mod.rs6
-rw-r--r--dhall/src/api/traits/static_type.rs149
6 files changed, 347 insertions, 0 deletions
diff --git a/dhall/src/api/mod.rs b/dhall/src/api/mod.rs
new file mode 100644
index 0000000..1522c9c
--- /dev/null
+++ b/dhall/src/api/mod.rs
@@ -0,0 +1,38 @@
+mod serde;
+pub(crate) mod traits;
+
+/// Deserialization of Dhall expressions into Rust
+pub mod de {
+ pub use crate::traits::{Deserialize, SimpleStaticType, StaticType};
+ #[doc(hidden)]
+ pub use dhall_proc_macros::SimpleStaticType;
+
+ /// 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<'a, T: Deserialize<'a>>(
+ s: &'a str,
+ ty: Option<&crate::phase::Type>,
+ ) -> crate::error::Result<T> {
+ T::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<'a, T: Deserialize<'a> + StaticType>(
+ s: &'a str,
+ ) -> crate::error::Result<T> {
+ from_str(s, Some(&<T as StaticType>::get_static_type()))
+ }
+}
diff --git a/dhall/src/api/serde.rs b/dhall/src/api/serde.rs
new file mode 100644
index 0000000..93921ba
--- /dev/null
+++ b/dhall/src/api/serde.rs
@@ -0,0 +1,69 @@
+use crate::error::{Error, Result};
+use crate::phase::{Normalized, Type};
+use crate::traits::Deserialize;
+use dhall_syntax::*;
+use std::borrow::Cow;
+
+impl<'a, T: serde::Deserialize<'a>> Deserialize<'a> for T {
+ fn from_str(s: &'a str, ty: Option<&Type>) -> Result<Self> {
+ let expr = Normalized::from_str(s, ty)?;
+ T::deserialize(Deserializer(Cow::Owned(expr.to_expr())))
+ }
+}
+
+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 {
+ self
+ }
+}
+
+impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> {
+ type Error = Error;
+ fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
+ 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/traits/deserialize.rs b/dhall/src/api/traits/deserialize.rs
new file mode 100644
index 0000000..9673cf9
--- /dev/null
+++ b/dhall/src/api/traits/deserialize.rs
@@ -0,0 +1,53 @@
+use crate::error::*;
+use crate::phase::*;
+
+/// 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<'de>: Sized {
+ /// See [dhall::de::from_str][crate::de::from_str]
+ fn from_str(s: &'de str, ty: Option<&Type>) -> Result<Self>;
+}
+
+impl<'de> Deserialize<'de> for Parsed {
+ /// Simply parses the provided string. Ignores the
+ /// provided type.
+ fn from_str(s: &'de str, _: Option<&Type>) -> Result<Self> {
+ Ok(Parsed::parse_str(s)?)
+ }
+}
+
+impl<'de> Deserialize<'de> for Resolved {
+ /// Parses and resolves the provided string. Ignores the
+ /// provided type.
+ fn from_str(s: &'de str, ty: Option<&Type>) -> Result<Self> {
+ Ok(Parsed::from_str(s, ty)?.resolve()?)
+ }
+}
+
+impl<'de> Deserialize<'de> for Typed {
+ /// Parses, resolves and typechecks the provided string.
+ fn from_str(s: &'de str, ty: Option<&Type>) -> Result<Self> {
+ let resolved = Resolved::from_str(s, ty)?;
+ match ty {
+ None => Ok(resolved.typecheck()?),
+ Some(t) => Ok(resolved.typecheck_with(t)?),
+ }
+ }
+}
+
+impl<'de> Deserialize<'de> for Normalized {
+ /// Parses, resolves, typechecks and normalizes the provided string.
+ fn from_str(s: &'de str, ty: Option<&Type>) -> Result<Self> {
+ Ok(Typed::from_str(s, ty)?.normalize())
+ }
+}
+
+impl<'de> Deserialize<'de> for Type {
+ fn from_str(s: &'de str, ty: Option<&Type>) -> Result<Self> {
+ Ok(Normalized::from_str(s, ty)?.to_type())
+ }
+}
diff --git a/dhall/src/api/traits/dynamic_type.rs b/dhall/src/api/traits/dynamic_type.rs
new file mode 100644
index 0000000..7763a28
--- /dev/null
+++ b/dhall/src/api/traits/dynamic_type.rs
@@ -0,0 +1,32 @@
+use crate::error::TypeError;
+use crate::phase::{Normalized, Type, Typed};
+use crate::traits::StaticType;
+use std::borrow::Cow;
+
+pub trait DynamicType {
+ fn get_type<'a>(&'a self) -> Result<Cow<'a, Type>, TypeError>;
+}
+
+impl<T: StaticType> DynamicType for T {
+ fn get_type<'a>(&'a self) -> Result<Cow<'a, Type>, TypeError> {
+ Ok(Cow::Owned(T::get_static_type()))
+ }
+}
+
+impl DynamicType for Type {
+ fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> {
+ self.get_type()
+ }
+}
+
+impl DynamicType for Normalized {
+ fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> {
+ self.0.get_type()
+ }
+}
+
+impl DynamicType for Typed {
+ fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> {
+ self.get_type()
+ }
+}
diff --git a/dhall/src/api/traits/mod.rs b/dhall/src/api/traits/mod.rs
new file mode 100644
index 0000000..315e17a
--- /dev/null
+++ b/dhall/src/api/traits/mod.rs
@@ -0,0 +1,6 @@
+mod deserialize;
+mod dynamic_type;
+mod static_type;
+pub use deserialize::Deserialize;
+pub use dynamic_type::DynamicType;
+pub use static_type::{SimpleStaticType, StaticType};
diff --git a/dhall/src/api/traits/static_type.rs b/dhall/src/api/traits/static_type.rs
new file mode 100644
index 0000000..8b141a0
--- /dev/null
+++ b/dhall/src/api/traits/static_type.rs
@@ -0,0 +1,149 @@
+use crate::phase::*;
+use dhall_proc_macros as dhall;
+use dhall_syntax::*;
+
+/// A value that has a statically-known Dhall type.
+///
+/// This trait is strictly more general than [SimpleStaticType].
+/// The reason is that it allows an arbitrary [Type] to be returned
+/// instead of just a [SimpleType].
+///
+/// For now the only interesting impl is [SimpleType] itself, who
+/// has a statically-known type which is not itself a [SimpleType].
+pub trait StaticType {
+ fn get_static_type() -> Type;
+}
+
+/// A Rust type that can be represented as a Dhall type.
+///
+/// A typical example is `Option<bool>`,
+/// 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<String, bool>` cannot implement
+/// [SimpleStaticType] because each different value would
+/// have a different Dhall record type.
+///
+/// The `Simple` in `SimpleStaticType` indicates that the returned type is
+/// a [SimpleType].
+pub trait SimpleStaticType {
+ fn get_simple_static_type() -> SimpleType;
+}
+
+fn mktype(x: SubExpr<X, X>) -> SimpleType {
+ x.into()
+}
+
+impl<T: SimpleStaticType> StaticType for T {
+ fn get_static_type() -> Type {
+ crate::phase::Normalized::from_thunk_and_type(
+ crate::phase::normalize::Thunk::from_normalized_expr(
+ T::get_simple_static_type().into(),
+ ),
+ Type::const_type(),
+ )
+ .to_type()
+ }
+}
+
+impl StaticType for SimpleType {
+ /// By definition, a [SimpleType] has type `Type`.
+ /// This returns the Dhall expression `Type`
+ fn get_static_type() -> Type {
+ Type::const_type()
+ }
+}
+
+impl SimpleStaticType for bool {
+ fn get_simple_static_type() -> SimpleType {
+ mktype(dhall::subexpr!(Bool))
+ }
+}
+
+impl SimpleStaticType for Natural {
+ fn get_simple_static_type() -> SimpleType {
+ mktype(dhall::subexpr!(Natural))
+ }
+}
+
+impl SimpleStaticType for u32 {
+ fn get_simple_static_type() -> SimpleType {
+ mktype(dhall::subexpr!(Natural))
+ }
+}
+
+impl SimpleStaticType for u64 {
+ fn get_simple_static_type() -> SimpleType {
+ mktype(dhall::subexpr!(Natural))
+ }
+}
+
+impl SimpleStaticType for Integer {
+ fn get_simple_static_type() -> SimpleType {
+ mktype(dhall::subexpr!(Integer))
+ }
+}
+
+impl SimpleStaticType for i32 {
+ fn get_simple_static_type() -> SimpleType {
+ mktype(dhall::subexpr!(Integer))
+ }
+}
+
+impl SimpleStaticType for i64 {
+ fn get_simple_static_type() -> SimpleType {
+ mktype(dhall::subexpr!(Integer))
+ }
+}
+
+impl SimpleStaticType for String {
+ fn get_simple_static_type() -> SimpleType {
+ mktype(dhall::subexpr!(Text))
+ }
+}
+
+impl<A: SimpleStaticType, B: SimpleStaticType> SimpleStaticType for (A, B) {
+ fn get_simple_static_type() -> SimpleType {
+ let ta: SubExpr<_, _> = A::get_simple_static_type().into();
+ let tb: SubExpr<_, _> = B::get_simple_static_type().into();
+ mktype(dhall::subexpr!({ _1: ta, _2: tb }))
+ }
+}
+
+impl<T: SimpleStaticType> SimpleStaticType for Option<T> {
+ fn get_simple_static_type() -> SimpleType {
+ let t: SubExpr<_, _> = T::get_simple_static_type().into();
+ mktype(dhall::subexpr!(Optional t))
+ }
+}
+
+impl<T: SimpleStaticType> SimpleStaticType for Vec<T> {
+ fn get_simple_static_type() -> SimpleType {
+ let t: SubExpr<_, _> = T::get_simple_static_type().into();
+ mktype(dhall::subexpr!(List t))
+ }
+}
+
+impl<'a, T: SimpleStaticType> SimpleStaticType for &'a T {
+ fn get_simple_static_type() -> SimpleType {
+ T::get_simple_static_type()
+ }
+}
+
+impl<T> SimpleStaticType for std::marker::PhantomData<T> {
+ fn get_simple_static_type() -> SimpleType {
+ mktype(dhall::subexpr!({}))
+ }
+}
+
+impl<T: SimpleStaticType, E: SimpleStaticType> SimpleStaticType
+ for std::result::Result<T, E>
+{
+ fn get_simple_static_type() -> SimpleType {
+ let tt: SubExpr<_, _> = T::get_simple_static_type().into();
+ let te: SubExpr<_, _> = E::get_simple_static_type().into();
+ mktype(dhall::subexpr!(< Ok: tt | Err: te>))
+ }
+}