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 { impl Merge for Metadata {
fn merge(self, other: Self) -> Self { fn merge(self, other: Self) -> Self {
Metadata { Metadata {
definitions: self.definitions.merge(other.definitions),
schema: self.schema.or(other.schema),
id: self.id.or(other.id), id: self.id.or(other.id),
title: self.title.or(other.title), title: self.title.or(other.title),
description: self.description.or(other.description), 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 /// 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 /// add them to the `SchemaGenerator`'s schema definitions and include them in the returned `SchemaObject`'s
/// [`definitions`](../schema/struct.Metadata.html#structfield.definitions) /// [`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 mut schema: SchemaObject = T::json_schema(self).into();
let metadata = schema.metadata(); schema.metadata().title = Some(T::schema_name());
metadata.schema = Some("http://json-schema.org/draft-07/schema#".to_owned()); RootSchema {
metadata.title = Some(T::schema_name()); meta_schema: Some("http://json-schema.org/draft-07/schema#".to_owned()),
metadata.definitions.extend(self.definitions().clone()); definitions: self.definitions().clone(),
schema schema,
}
} }
/// Consumes `self` and generates a root JSON Schema for the type `T`. /// 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 /// 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) /// 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 mut schema: SchemaObject = T::json_schema(&mut self).into();
let metadata = schema.metadata(); schema.metadata().title = Some(T::schema_name());
metadata.schema = Some("http://json-schema.org/draft-07/schema#".to_owned()); RootSchema {
metadata.title = Some(T::schema_name()); meta_schema: Some("http://json-schema.org/draft-07/schema#".to_owned()),
metadata.definitions.extend(self.into_definitions()); definitions: self.into_definitions(),
schema schema,
}
} }
/// Attemps to find the schema that the given `schema` is referencing. /// 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. /// A JSON Schema object.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "derive_json_schema", derive(JsonSchema))] #[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))] #[cfg_attr(feature = "derive_json_schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", default)] #[serde(rename_all = "camelCase", default)]
pub struct Metadata { 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. /// 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). /// 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")] #[serde(rename = "$id", skip_serializing_if = "Option::is_none")]
pub id: Option<String>, 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")] #[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>, pub title: Option<String>,
/// The `description` keyword. /// 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). /// 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")] #[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>, 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. /// The `default` keyword.
/// ///
/// See [JSON Schema Validation 9.2. "default"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.2). /// See [JSON Schema Validation 9.2. "default"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.2).

View file

@ -1,6 +1,278 @@
{ {
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"title": "Schema", "title": "RootSchema",
"type": "object",
"properties": {
"$id": {
"type": "string",
"nullable": true
},
"$ref": {
"type": "string",
"nullable": true
},
"$schema": {
"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
},
"definitions": {
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/Schema"
}
},
"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,
"definitions": { "definitions": {
"InstanceType": { "InstanceType": {
"enum": [ "enum": [
@ -34,10 +306,6 @@
"type": "string", "type": "string",
"nullable": true "nullable": true
}, },
"$schema": {
"type": "string",
"nullable": true
},
"additionalItems": { "additionalItems": {
"anyOf": [ "anyOf": [
{ {
@ -91,12 +359,6 @@
"default": { "default": {
"nullable": true "nullable": true
}, },
"definitions": {
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/Schema"
}
},
"deprecated": { "deprecated": {
"type": "boolean" "type": "boolean"
}, },
@ -297,13 +559,5 @@
}, },
"additionalProperties": true "additionalProperties": true
} }
}, }
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/components/schemas/SchemaObject"
}
]
} }

View file

@ -1,6 +1,304 @@
{ {
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"title": "Schema", "title": "RootSchema",
"type": "object",
"properties": {
"$id": {
"type": [
"string",
"null"
]
},
"$ref": {
"type": [
"string",
"null"
]
},
"$schema": {
"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,
"definitions": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/Schema"
}
},
"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,
"definitions": { "definitions": {
"InstanceType": { "InstanceType": {
"enum": [ "enum": [
@ -38,12 +336,6 @@
"null" "null"
] ]
}, },
"$schema": {
"type": [
"string",
"null"
]
},
"additionalItems": { "additionalItems": {
"anyOf": [ "anyOf": [
{ {
@ -94,12 +386,6 @@
] ]
}, },
"default": true, "default": true,
"definitions": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/Schema"
}
},
"deprecated": { "deprecated": {
"type": "boolean" "type": "boolean"
}, },
@ -349,13 +635,5 @@
} }
] ]
} }
}, }
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/definitions/SchemaObject"
}
]
} }

View file

@ -1,14 +1,14 @@
mod util; mod util;
use schemars::gen::SchemaSettings; use schemars::gen::SchemaSettings;
use schemars::schema::Schema; use schemars::schema::RootSchema;
use util::*; use util::*;
#[test] #[test]
fn schema_matches_default_settings() -> TestResult { fn schema_matches_default_settings() -> TestResult {
test_default_generated_schema::<Schema>("schema") test_default_generated_schema::<RootSchema>("schema")
} }
#[test] #[test]
fn schema_matches_openapi3() -> TestResult { 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 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::error::Error;
use std::fs; use std::fs;
use std::panic; use std::panic;
@ -18,7 +18,7 @@ pub fn test_default_generated_schema<T: JsonSchema>(file: &str) -> TestResult {
test_schema(&actual, file) 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)) { let expected_json = match fs::read_to_string(format!("tests/expected/{}.json", file)) {
Ok(j) => j, Ok(j) => j,
Err(e) => { Err(e) => {
@ -36,7 +36,7 @@ fn test_schema(actual: &SchemaObject, file: &str) -> TestResult {
Ok(()) 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)?; let actual_json = serde_json::to_string_pretty(&schema)?;
fs::write(format!("tests/actual/{}.json", file), actual_json)?; fs::write(format!("tests/actual/{}.json", file), actual_json)?;
Ok(()) Ok(())