From 792fbbb86e811dc4acc30aca22032ecc68edb9a3 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Tue, 22 Oct 2019 22:49:24 +0100 Subject: [PATCH] Add more info to README --- README.md | 190 ++++++++++++++++++++++--------- schemars/examples/main.rs | 19 ++++ schemars/examples/serde_attrs.rs | 24 ++++ schemars/examples/structs.rs | 26 ----- schemars/src/gen.rs | 11 +- schemars/src/schema.rs | 1 + schemars_derive/src/lib.rs | 2 +- 7 files changed, 182 insertions(+), 91 deletions(-) create mode 100644 schemars/examples/main.rs create mode 100644 schemars/examples/serde_attrs.rs delete mode 100644 schemars/examples/structs.rs diff --git a/README.md b/README.md index c93b0dd..22e29b9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# schemars +# Schemars [![Travis (.org)](https://img.shields.io/travis/GREsau/schemars?logo=travis)](https://travis-ci.org/GREsau/schemars) [![Crates.io](https://img.shields.io/crates/v/schemars)](https://crates.io/crates/schemars) @@ -9,97 +9,175 @@ Work in progress! ## Basic Usage +If you don't really care about the specifics, the easiest way to generate a JSON schema for you types is to `#[derive(JsonSchema)]` and use the `schema_for!` macro: + +```rust +use schemars::{schema_for, JsonSchema}; + +#[derive(JsonSchema)] +pub struct MyStruct { + pub my_int: i32, + pub my_bool: bool, + pub my_nullable_enum: Option, +} + +#[derive(JsonSchema)] +pub enum MyEnum { + Unit, + StringNewType(String) +} + +fn main() { + let schema = schema_for!(MyStruct); + println!("{}", serde_json::to_string_pretty(&schema).unwrap()); +} +``` + +
+Click to see the output JSON schema... + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MyStruct", + "definitions": { + "MyEnum": { + "anyOf": [ + { + "enum": [ + "Unit" + ] + }, + { + "type": "object", + "required": [ + "StringNewType" + ], + "properties": { + "StringNewType": { + "type": "string" + } + } + } + ] + } + }, + "type": "object", + "required": [ + "my_bool", + "my_int", + "my_nullable_enum" + ], + "properties": { + "my_bool": { + "type": "boolean" + }, + "my_int": { + "type": "integer", + "format": "int32" + }, + "my_nullable_enum": { + "anyOf": [ + { + "$ref": "#/definitions/MyEnum" + }, + { + "type": "null" + } + ] + } + } +} +``` +
+ +### Serde Compatibility + +One of the main aims of this library is compatibility with [serde](https://github.com/serde-rs/serde). Any generated schema *should* match how [serde_json](https://github.com/serde-rs/json) would serialize/deserialize to/from JSON. To support this, Schemars will check for any `#[serde(...)]` attributes on types that derive `JsonSchema`, and adjust the generated schema accordingly. + ```rust use schemars::{schema_for, JsonSchema}; use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize, JsonSchema)] #[serde(rename_all = "camelCase")] -struct MyStruct { - my_int: i32, - my_nullable: Option, - my_nested_struct: Nested, +pub struct MyStruct { + #[serde(rename = "myNumber")] + pub my_int: i32, + pub my_bool: bool, + #[serde(default)] + pub my_nullable_enum: Option, } #[derive(Deserialize, Serialize, JsonSchema)] -#[serde(rename_all = "camelCase")] -struct Nested { - #[serde(default)] - my_string: String, - #[serde(rename = "myArray")] - my_float_vec: Vec, - my_recursive_struct: Option>, +#[serde(untagged)] +pub enum MyEnum { + Unit, + StringNewType(String) } -fn main() -> Result<(), Box> { +fn main() { let schema = schema_for!(MyStruct); - println!("{}", serde_json::to_string_pretty(&schema)?); - Ok(()) + println!("{}", serde_json::to_string_pretty(&schema).unwrap()); } + ``` -This outputs the following: +
+Click to see the output JSON schema... ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct", - "type": "object", "definitions": { - "Nested": { - "type": "object", - "required": [ - "myArray", - "myRecursiveStruct" - ], - "properties": { - "myArray": { - "type": "array", - "items": { - "type": "number", - "format": "float" - } + "MyEnum": { + "anyOf": [ + { + "type": "null" }, - "myRecursiveStruct": { - "anyOf": [ - { - "$ref": "#/definitions/Nested" - }, - { - "type": "null" - } - ] - }, - "myString": { + { "type": "string" } - } + ] } }, + "type": "object", "required": [ - "myInt", - "myNestedStruct", - "myNullable" + "myBool", + "myNumber" ], "properties": { - "myInt": { + "myBool": { + "type": "boolean" + }, + "myNullableEnum": { + "anyOf": [ + { + "$ref": "#/definitions/MyEnum" + }, + { + "type": "null" + } + ] + }, + "myNumber": { "type": "integer", "format": "int32" - }, - "myNestedStruct": { - "$ref": "#/definitions/Nested" - }, - "myNullable": { - "type": [ - "boolean", - "null" - ] } } } ``` +
-Note that the `#[serde(...)]` attributes are respected. +`#[serde(...)]` attributes can be overriden using `#[schemars(...)]` attributes, which behave identically (e.g. `#[schemars(rename_all = "camelCase")]`). You may find this useful if you want to change the generated schema without affecting Serde, or if you're just not using Serde. + +## Feature Flags +- `chrono` - implements `JsonSchema` for all [Chrono](https://github.com/chronotope/chrono) types which are serializable by Serde. +- `derive_json_schema` - implements `JsonSchema` for Schemars types themselves + +## Customizing Schema Generation +TODO document! ## TODO - Documentation diff --git a/schemars/examples/main.rs b/schemars/examples/main.rs new file mode 100644 index 0000000..cc02a51 --- /dev/null +++ b/schemars/examples/main.rs @@ -0,0 +1,19 @@ +use schemars::{schema_for, JsonSchema}; + +#[derive(JsonSchema)] +pub struct MyStruct { + pub my_int: i32, + pub my_bool: bool, + pub my_nullable_enum: Option, +} + +#[derive(JsonSchema)] +pub enum MyEnum { + Unit, + StringNewType(String) +} + +fn main() { + let schema = schema_for!(MyStruct); + println!("{}", serde_json::to_string_pretty(&schema).unwrap()); +} diff --git a/schemars/examples/serde_attrs.rs b/schemars/examples/serde_attrs.rs new file mode 100644 index 0000000..70e6439 --- /dev/null +++ b/schemars/examples/serde_attrs.rs @@ -0,0 +1,24 @@ +use schemars::{schema_for, JsonSchema}; +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize, JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct MyStruct { + #[serde(rename = "myNumber")] + pub my_int: i32, + pub my_bool: bool, + #[serde(default)] + pub my_nullable_enum: Option, +} + +#[derive(Deserialize, Serialize, JsonSchema)] +#[serde(untagged)] +pub enum MyEnum { + Unit, + StringNewType(String) +} + +fn main() { + let schema = schema_for!(MyStruct); + println!("{}", serde_json::to_string_pretty(&schema).unwrap()); +} diff --git a/schemars/examples/structs.rs b/schemars/examples/structs.rs deleted file mode 100644 index 05f4b20..0000000 --- a/schemars/examples/structs.rs +++ /dev/null @@ -1,26 +0,0 @@ -use schemars::{schema_for, JsonSchema}; -use serde::{Deserialize, Serialize}; - -#[derive(Deserialize, Serialize, JsonSchema)] -#[serde(rename_all = "camelCase")] -struct MyStruct { - my_int: i32, - my_nullable: Option, - my_nested_struct: Nested, -} - -#[derive(Deserialize, Serialize, JsonSchema)] -#[serde(rename_all = "camelCase")] -struct Nested { - #[serde(default)] - my_string: String, - #[serde(rename = "myArray")] - my_float_vec: Vec, - my_recursive_struct: Option>, -} - -fn main() -> Result<(), Box> { - let schema = schema_for!(MyStruct); - println!("{}", serde_json::to_string_pretty(&schema)?); - Ok(()) -} diff --git a/schemars/src/gen.rs b/schemars/src/gen.rs index 160623e..c249536 100644 --- a/schemars/src/gen.rs +++ b/schemars/src/gen.rs @@ -1,7 +1,7 @@ use crate::schema::*; use crate::{JsonSchema, Map}; -/// Settings to customize how a Schema is generated. +/// Settings to customize how Schemas are generated. #[derive(Debug, PartialEq, Clone)] pub struct SchemaSettings { /// If `true`, schemas for [`Option`](Option) will include a `nullable` property. @@ -37,16 +37,11 @@ pub enum BoolSchemas { impl Default for SchemaSettings { fn default() -> SchemaSettings { - SchemaSettings::new() + SchemaSettings::draft07() } } impl SchemaSettings { - /// Creates `SchemaSettings` that conform to [JSON Schema Draft 7](https://json-schema.org/specification-links.html#draft-7). - pub fn new() -> SchemaSettings { - Self::draft07() - } - /// Creates `SchemaSettings` that conform to [JSON Schema Draft 7](https://json-schema.org/specification-links.html#draft-7). pub fn draft07() -> SchemaSettings { SchemaSettings { @@ -57,7 +52,7 @@ impl SchemaSettings { } } - /// Creates `SchemaSettings` that conform to [OpenAPI 3](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#schemaObject). + /// Creates `SchemaSettings` that conform to [OpenAPI 3.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#schemaObject). pub fn openapi3() -> SchemaSettings { SchemaSettings { option_nullable: true, diff --git a/schemars/src/schema.rs b/schemars/src/schema.rs index a3b1153..47fe3ba 100644 --- a/schemars/src/schema.rs +++ b/schemars/src/schema.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "derive_json_schema")] use crate as schemars; #[cfg(feature = "derive_json_schema")] use crate::JsonSchema; diff --git a/schemars_derive/src/lib.rs b/schemars_derive/src/lib.rs index 7aaee6e..547cf3c 100644 --- a/schemars_derive/src/lib.rs +++ b/schemars_derive/src/lib.rs @@ -199,7 +199,7 @@ fn schema_for_internal_tagged_enum<'a>( let field = &variant.fields[0]; let ty = get_json_schema_type(field); quote_spanned! {field.original.span()=> - <#ty>::json_schema(gen) + gen.subschema_for::<#ty>() } } Style::Struct => schema_for_struct(&variant.fields, cattrs),