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.
This crate provides support for consuming Dhall files the same way you would consume JSON or YAML. It uses the Serde serialization library to provide drop-in support for Dhall for any datatype that supports serde (and that's a lot of them !).
Basic usage
Deserialization (reading)
The entrypoint for deserialization is the [from_str()
] function. It reads a string containing
a Dhall expression and deserializes it into any serde-compatible type.
This could mean a common Rust type like HashMap
:
#
or a custom datatype, using serde's derive
mechanism:
#
Serialization (writing)
The entrypoint for serialization is the [serialize()
] function. It takes a serde-compatible
type value and serializes it to a string containing a Dhall expression.
This could mean a common Rust type like HashMap
:
#
or a custom datatype, using serde's derive
mechanism:
#
Beware that in order to serialize empty options, empty lists or enums correctly, you will need to provide a type annotation!
Replacing serde_json
or serde_yaml
If you used to consume JSON or YAML, you only need to replace serde_json::from_str
or
serde_yaml::from_str
with serde_dhall::from_str(…).parse()
.
If you used to produce JSON or YAML, you only need to replace serde_json::to_string
or
serde_yaml::to_string
with serde_dhall::serialize(…).to_string()
.
Additional type annotations
When deserializing, normal type checking is done to ensure that the returned value is a valid Dhall value. However types are first-class in Dhall, and this library allows you to additionally check that the input data matches a given Dhall type. That way, a type error will be caught on the Dhall side, and have pretty and explicit errors that point to the source file.
It is also possible to provide a type annotation when serializing. This is useful in particular
for types like HashMap
or [SimpleValue
] that do not have a fixed type as Dhall values.
Moreover, some values (namely empty options, empty lists, and enums) require a type annotation in order to be converted to Dhall, because the resulting Dhall value will contain the type explicitly.
There are two ways to provide a type in this way: you can provide it manually or you can let
Rust infer it for you. To let Rust infer the appropriate Dhall type, use the [StaticType
]
trait.
#
# fn main() -> serde_dhall::Result<()> {
use serde::Serialize;
use serde_dhall::{serialize, StaticType};
#[derive(Serialize, StaticType)]
enum MyOption {
MyNone,
MySome(u64),
}
let data = MyOption::MySome(0);
let string = serialize(&data)
.static_type_annotation()
.to_string()?;
// The resulting Dhall string depends on the type annotation; it could not have been
// printed without it.
assert_eq!(string, "< MyNone | MySome: Natural >.MySome 0".to_string());
# Ok(())
# }
To provide a type manually, you need a [SimpleType
] value. You can parse it from some Dhall
text like you would parse any other value.
#
# fn main() -> serde_dhall::Result<()> {
use serde_dhall::{serialize, from_str, SimpleValue};
let ty = from_str("< A | B: Bool >").parse()?;
let data = SimpleValue::Union("A".to_string(), None);
let string = serialize(&data)
.type_annotation(&ty)
.to_string()?;
assert_eq!(string, "< A | B: Bool >.A".to_string());
# Ok(())
# }
Controlling deserialization
If you need more control over the process of reading Dhall values, e.g. disabling
imports, see the [Deserializer
] methods.