Introduce separate RootSchema type

This commit is contained in:
Graham Esau 2019-10-23 18:23:12 +01:00
parent 5ede1c1c3a
commit ef7ec38696
7 changed files with 616 additions and 79 deletions

View file

@ -44,8 +44,6 @@ impl_merge!(SchemaObject {
impl Merge for Metadata {
fn merge(self, other: Self) -> Self {
Metadata {
definitions: self.definitions.merge(other.definitions),
schema: self.schema.or(other.schema),
id: self.id.or(other.id),
title: self.title.or(other.title),
description: self.description.or(other.description),

View file

@ -185,26 +185,28 @@ impl SchemaGenerator {
/// If `T`'s schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will
/// add them to the `SchemaGenerator`'s schema definitions and include them in the returned `SchemaObject`'s
/// [`definitions`](../schema/struct.Metadata.html#structfield.definitions)
pub fn root_schema_for<T: ?Sized + JsonSchema>(&mut self) -> SchemaObject {
pub fn root_schema_for<T: ?Sized + JsonSchema>(&mut self) -> RootSchema {
let mut schema: SchemaObject = T::json_schema(self).into();
let metadata = schema.metadata();
metadata.schema = Some("http://json-schema.org/draft-07/schema#".to_owned());
metadata.title = Some(T::schema_name());
metadata.definitions.extend(self.definitions().clone());
schema
schema.metadata().title = Some(T::schema_name());
RootSchema {
meta_schema: Some("http://json-schema.org/draft-07/schema#".to_owned()),
definitions: self.definitions().clone(),
schema,
}
}
/// Consumes `self` and generates a root JSON Schema for the type `T`.
///
/// If `T`'s schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will
/// include them in the returned `SchemaObject`'s [`definitions`](../schema/struct.Metadata.html#structfield.definitions)
pub fn into_root_schema_for<T: ?Sized + JsonSchema>(mut self) -> SchemaObject {
pub fn into_root_schema_for<T: ?Sized + JsonSchema>(mut self) -> RootSchema {
let mut schema: SchemaObject = T::json_schema(&mut self).into();
let metadata = schema.metadata();
metadata.schema = Some("http://json-schema.org/draft-07/schema#".to_owned());
metadata.title = Some(T::schema_name());
metadata.definitions.extend(self.into_definitions());
schema
schema.metadata().title = Some(T::schema_name());
RootSchema {
meta_schema: Some("http://json-schema.org/draft-07/schema#".to_owned()),
definitions: self.into_definitions(),
schema,
}
}
/// Attemps to find the schema that the given `schema` is referencing.

View file

@ -54,6 +54,28 @@ impl From<bool> for Schema {
}
}
/// The root object of a JSON Schema document.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "derive_json_schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", default)]
pub struct RootSchema {
/// The `$schema` keyword.
///
/// See [JSON Schema 8.1.1. The "$schema" Keyword](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.1.1).
#[serde(rename = "$schema", skip_serializing_if = "Option::is_none")]
pub meta_schema: Option<String>,
/// The root schema itself.
#[serde(flatten)]
pub schema: SchemaObject,
/// The `$defs` keyword.
///
/// This is currently serialized as `definitions` for backwards compatibility.
///
/// See [JSON Schema 8.2.5. Schema Re-Use With "$defs"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.5).
#[serde(alias = "$defs", skip_serializing_if = "Map::is_empty")]
pub definitions: Map<String, Schema>,
}
/// A JSON Schema object.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "derive_json_schema", derive(JsonSchema))]
@ -186,21 +208,11 @@ impl From<Schema> for SchemaObject {
#[cfg_attr(feature = "derive_json_schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", default)]
pub struct Metadata {
/// The `$schema` keyword.
///
/// This should be `Some` on a root schema, and `None` on subschemas.
///
/// See [JSON Schema 8.1.1. The "$schema" Keyword](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.1.1).
#[serde(rename = "$schema", skip_serializing_if = "Option::is_none")]
pub schema: Option<String>,
/// The `$id` keyword.
///
/// See [JSON Schema 8.2.2. The "$id" Keyword](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.2).
#[serde(rename = "$id", skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
/// The `title` keyword.
///
/// See [JSON Schema Validation 9.1. "title" and "description"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
/// The `description` keyword.
@ -208,13 +220,6 @@ pub struct Metadata {
/// See [JSON Schema Validation 9.1. "title" and "description"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
/// The `$defs` keyword.
///
/// This is currently serialized as `definitions` for backwards compatibility.
///
/// See [JSON Schema 8.2.5. Schema Re-Use With "$defs"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.5).
#[serde(alias = "$defs", skip_serializing_if = "Map::is_empty")]
pub definitions: Map<String, Schema>,
/// The `default` keyword.
///
/// See [JSON Schema Validation 9.2. "default"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.2).

View file

@ -1,29 +1,6 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Schema",
"definitions": {
"InstanceType": {
"enum": [
"null",
"boolean",
"object",
"array",
"number",
"string",
"integer"
]
},
"Schema": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/components/schemas/SchemaObject"
}
]
},
"SchemaObject": {
"title": "RootSchema",
"type": "object",
"properties": {
"$id": {
@ -295,9 +272,20 @@
"type": "boolean"
}
},
"additionalProperties": true
}
"additionalProperties": true,
"definitions": {
"InstanceType": {
"enum": [
"null",
"boolean",
"object",
"array",
"number",
"string",
"integer"
]
},
"Schema": {
"anyOf": [
{
"type": "boolean"
@ -306,4 +294,270 @@
"$ref": "#/components/schemas/SchemaObject"
}
]
},
"SchemaObject": {
"type": "object",
"properties": {
"$id": {
"type": "string",
"nullable": true
},
"$ref": {
"type": "string",
"nullable": true
},
"additionalItems": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/components/schemas/SchemaObject"
}
],
"nullable": true
},
"additionalProperties": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/components/schemas/SchemaObject"
}
],
"nullable": true
},
"allOf": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Schema"
},
"nullable": true
},
"anyOf": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Schema"
},
"nullable": true
},
"const": {
"nullable": true
},
"contains": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/components/schemas/SchemaObject"
}
],
"nullable": true
},
"default": {
"nullable": true
},
"deprecated": {
"type": "boolean"
},
"description": {
"type": "string",
"nullable": true
},
"else": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/components/schemas/SchemaObject"
}
],
"nullable": true
},
"enum": {
"type": "array",
"items": {},
"nullable": true
},
"exclusiveMaximum": {
"type": "number",
"format": "double",
"nullable": true
},
"exclusiveMinimum": {
"type": "number",
"format": "double",
"nullable": true
},
"format": {
"type": "string",
"nullable": true
},
"if": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/components/schemas/SchemaObject"
}
],
"nullable": true
},
"items": {
"anyOf": [
{
"$ref": "#/components/schemas/Schema"
},
{
"type": "array",
"items": {
"$ref": "#/components/schemas/Schema"
}
}
],
"nullable": true
},
"maxItems": {
"type": "integer",
"format": "uint32",
"nullable": true
},
"maxLength": {
"type": "integer",
"format": "uint32",
"nullable": true
},
"maxProperties": {
"type": "integer",
"format": "uint32",
"nullable": true
},
"maximum": {
"type": "number",
"format": "double",
"nullable": true
},
"minItems": {
"type": "integer",
"format": "uint32",
"nullable": true
},
"minLength": {
"type": "integer",
"format": "uint32",
"nullable": true
},
"minProperties": {
"type": "integer",
"format": "uint32",
"nullable": true
},
"minimum": {
"type": "number",
"format": "double",
"nullable": true
},
"multipleOf": {
"type": "number",
"format": "double",
"nullable": true
},
"not": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/components/schemas/SchemaObject"
}
],
"nullable": true
},
"oneOf": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Schema"
},
"nullable": true
},
"pattern": {
"type": "string",
"nullable": true
},
"patternProperties": {
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/Schema"
}
},
"properties": {
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/Schema"
}
},
"propertyNames": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/components/schemas/SchemaObject"
}
],
"nullable": true
},
"readOnly": {
"type": "boolean"
},
"required": {
"type": "array",
"items": {
"type": "string"
}
},
"then": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/components/schemas/SchemaObject"
}
],
"nullable": true
},
"title": {
"type": "string",
"nullable": true
},
"type": {
"anyOf": [
{
"$ref": "#/components/schemas/InstanceType"
},
{
"type": "array",
"items": {
"$ref": "#/components/schemas/InstanceType"
}
}
],
"nullable": true
},
"uniqueItems": {
"type": "boolean",
"nullable": true
},
"writeOnly": {
"type": "boolean"
}
},
"additionalProperties": true
}
}
}

View file

@ -1,29 +1,6 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Schema",
"definitions": {
"InstanceType": {
"enum": [
"null",
"boolean",
"object",
"array",
"number",
"string",
"integer"
]
},
"Schema": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/definitions/SchemaObject"
}
]
},
"SchemaObject": {
"title": "RootSchema",
"type": "object",
"properties": {
"$id": {
@ -321,6 +298,315 @@
"type": "boolean"
}
},
"additionalProperties": true,
"definitions": {
"InstanceType": {
"enum": [
"null",
"boolean",
"object",
"array",
"number",
"string",
"integer"
]
},
"Schema": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/definitions/SchemaObject"
}
]
},
"SchemaObject": {
"type": "object",
"properties": {
"$id": {
"type": [
"string",
"null"
]
},
"$ref": {
"type": [
"string",
"null"
]
},
"additionalItems": {
"anyOf": [
{
"$ref": "#/definitions/Schema"
},
{
"type": "null"
}
]
},
"additionalProperties": {
"anyOf": [
{
"$ref": "#/definitions/Schema"
},
{
"type": "null"
}
]
},
"allOf": {
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/Schema"
}
},
"anyOf": {
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/Schema"
}
},
"const": true,
"contains": {
"anyOf": [
{
"$ref": "#/definitions/Schema"
},
{
"type": "null"
}
]
},
"default": true,
"deprecated": {
"type": "boolean"
},
"description": {
"type": [
"string",
"null"
]
},
"else": {
"anyOf": [
{
"$ref": "#/definitions/Schema"
},
{
"type": "null"
}
]
},
"enum": {
"type": [
"array",
"null"
],
"items": true
},
"exclusiveMaximum": {
"type": [
"number",
"null"
],
"format": "double"
},
"exclusiveMinimum": {
"type": [
"number",
"null"
],
"format": "double"
},
"format": {
"type": [
"string",
"null"
]
},
"if": {
"anyOf": [
{
"$ref": "#/definitions/Schema"
},
{
"type": "null"
}
]
},
"items": {
"anyOf": [
{
"$ref": "#/definitions/SingleOrVec_For_Schema"
},
{
"type": "null"
}
]
},
"maxItems": {
"type": [
"integer",
"null"
],
"format": "uint32"
},
"maxLength": {
"type": [
"integer",
"null"
],
"format": "uint32"
},
"maxProperties": {
"type": [
"integer",
"null"
],
"format": "uint32"
},
"maximum": {
"type": [
"number",
"null"
],
"format": "double"
},
"minItems": {
"type": [
"integer",
"null"
],
"format": "uint32"
},
"minLength": {
"type": [
"integer",
"null"
],
"format": "uint32"
},
"minProperties": {
"type": [
"integer",
"null"
],
"format": "uint32"
},
"minimum": {
"type": [
"number",
"null"
],
"format": "double"
},
"multipleOf": {
"type": [
"number",
"null"
],
"format": "double"
},
"not": {
"anyOf": [
{
"$ref": "#/definitions/Schema"
},
{
"type": "null"
}
]
},
"oneOf": {
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/Schema"
}
},
"pattern": {
"type": [
"string",
"null"
]
},
"patternProperties": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/Schema"
}
},
"properties": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/Schema"
}
},
"propertyNames": {
"anyOf": [
{
"$ref": "#/definitions/Schema"
},
{
"type": "null"
}
]
},
"readOnly": {
"type": "boolean"
},
"required": {
"type": "array",
"items": {
"type": "string"
}
},
"then": {
"anyOf": [
{
"$ref": "#/definitions/Schema"
},
{
"type": "null"
}
]
},
"title": {
"type": [
"string",
"null"
]
},
"type": {
"anyOf": [
{
"$ref": "#/definitions/SingleOrVec_For_InstanceType"
},
{
"type": "null"
}
]
},
"uniqueItems": {
"type": [
"boolean",
"null"
]
},
"writeOnly": {
"type": "boolean"
}
},
"additionalProperties": true
},
"SingleOrVec_For_InstanceType": {
@ -349,13 +635,5 @@
}
]
}
},
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/definitions/SchemaObject"
}
]
}

View file

@ -1,14 +1,14 @@
mod util;
use schemars::gen::SchemaSettings;
use schemars::schema::Schema;
use schemars::schema::RootSchema;
use util::*;
#[test]
fn schema_matches_default_settings() -> TestResult {
test_default_generated_schema::<Schema>("schema")
test_default_generated_schema::<RootSchema>("schema")
}
#[test]
fn schema_matches_openapi3() -> TestResult {
test_generated_schema::<Schema>("schema-openapi3", SchemaSettings::openapi3())
test_generated_schema::<RootSchema>("schema-openapi3", SchemaSettings::openapi3())
}

View file

@ -1,5 +1,5 @@
use pretty_assertions::assert_eq;
use schemars::{gen::SchemaSettings, schema::SchemaObject, schema_for, JsonSchema};
use schemars::{gen::SchemaSettings, schema::RootSchema, schema_for, JsonSchema};
use std::error::Error;
use std::fs;
use std::panic;
@ -18,7 +18,7 @@ pub fn test_default_generated_schema<T: JsonSchema>(file: &str) -> TestResult {
test_schema(&actual, file)
}
fn test_schema(actual: &SchemaObject, file: &str) -> TestResult {
fn test_schema(actual: &RootSchema, file: &str) -> TestResult {
let expected_json = match fs::read_to_string(format!("tests/expected/{}.json", file)) {
Ok(j) => j,
Err(e) => {
@ -36,7 +36,7 @@ fn test_schema(actual: &SchemaObject, file: &str) -> TestResult {
Ok(())
}
fn write_actual_to_file(schema: &SchemaObject, file: &str) -> TestResult {
fn write_actual_to_file(schema: &RootSchema, file: &str) -> TestResult {
let actual_json = serde_json::to_string_pretty(&schema)?;
fs::write(format!("tests/actual/{}.json", file), actual_json)?;
Ok(())