From 85e2e8ee5e83dadd05b6974ba6c951350cb97a61 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 21 Mar 2020 21:30:30 +0000 Subject: Introduce option builder --- Cargo.lock | 2 + README.md | 1 + dhall/Cargo.toml | 2 +- dhall/src/lib.rs | 3 + dhall/src/semantics/builtins.rs | 6 +- dhall/src/semantics/resolve/resolve.rs | 10 ++- serde_dhall/Cargo.toml | 2 + serde_dhall/src/lib.rs | 16 ++-- serde_dhall/src/options.rs | 138 +++++++++++++++++++++++++++++++++ serde_dhall/src/value.rs | 32 +++----- 10 files changed, 173 insertions(+), 39 deletions(-) create mode 100644 serde_dhall/src/options.rs diff --git a/Cargo.lock b/Cargo.lock index b4230ff..31638a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -964,7 +964,9 @@ version = "0.4.0" dependencies = [ "dhall 0.4.0", "dhall_proc_macros 0.4.0", + "reqwest 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/README.md b/README.md index 2cb3c0b..cce65e3 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,7 @@ same name as the corresponding test. #### [???] +- Add `serde_dhall::options` for finer control over Dhall behavior - Breaking change: use `serde_dhall::simple::Type` type for type-checking instead of `serde_dhall::Value` #### [0.4.0] diff --git a/dhall/Cargo.toml b/dhall/Cargo.toml index 0ffe78b..02ec1e7 100644 --- a/dhall/Cargo.toml +++ b/dhall/Cargo.toml @@ -22,7 +22,7 @@ reqwest = { version = "0.10", features = ["blocking"] } serde = "1.0" serde_cbor = "0.9.0" smallvec = "1.0.0" -url = "2.1.1" +url = "2.1" [dev-dependencies] pretty_assertions = "0.6.1" diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index 0f4b623..adca2de 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -72,6 +72,9 @@ impl Parsed { pub fn resolve(self) -> Result { resolve::resolve(self) } + pub fn skip_resolve(self) -> Result { + resolve::skip_resolve(self) + } /// Converts a value back to the corresponding AST expression. pub fn to_expr(&self) -> Expr { diff --git a/dhall/src/semantics/builtins.rs b/dhall/src/semantics/builtins.rs index fa22bd0..6007a92 100644 --- a/dhall/src/semantics/builtins.rs +++ b/dhall/src/semantics/builtins.rs @@ -1,5 +1,5 @@ use crate::semantics::{ - skip_resolve, typecheck, Hir, HirKind, Nir, NirKind, NzEnv, VarEnv, + skip_resolve_expr, typecheck, Hir, HirKind, Nir, NirKind, NzEnv, VarEnv, }; use crate::syntax::map::DupTreeMap; use crate::syntax::Const::Type; @@ -189,7 +189,7 @@ pub fn type_of_builtin(b: Builtin) -> Hir { forall (A: Type) -> Optional A ), }; - skip_resolve(&expr).unwrap() + skip_resolve_expr(&expr).unwrap() } // Ad-hoc macro to help construct closures @@ -254,7 +254,7 @@ fn apply_builtin(b: Builtin, args: Vec, env: NzEnv) -> NirKind { DoneAsIs, } let make_closure = |e| { - typecheck(&skip_resolve(&e).unwrap()) + typecheck(&skip_resolve_expr(&e).unwrap()) .unwrap() .eval(env.clone()) }; diff --git a/dhall/src/semantics/resolve/resolve.rs b/dhall/src/semantics/resolve/resolve.rs index 07bd06c..fe3f3a9 100644 --- a/dhall/src/semantics/resolve/resolve.rs +++ b/dhall/src/semantics/resolve/resolve.rs @@ -227,7 +227,7 @@ fn resolve_one_import( } ImportMode::Location => { let expr = location.into_location(); - let hir = skip_resolve(&expr)?; + let hir = skip_resolve_expr(&expr)?; let ty = hir.typecheck_noenv()?.ty().clone(); Ok((hir, ty)) } @@ -333,12 +333,18 @@ pub fn resolve(parsed: Parsed) -> Result { resolve_with_env(&mut ImportEnv::new(), parsed) } -pub fn skip_resolve(expr: &Expr) -> Result { +pub fn skip_resolve_expr(expr: &Expr) -> Result { traverse_resolve_expr(&mut NameEnv::new(), expr, &mut |import| { Err(ImportError::UnexpectedImport(import).into()) }) } +pub fn skip_resolve(parsed: Parsed) -> Result { + let Parsed(expr, _) = parsed; + let resolved =skip_resolve_expr(&expr)?; + Ok(Resolved(resolved)) +} + pub trait Canonicalize { fn canonicalize(&self) -> Self; } diff --git a/serde_dhall/Cargo.toml b/serde_dhall/Cargo.toml index 0ec792a..71a9b02 100644 --- a/serde_dhall/Cargo.toml +++ b/serde_dhall/Cargo.toml @@ -13,3 +13,5 @@ edition = "2018" serde = { version = "1.0", features = ["derive"] } dhall = { version = "=0.4.0", path = "../dhall" } dhall_proc_macros = { version = "=0.4.0", path = "../dhall_proc_macros" } +reqwest = { version = "0.10", features = ["blocking"] } +url = "2.1" diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 4b1a689..5b8f470 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -169,6 +169,8 @@ //! [serde::Deserialize]: https://docs.serde.rs/serde/trait.Deserialize.html mod error; +/// Finer-grained control over deserializing Dhall +pub mod options; mod serde; /// Serde-compatible values and their type pub mod simple; @@ -198,14 +200,6 @@ pub trait Deserialize: sealed::Sealed + Sized { fn from_dhall(v: &Value) -> Result; } -fn from_str_with_annot(s: &str, ty: Option<&simple::Type>) -> Result -where - T: Deserialize, -{ - let val = Value::from_str_with_annot(s, ty)?; - T::from_dhall(&val) -} - /// Deserialize an instance of type `T` from a string of Dhall text. /// /// This will recursively resolve all imports in the expression, and @@ -216,7 +210,7 @@ pub fn from_str(s: &str) -> Result where T: Deserialize, { - from_str_with_annot(s, None) + options::from_str(s).parse() } /// Deserialize an instance of type `T` from a string of Dhall text, @@ -228,7 +222,7 @@ pub fn from_str_check_type(s: &str, ty: &simple::Type) -> Result where T: Deserialize, { - from_str_with_annot(s, Some(ty)) + options::from_str(s).type_annotation(ty).parse() } /// Deserialize an instance of type `T` from a string of Dhall text, @@ -241,5 +235,5 @@ pub fn from_str_auto_type(s: &str) -> Result where T: Deserialize + StaticType, { - from_str_check_type(s, &::static_type()) + options::from_str(s).static_type_annotation().parse() } diff --git a/serde_dhall/src/options.rs b/serde_dhall/src/options.rs new file mode 100644 index 0000000..542804b --- /dev/null +++ b/serde_dhall/src/options.rs @@ -0,0 +1,138 @@ +use std::path::{Path, PathBuf}; + +use dhall::Parsed; + +use crate::simple::Type as SimpleType; +use crate::{Deserialize, Error, Result, StaticType, Value}; + +#[derive(Debug, Clone)] +enum Source<'a> { + Str(&'a str), + File(PathBuf), + // Url(&'a str), +} + +/// Options and flags which can be used to configure how a dhall value is read. +/// +/// This builder exposes the ability to configure how a value is deserialized and what operations +/// are permitted during evaluation. The functions in the crate root are aliases for +/// commonly used options using this builder. +/// +/// Generally speaking, when using `Options`, you'll create it with `from_str` or `from_file`, then +/// chain calls to methods to set each option, then call `parse`. This will give you a +/// `serde_dhall::Result` where `T` a deserializable type of your choice. +/// +/// # Examples +/// +/// Reading from a file: +/// +/// ```no_run +/// # fn main() -> serde_dhall::Result<()> { +/// use serde_dhall::options; +/// +/// let data = options::from_file("foo.dhall").parse()?; +/// # Ok(()) +/// # } +/// ``` +/// +/// Reading from a file and checking the value against a provided type: +/// +/// ```no_run +/// # fn main() -> serde_dhall::Result<()> { +/// use serde_dhall::options; +/// +/// let ty = options::from_str("{ x: Natural, y: Natural }").parse()?; +/// let data = options::from_file("foo.dhall") +/// .type_annotation(&ty) +/// .parse()?; +/// # Ok(()) +/// # } +/// ``` +#[derive(Debug, Clone)] +pub struct Options<'a, T> { + source: Source<'a>, + annot: Option, + allow_imports: bool, + // allow_remote_imports: bool, + // use_cache: bool, + target_type: std::marker::PhantomData, +} + +impl<'a, T> Options<'a, T> { + fn default_with_source(source: Source<'a>) -> Self { + Options { + source, + annot: None, + allow_imports: true, + // allow_remote_imports: true, + // use_cache: true, + target_type: std::marker::PhantomData, + } + } + fn from_str(s: &'a str) -> Self { + Self::default_with_source(Source::Str(s)) + } + fn from_file>(path: P) -> Self { + Self::default_with_source(Source::File(path.as_ref().to_owned())) + } + // fn from_url(url: &'a str) -> Self { + // Self::default_with_source(Source::Url(url)) + // } + + pub fn imports(&mut self, imports: bool) -> &mut Self { + self.allow_imports = imports; + self + } + // pub fn remote_imports(&mut self, imports: bool) -> &mut Self { + // self.allow_remote_imports = imports; + // if imports { + // self.allow_imports = true; + // } + // self + // } + pub fn type_annotation(&mut self, ty: &SimpleType) -> &mut Self { + self.annot = Some(ty.clone()); + self + } + pub fn static_type_annotation(&mut self) -> &mut Self + where + T: StaticType, + { + self.annot = Some(T::static_type()); + self + } + + fn _parse(&self) -> dhall::error::Result { + let parsed = match &self.source { + Source::Str(s) => Parsed::parse_str(s)?, + Source::File(p) => Parsed::parse_file(p.as_ref())?, + }; + let resolved = if self.allow_imports { + parsed.resolve()? + } else { + parsed.skip_resolve()? + }; + let typed = match &self.annot { + None => resolved.typecheck()?, + Some(ty) => resolved.typecheck_with(&ty.to_value().hir)?, + }; + Ok(Value::from_nir(typed.normalize().as_nir())) + } + pub fn parse(&self) -> Result + where + T: Deserialize, + { + let val = self._parse().map_err(Error::Dhall)?; + T::from_dhall(&val) + } +} + +pub fn from_str<'a, T>(s: &'a str) -> Options<'a, T> { + Options::from_str(s) +} +pub fn from_file<'a, T, P: AsRef>(path: P) -> Options<'a, T> { + Options::from_file(path) +} +// pub fn from_url<'a, T>(url: &'a str) -> Options<'a, T> { +// Options::from_url(url) +// } diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs index 62632e5..d4ded90 100644 --- a/serde_dhall/src/value.rs +++ b/serde_dhall/src/value.rs @@ -1,9 +1,8 @@ use dhall::semantics::{Hir, Nir}; use dhall::syntax::Expr; -use dhall::Parsed; use crate::simple::{Type as SimpleType, Value as SimpleValue}; -use crate::Error; +use crate::{sealed::Sealed, Deserialize, Error}; /// An arbitrary Dhall value. #[derive(Debug, Clone)] @@ -17,32 +16,13 @@ pub struct Value { } impl Value { - /// Parse a string into a Value, and optionally ensure that the value matches the provided type - /// annotation. - pub fn from_str_with_annot( - s: &str, - ty: Option<&SimpleType>, - ) -> Result { - Self::from_str_with_annot_dhall_error(s, ty).map_err(Error::Dhall) - } - fn from_nir(x: &Nir) -> Self { + pub(crate) fn from_nir(x: &Nir) -> Self { Value { hir: x.to_hir_noenv(), as_simple_val: SimpleValue::from_nir(x), as_simple_ty: SimpleType::from_nir(x), } } - fn from_str_with_annot_dhall_error( - s: &str, - ty: Option<&SimpleType>, - ) -> Result { - let resolved = Parsed::parse_str(s)?.resolve()?; - let typed = match ty { - None => resolved.typecheck()?, - Some(ty) => resolved.typecheck_with(&ty.to_value().hir)?, - }; - Ok(Self::from_nir(typed.normalize().as_nir())) - } /// Converts a Value into a SimpleValue. pub fn to_simple_value(&self) -> Option { @@ -70,3 +50,11 @@ impl std::fmt::Display for Value { self.to_expr().fmt(f) } } + +impl Sealed for Value {} + +impl Deserialize for Value { + fn from_dhall(v: &Value) -> Result { + Ok(v.clone()) + } +} -- cgit v1.2.3