diff --git a/schemars/src/flatten.rs b/schemars/src/flatten.rs index 31524c4..529b34d 100644 --- a/schemars/src/flatten.rs +++ b/schemars/src/flatten.rs @@ -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), diff --git a/schemars/src/gen.rs b/schemars/src/gen.rs index c249536..9e2b6bb 100644 --- a/schemars/src/gen.rs +++ b/schemars/src/gen.rs @@ -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(&mut self) -> SchemaObject { + pub fn root_schema_for(&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(mut self) -> SchemaObject { + pub fn into_root_schema_for(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. diff --git a/schemars/src/schema.rs b/schemars/src/schema.rs index 47fe3ba..f1c2ed0 100644 --- a/schemars/src/schema.rs +++ b/schemars/src/schema.rs @@ -54,6 +54,28 @@ impl From 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, + /// 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, +} + /// 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 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, /// 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, - /// 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, /// 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, - /// 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, /// The `default` keyword. /// /// See [JSON Schema Validation 9.2. "default"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.2). diff --git a/schemars/tests/expected/schema-openapi3.json b/schemars/tests/expected/schema-openapi3.json index e091416..a5bce92 100644 --- a/schemars/tests/expected/schema-openapi3.json +++ b/schemars/tests/expected/schema-openapi3.json @@ -1,6 +1,278 @@ { "$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": { "InstanceType": { "enum": [ @@ -34,10 +306,6 @@ "type": "string", "nullable": true }, - "$schema": { - "type": "string", - "nullable": true - }, "additionalItems": { "anyOf": [ { @@ -91,12 +359,6 @@ "default": { "nullable": true }, - "definitions": { - "type": "object", - "additionalProperties": { - "$ref": "#/components/schemas/Schema" - } - }, "deprecated": { "type": "boolean" }, @@ -297,13 +559,5 @@ }, "additionalProperties": true } - }, - "anyOf": [ - { - "type": "boolean" - }, - { - "$ref": "#/components/schemas/SchemaObject" - } - ] + } } \ No newline at end of file diff --git a/schemars/tests/expected/schema.json b/schemars/tests/expected/schema.json index 009b6b8..ef9dd24 100644 --- a/schemars/tests/expected/schema.json +++ b/schemars/tests/expected/schema.json @@ -1,6 +1,304 @@ { "$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": { "InstanceType": { "enum": [ @@ -38,12 +336,6 @@ "null" ] }, - "$schema": { - "type": [ - "string", - "null" - ] - }, "additionalItems": { "anyOf": [ { @@ -94,12 +386,6 @@ ] }, "default": true, - "definitions": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Schema" - } - }, "deprecated": { "type": "boolean" }, @@ -349,13 +635,5 @@ } ] } - }, - "anyOf": [ - { - "type": "boolean" - }, - { - "$ref": "#/definitions/SchemaObject" - } - ] + } } \ No newline at end of file diff --git a/schemars/tests/schema_for_schema.rs b/schemars/tests/schema_for_schema.rs index 3bc0411..0e2debb 100644 --- a/schemars/tests/schema_for_schema.rs +++ b/schemars/tests/schema_for_schema.rs @@ -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") + test_default_generated_schema::("schema") } #[test] fn schema_matches_openapi3() -> TestResult { - test_generated_schema::("schema-openapi3", SchemaSettings::openapi3()) + test_generated_schema::("schema-openapi3", SchemaSettings::openapi3()) } diff --git a/schemars/tests/util/mod.rs b/schemars/tests/util/mod.rs index 22e893b..fa191fe 100644 --- a/schemars/tests/util/mod.rs +++ b/schemars/tests/util/mod.rs @@ -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(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(())