Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove dependency on rustfmt installation for consumers #221

Merged
merged 10 commits into from
Mar 17, 2023
19 changes: 19 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,25 @@

== Unreleased changes (release date TBD)

Breaking change: this release removes the `ToString` implementation for
`TypeSpace` and the `rustfmt-wrapper` dependency. This allows users to use a
pretty printing method of choice. Users can use the `ToTokens` implementation of
`TypeSpace`:

Before:
[source,rust]
----
let typespace = TypeSpace::default();
typespace.to_string()
----

After:
[source,rust]
----
let typespace = TypeSpace::default();
typespace.to_stream().to_string()
mbrobbel marked this conversation as resolved.
Show resolved Hide resolved
----

https://github.com/oxidecomputer/typify/compare/v0.0.10\...HEAD[Full list of commits]

* Allow per-type renames and derive macro applications (#131)
Expand Down
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

63 changes: 47 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
Typify compiles JSON Schema documents into Rust types. It can be used in one of
three ways:

- via the macro `import_types!("types.json")` to generate Rust types directly
in your program
- 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 via the builder functions to generate persistent files e.g. when building
API bindings.
API bindings.

## JSON Schema → Rust types

Typify translates JSON Schema types in a few different ways depending on some basic properties of the schema:
Typify translates JSON Schema types in a few different ways depending on some
basic properties of the schema:

### Built-in types

Expand All @@ -23,17 +24,17 @@ 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).
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.
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

Expand All @@ -49,14 +50,43 @@ simply get the `#[serde(default)]` attribute (so you won't see e.g.

### OneOf

The `OneOf` construct maps to a Rust enum. Typify maps this to the various [serde enum types](https://serde.rs/enum-representations.html).
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`).

### No formatting

```rust
typespace.to_stream().to_string()
```

### Rustfmt

```rust
rustfmt_wrapper::rustfmt(typespace.to_stream().to_string())?
```

### Prettyplease

```rust
prettyplease::unparse(&syn::parse2::<syn::File>(typespace.to_stream())?)
```

## WIP

Typify is a work in progress. Changes that affect output will be indicated with
Expand All @@ -73,9 +103,9 @@ Bounded numbers aren't very well handled. Consider, for example, the schema:

```json
{
"type": "integer",
"minimum": 1,
"maximum": 6
"type": "integer",
"minimum": 1,
"maximum": 6
}
```

Expand All @@ -98,4 +128,5 @@ struct A {
a: Box<A>,
}
```

.. but it does not support more complex cycles such as A -> B -> A.
2 changes: 1 addition & 1 deletion cargo-typify/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ pub fn convert(args: &CliArgs) -> Result<String> {
use serde::{Deserialize, Serialize};
";

let contents = format!("{intro}\n{}", type_space.to_string());
let contents = format!("{intro}\n{}", type_space.to_stream().to_string());

let contents = rustfmt_wrapper::rustfmt(contents).wrap_err("Failed to format Rust code")?;

Expand Down
2 changes: 1 addition & 1 deletion cargo-typify/tests/integration.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use expectorate::assert_contents;
use newline_converter::{dos2unix, unix2dos};
use newline_converter::dos2unix;
use tempdir::TempDir;

#[test]
Expand Down
2 changes: 2 additions & 0 deletions example-build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ serde = "1.0"
serde_json = "1.0.94"

[build-dependencies]
prettyplease = "0.1"
schemars = "0.8"
serde_json = "1.0.94"
syn = "1.0.109"
typify = { path = "../typify" }
2 changes: 1 addition & 1 deletion example-build/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn main() {
let contents = format!(
"{}\n{}",
"use serde::{Deserialize, Serialize};",
type_space.to_string()
prettyplease::unparse(&syn::parse2::<syn::File>(type_space.to_stream()).unwrap())
);

let mut out_file = Path::new(&env::var("OUT_DIR").unwrap()).to_path_buf();
Expand Down
2 changes: 1 addition & 1 deletion typify-impl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ log = "0.4"
proc-macro2 = "1.0"
quote = "1.0"
regress = "0.5.0"
rustfmt-wrapper = "0.2"
schemars = "0.8.12"
serde_json = "1.0"
syn = { version = "1.0", features = ["full"] }
Expand All @@ -24,6 +23,7 @@ unicode-ident = "1.0.8"
[dev-dependencies]
expectorate = "1.0"
paste = "1.0"
rustfmt-wrapper = "0.2"
schema = "0.0.1"
schemars = { version = "0.8.12", features = ["uuid1"] }
serde = "1.0"
Expand Down
7 changes: 0 additions & 7 deletions typify-impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use log::info;
use output::OutputSpace;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use rustfmt_wrapper::rustfmt;
use schemars::schema::{Metadata, Schema};
use thiserror::Error;
use type_entry::{
Expand Down Expand Up @@ -801,12 +800,6 @@ impl TypeSpace {
}
}

impl ToString for TypeSpace {
fn to_string(&self) -> String {
rustfmt(self.to_stream().to_string()).unwrap()
}
}

impl ToTokens for TypeSpace {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.extend(self.to_stream())
Expand Down
2 changes: 2 additions & 0 deletions typify-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ serde_json = "1.0.94"

[build-dependencies]
ipnetwork = { version = "0.20.0", features = ["schemars"] }
prettyplease = "0.1"
schemars = "0.8.12"
serde = "1.0.155"
syn = "1.0.109"
typify = { path = "../typify" }
2 changes: 1 addition & 1 deletion typify-test/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ fn main() {
let contents = format!(
"{}\n{}",
"use serde::{Deserialize, Serialize};",
type_space.to_string()
prettyplease::unparse(&syn::parse2::<syn::File>(type_space.to_stream()).unwrap())
);

let mut out_file = Path::new(&env::var("OUT_DIR").unwrap()).to_path_buf();
Expand Down