# Typify
Typify compiles JSON Schema documents into Rust types. It can be used in one of
several ways:
- via the macro `import_types!("types.json")` to generate Rust types directly in
your program
- via a builder interface to generate Rust types in `build.rs` or `xtask`
- via the builder functions to generate persistent files e.g. when building
API bindings
- using the [`cargo typify`](./cargo-typify/README.md) command
**If generation fails or is lousy**: Please file an issue and include the JSON
Schema and Rust output (if there is any). Use `cargo typify` command to
generate code from the command-line.
## JSON Schema → Rust types
Typify translates JSON Schema types in a few different ways depending on some
basic properties of the schema:
### Built-in types
Integers, floating-point numbers, strings, etc. Those all have straightforward
representations in Rust. The only significant nuance is how to select the
appropriate built-in type based on type attributes. For example, a JSON Schema
might specify a maximum and/or minimum that indicates the appropriate integral
type to use.
String schemas that include a `format` are represented with the appropriate Rust
type. For example `{ "type": "string", "format": "uuid" }` is represented as a
`uuid::Uuid` (which requires the `uuid` crate be included as a dependency).
### Arrays
JSON Schema arrays can turn into one of three Rust types `Vec<T>`, `HashSet<T>`,
and tuples depending on the schema properties. An array may have a fixed length
that matches a fixed list of item types; this is well represented by a Rust
tuples. The distinction between `Vec<T>` and `HashSet<T>` is only if the
schema's `uniqueItems` field is `false` or `true` respectively.
### Objects
In general, objects turn in to Rust structs. If, however, the schema defines no
properties, Typify emits a `HashMap<String, T>` if the `additionalProperties`
schema specifies `T` or a `HashMap<String, serde_json::Value>` otherwise.
Properties that are not in the `required` set are typically represented as an
`Option<T>` with the `#[serde(default)]` attribute applied. Non-required
properties with types that already have a default value (such as a `Vec<T>`)
simply get the `#[serde(default)]` attribute (so you won't see e.g.
`Option<Vec<T>>`).
### OneOf
The `OneOf` construct maps to a Rust enum. Typify maps this to the various
[serde enum types](https://serde.rs/enum-representations.html).
### AnyOf / AllOf
The `anyOf` and `allOf` constructs are a little trickier to handle, but (in
general) Typify models these as structs where each member is decorated with the
`#[serde(flatten)]` attribute (with `Option` wrappers in the case of `anyOf`).
## Formatting
By default Typify's generated code is not formatted. If formatted code is
preferred, crates like [rustfmt-wrapper](https://docs.rs/rustfmt-wrapper) and
[prettyplease](https://docs.rs/prettyplease) can be used to format the generated
code before writing it to a file.
The examples below show different ways to convert a `TypeSpace` to a string
(`typespace` is a `typify::TypeSpace`).
### `rustfmt`
Best for generation of code that might be checked in alongside hand-written
code such as in the case of an `xtask` or stand-alone code generator (list
`cargo-typify`).
```rust
rustfmt_wrapper::rustfmt(typespace.to_stream().to_string())?
```
### `prettyplease`
Best for `build.rs` scripts where transitive dependencies might not have
`rustfmt` installed so should be self-contained.
```rust
prettyplease::unparse(&syn::parse2::<syn::File>(typespace.to_stream())?)
```
### No formatting
If no human will ever see the code (and this is almost never the case).
```rust
typespace.to_stream().to_string()
```
## WIP
Typify is a work in progress. Changes that affect output will be indicated with
a breaking change to the crate version number.
In general, if you have a JSON Schema that causes Typify to fail or if the
generated type isn't what you expect, please file an issue.
There are some known areas where we'd like to improve:
### Bounded numbers
Bounded numbers aren't very well handled. Consider, for example, the schema:
```json
{
"type": "integer",
"minimum": 1,
"maximum": 6
}
```
The resulting types won't enforce those value constraints.
### Configurable dependencies
A string schema with `format` set to `uuid` will result in the `uuid::Uuid`
type; similarly, a `format` of `date` translates to
`chrono::Date<chrono::offset::Utc>`. For users that don't want dependencies on
`uuid` or `chrono` it would be useful for Typify to optionally represent those
as `String` (or as some other, consumer-specified type).
### Cyclic types
Typify has special-case handling for self-referential types. For example:
```rust
struct A {
a: Box<A>,
}
```
.. but it does not support more complex cycles such as A -> B -> A.