diff options
author | stuebinm | 2021-04-10 22:59:07 +0200 |
---|---|---|
committer | stuebinm | 2021-04-10 22:59:07 +0200 |
commit | 2958333c83dce911463734e391fe3ccc76cfc9d5 (patch) | |
tree | f8b6a6370222f6f1053061f684c27ead563b7c74 | |
parent | c307b914eca192f8e8648345e365f9c0207bc18c (diff) |
add type substitutions to serde_dhall
this adds subsititutions, which work similar to the
`Dhall.substitutions` mechanism of the Haskell dhall package, and which
can be used e.g. like this:
```rust
serde_dhall.from_str(...)
.substitute_names(hashmap!["Newtype" => static_type])
.parse::<SimpleType>()?
```
The idea behind this is to make it easy to add programmer-defined types
which may be used in configs for programs, without forcing the program's
users to re-import the same type definitions each time (or the
programmers to keep the dhall-based definitions in sync with their rust
types, since these are now generated automatically).
Caveats so far:
- makes some of the code ugly (dhall internals are now used in
serde_dhall/src/lib.rs, for example)
- haven't tested error messages so far
- some unecessary copying of strings and static type values
-rw-r--r-- | dhall/src/lib.rs | 5 | ||||
-rw-r--r-- | dhall/src/syntax/ast/expr.rs | 13 | ||||
-rw-r--r-- | serde_dhall/src/options/de.rs | 30 |
3 files changed, 46 insertions, 2 deletions
diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index 4238b6e..f3a11a1 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -99,6 +99,11 @@ impl Parsed { pub fn to_expr(&self) -> Expr { self.0.clone() } + + pub fn substitute_name(self, label: syntax::Label, value: Expr) -> Parsed { + let Parsed (expr, import_location) = self; + Parsed (expr.substitute_name(label, value), import_location) + } } impl<'cx> Resolved<'cx> { diff --git a/dhall/src/syntax/ast/expr.rs b/dhall/src/syntax/ast/expr.rs index eba2735..bc3ffa4 100644 --- a/dhall/src/syntax/ast/expr.rs +++ b/dhall/src/syntax/ast/expr.rs @@ -182,6 +182,19 @@ impl Expr { let data = binary::encode(self)?; Ok(crate::utils::sha256_hash(&data)) } + + /// this wraps the expression into an additional let-binding + pub fn substitute_name(self, label: Label, value: Expr) -> Expr { + Expr::new( + ExprKind::Let( + label, + None, + value, + self + ), + Span::Artificial + ) + } } // Empty enum to indicate that no error can occur diff --git a/serde_dhall/src/options/de.rs b/serde_dhall/src/options/de.rs index 103f161..a070af7 100644 --- a/serde_dhall/src/options/de.rs +++ b/serde_dhall/src/options/de.rs @@ -1,4 +1,5 @@ use std::path::{Path, PathBuf}; +use std::collections::HashMap; use dhall::{Ctxt, Parsed}; @@ -58,6 +59,7 @@ pub struct Deserializer<'a, A> { source: Source<'a>, annot: A, allow_imports: bool, + substitutions: HashMap<dhall::syntax::Label, dhall::syntax::Expr>, // allow_remote_imports: bool, // use_cache: bool, } @@ -68,6 +70,7 @@ impl<'a> Deserializer<'a, NoAnnot> { source, annot: NoAnnot, allow_imports: true, + substitutions: HashMap::new(), // allow_remote_imports: true, // use_cache: true, } @@ -131,6 +134,7 @@ impl<'a> Deserializer<'a, NoAnnot> { annot: ManualAnnot(ty), source: self.source, allow_imports: self.allow_imports, + substitutions: self.substitutions, } } @@ -181,6 +185,7 @@ impl<'a> Deserializer<'a, NoAnnot> { annot: StaticAnnot, source: self.source, allow_imports: self.allow_imports, + substitutions: self.substitutions, } } } @@ -223,6 +228,18 @@ impl<'a, A> Deserializer<'a, A> { // self // } + pub fn substitute_names(self, substs: HashMap<String, SimpleType>) -> Self { + Deserializer { + substitutions: substs + .iter() + .map(|(s, ty)| { + (dhall::syntax::Label::from_str(s), ty.to_expr()) + }) + .collect(), + ..self + } + } + fn _parse<T>(&self) -> dhall::error::Result<Result<Value>> where A: TypeAnnot, @@ -234,11 +251,20 @@ impl<'a, A> Deserializer<'a, A> { Source::File(p) => Parsed::parse_file(p.as_ref())?, Source::BinaryFile(p) => Parsed::parse_binary_file(p.as_ref())?, }; + + let parsed_with_substs = self + .substitutions + .iter() + .fold(parsed, |acc, (name, subst)| { + acc.substitute_name(name.clone(), subst.clone()) + }); + let resolved = if self.allow_imports { - parsed.resolve(cx)? + parsed_with_substs.resolve(cx)? } else { - parsed.skip_resolve(cx)? + parsed_with_substs.skip_resolve(cx)? }; + //println!("{:#?}", resolved); let typed = match &T::get_annot(self.annot) { None => resolved.typecheck(cx)?, Some(ty) => resolved.typecheck_with(cx, &ty.to_hir())?, |