Crates.io | serde_dhall_typegen |
lib.rs | serde_dhall_typegen |
version | 0.1.0 |
source | src |
created_at | 2022-04-15 23:16:07.011701 |
updated_at | 2022-04-15 23:16:07.011701 |
description | Automatically generate Rust structs and enums from Dhall types |
homepage | |
repository | https://github.com/colinjneville/serde_dhall_typegen |
max_upload_size | |
id | 568745 |
size | 100,779 |
A macro for automatically generating Rust structs and enums from Dhall types. Dhall values of these types can then be deserialized into Rust with the serde_dhall
crate.
Currently customizability is limited and the exact Rust generated is very subject to change, so it is only recommended to use this crate for prototyping while your Dhall schemas are changing often.
Create serialization-compatible Rust types in the following mod
for the Dhall types contained in a given folder.
Hand-written impl
s can be included in the mod
block.
By default, the generated types are named as the Pascal case of the Dhall file they are defined in (e.g. my_type.dhall
-> MyType
), but this can be overwritten (see Dhall metadata).
Any sub-unions or records contained within these files will be assigned an arbitrary name by default (which may change between compiles).
my_dhall_stuff.rs
#[serde_dhall_typegen::dhall_types("./dhall/schema/")]
mod dhall { }
name = literal
impl = bool
- Should functions be generated for all types? Equivalent to setting named_impl
and anonymous_impl
named_impl = bool
- Should functions be generated for named types? Equivalent to setting named_enum_impl
and named_struct_impl
anonymous_impl = bool
- Should functions be generated for anonymous types? Equivalent to setting anonymous_enum_impl
and anonymous_struct_impl
named_enum_impl = bool
- Should variant access functions be generated for anonymous enums? Defaults to false
named_struct_impl = bool
- Should member access functions be generated for anonymous structs? Defaults to false
anonymous_enum_impl = bool
- Should member access functions be generated for anonymous enums? Defaults to true
anonymous_struct_impl = bool
- Should member access functions be generated for anonymous structs? Defaults to false
Each .dhall file in the specified directory will be evaluated and must return a record, a union, a schema,
or a function taking a single Type
parameter which returns one of the previous types.
dhall_types
supports generating types with a single type parameter when provided a Dhall file containing a function of type Type -> Type
.
The type parameter will be named T
. Like non-generic types, member types will be interpreted as instances of generic types if possible. For example,
\(T: Type) -> {
field: T
}
let MyGeneric = ./my_generic.dhall
{
a: MyGeneric Natural,
b: { field: Text },
}
... becomes...
pub struct MyGeneric<T> {
pub field: T,
}
pub struct MyType {
a: MyGeneric<u64>,
b: MyGeneric<String>,
}
Anonymous enums have functions generated for each variant by default. If a variant MyVariant
has an associated type T
, the enum will have the following functions:
fn my_variant(&self) -> Option<&T>
fn my_variant_mut(&mut self) -> Option<&mut T>
fn into_my_variant(self) -> Result<T, Self>
If the variant does not have an associated type, it will have the following function:
fn is_my_variant(&self) -> bool
If struct functions are enabled, a field my_field
of type T
will have the following functions:
fn my_field(&self) -> &T
fn my_field_mut(&mut self) -> &mut T
Because Dhall and Rust have fundamentally different type systems, it may be necessary to make some non-functional changes in the Dhall to generate Rust types as you intend.
Dhall's types are structural, so two types with the same structure in two separate files are the same type as far as Dhall is concerned. This is considered an error when generating
Rust types, however, as it creates ambiguities when determining which Rust type should be used for a given Dhall type.
To avoid the issue, you can assign 'name metadata' to your identical Dhall types so that they can be differentiated during evaluation. This will not affect your Dhall types in other
contexts - the resultant Dhall types are only modified when specific environment variables are set.
The first method can be used with either records or unions and precedes the type:
env:rust_type ? (\(T: Type)->\(T: Type)->T) <MyEnumA>
< A | B >
env:rust_type ? (\(T: Type)->\(T: Type)->T) <MyEnumB>
< A | B >
... generates...
pub enum MyEnumA {
A,
B,
}
pub enum MyEnumB {
A,
B,
}
The second method is less verbose, but can only be used for records and comes after the type:
{
name: Text
}
//\\ (env:rust_type <MyStructA> ? {})
{
name: Text
}
//\\ (env:rust_type <MyStructB> ? {})
... generates...
pub struct MyStructA {
pub name: String,
}
pub struct MyStructB {
pub name: String,
}
You can use let
bindings to reduce the verbosity:
let rust_type = env:rust_type ? (\(T: Type)->\(T: Type)->T)
let rust_struct = \(T: Type) -> (env:rust_struct T ? {})
let Abc = rust_type <Abc> < A | B | C >
let Def = rust_type <Def> < D | E | F >
let Ghi = { g: Bool, h: Bool, i: Bool } //\\ (rust_struct <Ghi>)
let Jkl = { j: Bool, k: Bool, l: Bool } //\\ (rust_struct <Jkl>)
{
name: Text,
age_range: <
Baby |
Toddler |
Child |
Teenager |
Adult |
Senior
>,
occupation: Optional {
title: Text,
salary: Natural,
}
}
#[serde_dhall_typegen::dhall_types("./dhall/schema/")]
mod dhall { }
mod dhall {
#[derive(Debug, Clone, Eq, PartialEq, Hash, ::serde::Serialize, ::serde::Deserialize)]
pub struct Person {
pub age_range: PersonAnon0,
pub name: String,
pub occupation: Option<PersonAnon1>,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, ::serde::Serialize, ::serde::Deserialize)]
pub enum PersonAnon0 {
Teenager,
Senior,
Baby,
Toddler,
Child,
Adult,
}
impl PersonAnon0 {
pub fn is_teenager(&self) -> bool {
match self {
Self::Teenager => true,
_ => false,
}
}
pub fn is_senior(&self) -> bool {
match self {
Self::Senior => true,
_ => false,
}
}
pub fn is_baby(&self) -> bool {
match self {
Self::Baby => true,
_ => false,
}
}
pub fn is_toddler(&self) -> bool {
match self {
Self::Toddler => true,
_ => false,
}
}
pub fn is_child(&self) -> bool {
match self {
Self::Child => true,
_ => false,
}
}
pub fn is_adult(&self) -> bool {
match self {
Self::Adult => true,
_ => false,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, ::serde::Serialize, ::serde::Deserialize)]
pub struct PersonAnon1 {
pub salary: u64,
pub title: String,
}
}
#[non_exhaustive]
).