From 9eea144f1fcbda864785245a1ef8baa7d2e2e2c5 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Sun, 12 May 2024 19:23:54 +0100 Subject: [PATCH 01/31] Define `Schema` as a newtype around `serde_json::Value` (#289) --- .github/workflows/ci.yml | 2 +- Cargo.lock | 110 +-- schemars/Cargo.toml | 85 +-- schemars/examples/custom_serialization.rs | 10 +- schemars/src/_private.rs | 172 +++-- schemars/src/flatten.rs | 300 +++----- schemars/src/gen.rs | 237 +++---- schemars/src/json_schema_impls/array.rs | 70 +- schemars/src/json_schema_impls/arrayvec05.rs | 37 - schemars/src/json_schema_impls/arrayvec07.rs | 19 +- schemars/src/json_schema_impls/atomic.rs | 12 +- schemars/src/json_schema_impls/bytes.rs | 7 - .../{chrono.rs => chrono04.rs} | 42 +- schemars/src/json_schema_impls/core.rs | 235 +++--- schemars/src/json_schema_impls/decimal.rs | 22 +- .../{either.rs => either1.rs} | 11 +- schemars/src/json_schema_impls/enumset.rs | 6 - schemars/src/json_schema_impls/ffi.rs | 42 +- schemars/src/json_schema_impls/indexmap.rs | 8 - schemars/src/json_schema_impls/indexmap2.rs | 2 - schemars/src/json_schema_impls/maps.rs | 17 +- schemars/src/json_schema_impls/mod.rs | 84 +-- .../src/json_schema_impls/nonzero_signed.rs | 17 +- .../src/json_schema_impls/nonzero_unsigned.rs | 24 +- schemars/src/json_schema_impls/primitives.rs | 129 ++-- schemars/src/json_schema_impls/semver.rs | 30 - schemars/src/json_schema_impls/semver1.rs | 24 + schemars/src/json_schema_impls/sequences.rs | 31 +- schemars/src/json_schema_impls/serdejson.rs | 13 +- schemars/src/json_schema_impls/smallvec.rs | 6 - schemars/src/json_schema_impls/smol_str.rs | 6 - schemars/src/json_schema_impls/time.rs | 43 +- schemars/src/json_schema_impls/tuple.rs | 51 +- .../src/json_schema_impls/{url.rs => url2.rs} | 15 +- schemars/src/json_schema_impls/uuid08.rs | 26 - schemars/src/json_schema_impls/uuid1.rs | 13 +- schemars/src/json_schema_impls/wrapper.rs | 2 - schemars/src/lib.rs | 55 +- schemars/src/macros.rs | 16 + schemars/src/schema.rs | 669 +++++------------- schemars/src/ser.rs | 215 +++--- schemars/src/visit.rs | 221 +++--- schemars/tests/arrayvec.rs | 10 - schemars/tests/bytes.rs | 2 +- schemars/tests/chrono.rs | 2 +- schemars/tests/decimal.rs | 7 +- schemars/tests/dereference.rs | 24 - schemars/tests/either.rs | 2 +- schemars/tests/enum.rs | 15 +- schemars/tests/enum_deny_unknown_fields.rs | 15 +- schemars/tests/enumset.rs | 5 +- schemars/tests/expected/arrayvec_string.json | 2 +- schemars/tests/expected/bigdecimal03.json | 6 - schemars/tests/expected/either.json | 2 +- .../tests/expected/enum-internal-duf.json | 3 +- schemars/tests/expected/range.json | 4 +- .../tests/expected/remote_derive_generic.json | 8 +- schemars/tests/expected/result.json | 8 +- .../tests/expected/schema-name-custom.json | 2 +- .../tests/expected/schema-name-default.json | 30 +- .../expected/schema-name-mixed-generics.json | 6 +- .../expected/schema_with-enum-internal.json | 8 +- schemars/tests/expected/smallvec.json | 2 +- schemars/tests/expected/smol_str.json | 2 +- schemars/tests/indexmap.rs | 8 +- schemars/tests/indexmap2.rs | 17 - schemars/tests/schema_for_schema.rs | 20 - schemars/tests/schema_with_enum.rs | 6 +- schemars/tests/schema_with_struct.rs | 2 +- schemars/tests/semver.rs | 2 +- schemars/tests/smallvec.rs | 2 +- schemars/tests/smol_str.rs | 2 +- schemars/tests/url.rs | 2 +- schemars/tests/util/mod.rs | 38 +- schemars/tests/uuid.rs | 5 - schemars_derive/src/attr/validation.rs | 171 ++--- schemars_derive/src/lib.rs | 6 +- schemars_derive/src/metadata.rs | 61 +- schemars_derive/src/schema_exprs.rs | 143 ++-- 79 files changed, 1362 insertions(+), 2424 deletions(-) delete mode 100644 schemars/src/json_schema_impls/arrayvec05.rs delete mode 100644 schemars/src/json_schema_impls/bytes.rs rename schemars/src/json_schema_impls/{chrono.rs => chrono04.rs} (61%) rename schemars/src/json_schema_impls/{either.rs => either1.rs} (67%) delete mode 100644 schemars/src/json_schema_impls/enumset.rs delete mode 100644 schemars/src/json_schema_impls/indexmap.rs delete mode 100644 schemars/src/json_schema_impls/semver.rs create mode 100644 schemars/src/json_schema_impls/semver1.rs delete mode 100644 schemars/src/json_schema_impls/smallvec.rs delete mode 100644 schemars/src/json_schema_impls/smol_str.rs rename schemars/src/json_schema_impls/{url.rs => url2.rs} (56%) delete mode 100644 schemars/src/json_schema_impls/uuid08.rs delete mode 100644 schemars/tests/dereference.rs delete mode 100644 schemars/tests/expected/bigdecimal03.json delete mode 100644 schemars/tests/indexmap2.rs delete mode 100644 schemars/tests/schema_for_schema.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d37634e..7c72306 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: - nightly include: - rust: 1.60.0 - test_features: "--features impl_json_schema" + test_features: "" allow_failure: false - rust: stable test_features: "--all-features" diff --git a/Cargo.lock b/Cargo.lock index 73b75c5..6a57a49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,19 +7,16 @@ name = "apistos-schemars" version = "0.8.21" dependencies = [ "apistos-schemars_derive", - "arrayvec 0.5.2", - "arrayvec 0.7.4", - "bigdecimal 0.3.1", - "bigdecimal 0.4.2", + "arrayvec", + "bigdecimal", "bytes", "chrono", "dyn-clone", "either", "enumset", - "indexmap 1.9.3", - "indexmap 2.0.2", - "ipnetwork", + "indexmap", "pretty_assertions", + "ref-cast", "rust_decimal", "semver", "serde", @@ -28,8 +25,7 @@ dependencies = [ "smol_str", "trybuild", "url", - "uuid 0.8.2", - "uuid 1.5.0", + "uuid", ] [[package]] @@ -43,12 +39,6 @@ dependencies = [ "syn", ] -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - [[package]] name = "arrayvec" version = "0.7.4" @@ -70,17 +60,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bigdecimal" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - [[package]] name = "bigdecimal" version = "0.4.2" @@ -209,12 +188,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hashbrown" version = "0.14.2" @@ -237,17 +210,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - [[package]] name = "indexmap" version = "2.0.2" @@ -255,17 +217,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" dependencies = [ "equivalent", - "hashbrown 0.14.2", - "serde", -] - -[[package]] -name = "ipnetwork" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" -dependencies = [ - "serde", + "hashbrown", ] [[package]] @@ -334,20 +286,40 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ref-cast" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", + "quote", + "syn", ] [[package]] @@ -356,7 +328,7 @@ version = "1.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4c4216490d5a413bc6d10fa4742bd7d4955941d062c0ef873141d6b0e7b30fd" dependencies = [ - "arrayvec 0.7.4", + "arrayvec", "num-traits", ] @@ -371,9 +343,6 @@ name = "semver" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" -dependencies = [ - "serde", -] [[package]] name = "serde" @@ -425,18 +394,15 @@ checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "smol_str" -version = "0.1.24" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad6c857cbab2627dcf01ec85a623ca4e7dcb5691cbaa3d7fb7653671f0d09c9" -dependencies = [ - "serde", -] +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" [[package]] name = "syn" -version = "2.0.38" +version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" dependencies = [ "proc-macro2", "quote", @@ -514,12 +480,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "uuid" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" - [[package]] name = "uuid" version = "1.5.0" diff --git a/schemars/Cargo.toml b/schemars/Cargo.toml index d17d893..ea12342 100644 --- a/schemars/Cargo.toml +++ b/schemars/Cargo.toml @@ -13,69 +13,50 @@ rust-version = "1.60" [dependencies] schemars_derive = { package = "apistos-schemars_derive", version = "=0.8.21", optional = true, path = "../schemars_derive" } -serde = { version = "1.0", features = ["derive"] } +serde = "1.0" serde_json = "1.0.25" dyn-clone = "1.0" +ref-cast = "1.0.22" -chrono = { version = "0.4", default-features = false, optional = true } -indexmap = { version = "1.2", features = ["serde-1"], optional = true } -indexmap2 = { version = "2.0", features = ["serde"], optional = true, package = "indexmap" } -ipnetwork = { version = "0.20", optional = true } -either = { version = "1.3", default-features = false, optional = true } -uuid08 = { version = "0.8", default-features = false, optional = true, package = "uuid" } +# optional dependencies +chrono04 = { version = "0.4", default-features = false, optional = true, package = "chrono" } +indexmap2 = { version = "2.0", default-features = false, optional = true, package = "indexmap" } +either1 = { version = "1.3", default-features = false, optional = true, package = "either" } uuid1 = { version = "1.0", default-features = false, optional = true, package = "uuid" } -smallvec = { version = "1.0", optional = true } -arrayvec05 = { version = "0.5", default-features = false, optional = true, package = "arrayvec" } +smallvec1 = { version = "1.0", default-features = false, optional = true, package = "smallvec" } arrayvec07 = { version = "0.7", default-features = false, optional = true, package = "arrayvec" } -url = { version = "2.0", default-features = false, optional = true } -bytes = { version = "1.0", optional = true } -rust_decimal = { version = "1", default-features = false, optional = true } -bigdecimal03 = { version = "0.3", default-features = false, optional = true, package = "bigdecimal" } +url2 = { version = "2.0", default-features = false, optional = true, package = "url" } +bytes1 = { version = "1.0", default-features = false, optional = true, package = "bytes" } +rust_decimal1 = { version = "1", default-features = false, optional = true, package = "rust_decimal"} bigdecimal04 = { version = "0.4", default-features = false, optional = true, package = "bigdecimal" } -enumset = { version = "1.0", optional = true } -smol_str = { version = "0.1.17", optional = true } -semver = { version = "1.0.9", features = ["serde"], optional = true } +enumset1 = { version = "1.0", default-features = false, optional = true, package = "enumset" } +smol_str02 = { version = "0.2.1", default-features = false, optional = true, package = "smol_str" } +semver1 = { version = "1.0.9", default-features = false, optional = true, package = "semver" } [dev-dependencies] pretty_assertions = "1.2.1" trybuild = "1.0" +serde = { version = "1.0", features = ["derive"] } [features] default = ["derive"] derive = ["schemars_derive"] -# Use a different representation for the map type of Schemars. -# This allows data to be read into a Value and written back to a JSON string -# while preserving the order of map keys in the input. -preserve_order = ["indexmap"] - -impl_json_schema = ["derive"] -# derive_json_schema will be removed in a later version -derive_json_schema = ["impl_json_schema"] - -# `uuid` feature contains `uuid08` only for back-compat - will be changed to include uuid 1.0 instead in a later version -uuid = ["uuid08"] -# `arrayvec` feature without version suffix is included only for back-compat - will be removed in a later version -arrayvec = ["arrayvec05"] -indexmap1 = ["indexmap"] - raw_value = ["serde_json/raw_value"] -# `bigdecimal` feature without version suffix is included only for back-compat - will be removed in a later version -bigdecimal = ["bigdecimal03"] ui_test = [] [[test]] -name = "chrono" -required-features = ["chrono"] +name = "ui" +required-features = ["ui_test"] [[test]] -name = "indexmap" -required-features = ["indexmap"] +name = "chrono" +required-features = ["chrono04"] [[test]] -name = "indexmap2" +name = "indexmap" required-features = ["indexmap2"] [[test]] @@ -84,51 +65,43 @@ required-features = ["ipnetwork"] [[test]] name = "either" -required-features = ["either"] +required-features = ["either1"] [[test]] name = "uuid" -required-features = ["uuid08", "uuid1"] +required-features = ["uuid1"] [[test]] name = "smallvec" -required-features = ["smallvec"] +required-features = ["smallvec1"] [[test]] name = "bytes" -required-features = ["bytes"] +required-features = ["bytes1"] [[test]] name = "arrayvec" -required-features = ["arrayvec05", "arrayvec07"] - -[[test]] -name = "schema_for_schema" -required-features = ["impl_json_schema"] - -[[test]] -name = "ui" -required-features = ["ui_test"] +required-features = ["arrayvec07"] [[test]] name = "url" -required-features = ["url"] +required-features = ["url2"] [[test]] name = "enumset" -required-features = ["enumset"] +required-features = ["enumset1"] [[test]] name = "smol_str" -required-features = ["smol_str"] +required-features = ["smol_str02"] [[test]] name = "semver" -required-features = ["semver"] +required-features = ["semver1"] [[test]] name = "decimal" -required-features = ["rust_decimal", "bigdecimal03", "bigdecimal04"] +required-features = ["rust_decimal1", "bigdecimal04"] [package.metadata.docs.rs] all-features = true diff --git a/schemars/examples/custom_serialization.rs b/schemars/examples/custom_serialization.rs index 7cb0568..7ef6ced 100644 --- a/schemars/examples/custom_serialization.rs +++ b/schemars/examples/custom_serialization.rs @@ -1,5 +1,5 @@ extern crate apistos_schemars as schemars; -use schemars::schema::{Schema, SchemaObject}; +use schemars::Schema; use schemars::{gen::SchemaGenerator, schema_for, JsonSchema}; use serde::{Deserialize, Serialize}; @@ -22,9 +22,11 @@ pub struct MyStruct { } fn make_custom_schema(gen: &mut SchemaGenerator) -> Schema { - let mut schema: SchemaObject = ::json_schema(gen).into(); - schema.format = Some("boolean".to_owned()); - schema.into() + let mut schema = String::json_schema(gen); + schema + .ensure_object() + .insert("format".into(), "boolean".into()); + schema } fn eight() -> i32 { diff --git a/schemars/src/_private.rs b/schemars/src/_private.rs index 5b03178..26c23a8 100644 --- a/schemars/src/_private.rs +++ b/schemars/src/_private.rs @@ -1,7 +1,8 @@ use crate::gen::SchemaGenerator; -use crate::schema::{InstanceType, ObjectValidation, Schema, SchemaObject}; -use crate::{JsonSchema, Map, Set}; +use crate::JsonSchema; +use crate::Schema; use serde::Serialize; +use serde_json::Map; use serde_json::Value; // Helper for generating schemas for flattened `Option` fields. @@ -12,12 +13,8 @@ pub fn json_schema_for_flatten( let mut schema = T::_schemars_private_non_optional_json_schema(gen); if T::_schemars_private_is_option() && !required { - if let Schema::Object(SchemaObject { - object: Some(ref mut object_validation), - .. - }) = schema - { - object_validation.required.clear(); + if let Some(object) = schema.as_object_mut() { + object.remove("required"); } } @@ -57,38 +54,22 @@ impl MaybeSerializeWrapper { /// Create a schema for a unit enum pub fn new_unit_enum(variant: &str) -> Schema { - Schema::Object(SchemaObject { - instance_type: Some(InstanceType::String.into()), - enum_values: Some(vec![variant.into()]), - ..SchemaObject::default() + // TODO switch from single-valued "enum" to "const" + json_schema!({ + "type": "string", + "enum": [variant], }) } /// Create a schema for an externally tagged enum pub fn new_externally_tagged_enum(variant: &str, sub_schema: Schema) -> Schema { - Schema::Object(SchemaObject { - instance_type: Some(InstanceType::Object.into()), - object: Some(Box::new(ObjectValidation { - properties: { - let mut props = Map::new(); - props.insert(variant.to_owned(), sub_schema); - props - }, - required: { - let mut required = Set::new(); - required.insert(variant.to_owned()); - required - }, - // Externally tagged variants must prohibit additional - // properties irrespective of the disposition of - // `deny_unknown_fields`. If additional properties were allowed - // one could easily construct an object that validated against - // multiple variants since here it's the properties rather than - // the values of a property that distingish between variants. - additional_properties: Some(Box::new(false.into())), - ..Default::default() - })), - ..SchemaObject::default() + json_schema!({ + "type": "object", + "properties": { + variant: sub_schema + }, + "required": [variant], + "additionalProperties": false, }) } @@ -98,74 +79,87 @@ pub fn new_internally_tagged_enum( variant: &str, deny_unknown_fields: bool, ) -> Schema { - let tag_schema = Schema::Object(SchemaObject { - instance_type: Some(InstanceType::String.into()), - enum_values: Some(vec![variant.into()]), - ..Default::default() + // TODO switch from single-valued "enum" to "const" + let mut schema = json_schema!({ + "type": "object", + "properties": { + tag_name: { + "type": "string", + "enum": [variant], + } + }, + "required": [tag_name], }); - Schema::Object(SchemaObject { - instance_type: Some(InstanceType::Object.into()), - object: Some(Box::new(ObjectValidation { - properties: { - let mut props = Map::new(); - props.insert(tag_name.to_owned(), tag_schema); - props - }, - required: { - let mut required = Set::new(); - required.insert(tag_name.to_owned()); - required - }, - additional_properties: deny_unknown_fields.then(|| Box::new(false.into())), - ..Default::default() - })), - ..SchemaObject::default() - }) + + if deny_unknown_fields { + schema + .as_object_mut() + .unwrap() + .insert("additionalProperties".into(), false.into()); + } + + schema } pub fn insert_object_property( - obj: &mut ObjectValidation, + schema: &mut Schema, key: &str, has_default: bool, required: bool, - schema: Schema, + sub_schema: Schema, ) { - obj.properties.insert(key.to_owned(), schema); + let obj = schema.ensure_object(); + if let Some(properties) = obj + .entry("properties") + .or_insert(Value::Object(Map::new())) + .as_object_mut() + { + properties.insert(key.to_owned(), sub_schema.into()); + } + if !has_default && (required || !T::_schemars_private_is_option()) { - obj.required.insert(key.to_owned()); + if let Some(req) = obj + .entry("required") + .or_insert(Value::Array(Vec::new())) + .as_array_mut() + { + req.push(key.into()); + } } } -pub mod metadata { - use crate::Schema; - use serde_json::Value; - - macro_rules! add_metadata_fn { - ($method:ident, $name:ident, $ty:ty) => { - pub fn $method(schema: Schema, $name: impl Into<$ty>) -> Schema { - let value = $name.into(); - if value == <$ty>::default() { - schema - } else { - let mut schema_obj = schema.into_object(); - schema_obj.metadata().$name = value.into(); - Schema::Object(schema_obj) - } +pub fn insert_validation_property( + schema: &mut Schema, + required_type: &str, + key: &str, + value: impl Into, +) { + if schema.has_type(required_type) || (required_type == "number" && schema.has_type("integer")) { + schema.ensure_object().insert(key.to_owned(), value.into()); + } +} + +pub fn append_required(schema: &mut Schema, key: &str) { + if schema.has_type("object") { + if let Value::Array(array) = schema + .ensure_object() + .entry("required") + .or_insert(Value::Array(Vec::new())) + { + let value = Value::from(key); + if !array.contains(&value) { + array.push(value); } - }; + } } +} - add_metadata_fn!(add_description, description, String); - add_metadata_fn!(add_id, id, String); - add_metadata_fn!(add_title, title, String); - add_metadata_fn!(add_deprecated, deprecated, bool); - add_metadata_fn!(add_read_only, read_only, bool); - add_metadata_fn!(add_write_only, write_only, bool); - add_metadata_fn!(add_default, default, Option); - - pub fn add_examples>(schema: Schema, examples: I) -> Schema { - let mut schema_obj = schema.into_object(); - schema_obj.metadata().examples.extend(examples); - Schema::Object(schema_obj) +pub fn apply_inner_validation(schema: &mut Schema, f: fn(&mut Schema) -> ()) { + if let Some(inner_schema) = schema + .as_object_mut() + .and_then(|o| o.get_mut("items")) + .and_then(|i| <&mut Schema>::try_from(i).ok()) + { + f(inner_schema); } } diff --git a/schemars/src/flatten.rs b/schemars/src/flatten.rs index 68a2068..fc66b74 100644 --- a/schemars/src/flatten.rs +++ b/schemars/src/flatten.rs @@ -1,237 +1,111 @@ -use crate::schema::*; -use crate::{Map, Set}; +use serde_json::map::Entry; +use serde_json::Value; + +use crate::Schema; impl Schema { /// This function is only public for use by schemars_derive. /// /// It should not be considered part of the public API. #[doc(hidden)] - pub fn flatten(self, other: Self) -> Schema { - if is_null_type(&self) { - return other; - } else if is_null_type(&other) { + pub fn flatten(mut self, other: Self) -> Schema { + // This special null-type-schema handling is here for backward-compatibility, but needs reviewing. + // I think it's only needed to make internally-tagged enum unit variants behave correctly, but that + // should be handled entirely within schemars_derive. + if other + .as_object() + .and_then(|o| o.get("type")) + .and_then(|t| t.as_str()) + == Some("null") + { return self; } - let mut s1: SchemaObject = self.into(); - let mut s2: SchemaObject = other.into(); - - let s1_subschemas = &mut s1.subschemas; - let s1_all_of = s1_subschemas.as_mut().and_then(|sub_sch| sub_sch.all_of.as_mut()); - let s2_subschemas = &mut s2.subschemas; - let s2_all_of = s2_subschemas.as_mut().and_then(|sub_sch| sub_sch.all_of.as_mut()); - match (s1_all_of, s2_all_of) { - (Some(s1_all_of), Some(s2_all_of)) => { - let mut s1_all_of = std::mem::take(s1_all_of); - s1_all_of.append(s2_all_of); - Schema::Object(SchemaObject { - subschemas: Some(Box::new(SubschemaValidation { - all_of: Some(s1_all_of), - ..Default::default() - })), - ..Default::default() - }) - } - (Some(s1_all_of), None) => { - let mut s1_all_of = std::mem::take(s1_all_of); - s1_all_of.push(Schema::Object(s2)); - Schema::Object(SchemaObject { - subschemas: Some(Box::new(SubschemaValidation { - all_of: Some(s1_all_of), - ..Default::default() - })), - ..Default::default() - }) - } - (None, Some(s2_all_of)) => { - let mut s2_all_of = std::mem::take(s2_all_of); - s2_all_of.push(Schema::Object(s1)); - Schema::Object(SchemaObject { - subschemas: Some(Box::new(SubschemaValidation { - all_of: Some(s2_all_of), - ..Default::default() - })), - ..Default::default() - }) - } - (None, None) => { - match (s1_subschemas.is_some(), s2_subschemas.is_some()) { - (true, true) => Schema::Object(SchemaObject { - subschemas: Some(Box::new(SubschemaValidation { - all_of: Some(vec![ - Schema::Object(s1), - Schema::Object(s2), - ]), - ..Default::default() - })), - ..Default::default() - }), - (true, false) | - (false, true) | - (false, false) => Schema::Object(s1.merge(s2)), + if let Value::Object(mut obj2) = other.to_value() { + let obj1 = self.ensure_object(); + + let ap2 = obj2.remove("additionalProperties"); + if let Entry::Occupied(mut ap1) = obj1.entry("additionalProperties") { + match ap2 { + Some(ap2) => { + flatten_additional_properties(ap1.get_mut(), ap2); + } + None => { + ap1.remove(); + } } } - } - } -} - -pub(crate) trait Merge: Sized { - fn merge(self, other: Self) -> Self; -} -macro_rules! impl_merge { - ($ty:ident { merge: $($merge_field:ident)*, or: $($or_field:ident)*, }) => { - impl Merge for $ty { - fn merge(self, other: Self) -> Self { - $ty { - $($merge_field: self.$merge_field.merge(other.$merge_field),)* - $($or_field: self.$or_field.or(other.$or_field),)* + for (key, value2) in obj2 { + match obj1.entry(key) { + Entry::Vacant(vacant) => { + vacant.insert(value2); + } + Entry::Occupied(mut occupied) => { + match occupied.key().as_str() { + // This special "type" handling can probably be removed once the enum variant `with`/`schema_with` behaviour is fixed + "type" => match (occupied.get_mut(), value2) { + (Value::Array(a1), Value::Array(mut a2)) => { + a2.retain(|v2| !a1.contains(v2)); + a1.extend(a2); + } + (v1, Value::Array(mut a2)) => { + if !a2.contains(v1) { + a2.push(std::mem::take(v1)); + *occupied.get_mut() = Value::Array(a2); + } + } + (Value::Array(a1), v2) => { + if !a1.contains(&v2) { + a1.push(v2); + } + } + (v1, v2) => { + if v1 != &v2 { + *occupied.get_mut() = + Value::Array(vec![std::mem::take(v1), v2]); + } + } + }, + "required" => { + if let Value::Array(a1) = occupied.into_mut() { + if let Value::Array(a2) = value2 { + a1.extend(a2); + } + } + } + "properties" | "patternProperties" => { + if let Value::Object(o1) = occupied.into_mut() { + if let Value::Object(o2) = value2 { + o1.extend(o2); + } + } + } + _ => { + // leave the original value as it is (don't modify `self`) + } + }; + } } } } - }; - ($ty:ident { or: $($or_field:ident)*, }) => { - impl_merge!( $ty { merge: , or: $($or_field)*, }); - }; -} - -// For ObjectValidation::additional_properties. -impl Merge for Option> { - fn merge(self, other: Self) -> Self { - match (self.map(|x| *x), other.map(|x| *x)) { - // Perfer permissive schemas. - (Some(Schema::Bool(true)), _) => Some(Box::new(true.into())), - (_, Some(Schema::Bool(true))) => Some(Box::new(true.into())), - (None, _) => None, - (_, None) => None, - // Merge if we have two non-trivial schemas. - (Some(Schema::Object(s1)), Some(Schema::Object(s2))) => { - Some(Box::new(Schema::Object(s1.merge(s2)))) - } - - // Perfer the more permissive schema. - (Some(s1 @ Schema::Object(_)), Some(Schema::Bool(false))) => Some(Box::new(s1)), - (Some(Schema::Bool(false)), Some(s2 @ Schema::Object(_))) => Some(Box::new(s2)), - - // Default to the null schema. - (Some(Schema::Bool(false)), Some(Schema::Bool(false))) => Some(Box::new(false.into())), - } + self } } -impl_merge!(SchemaObject { - merge: extensions instance_type enum_values - metadata subschemas number string array object, - or: format const_value reference, -}); - -impl Merge for Metadata { - fn merge(self, other: Self) -> Self { - Metadata { - id: self.id.or(other.id), - title: self.title.or(other.title), - description: self.description.or(other.description), - default: self.default.or(other.default), - deprecated: self.deprecated || other.deprecated, - read_only: self.read_only || other.read_only, - write_only: self.write_only || other.write_only, - examples: self.examples.merge(other.examples), +// TODO validate behaviour when flattening a normal struct into a struct with deny_unknown_fields +fn flatten_additional_properties(v1: &mut Value, v2: Value) { + match (v1, v2) { + (v1, Value::Bool(true)) => { + *v1 = Value::Bool(true); } - } -} - -impl_merge!(SubschemaValidation { - or: all_of any_of one_of not if_schema then_schema else_schema, -}); - -impl_merge!(NumberValidation { - or: multiple_of maximum exclusive_maximum minimum exclusive_minimum, -}); - -impl_merge!(StringValidation { - or: max_length min_length pattern, -}); - -impl_merge!(ArrayValidation { - or: items additional_items max_items min_items unique_items contains, -}); - -impl_merge!(ObjectValidation { - merge: required properties pattern_properties additional_properties, - or: max_properties min_properties property_names, -}); - -impl Merge for Option { - fn merge(self, other: Self) -> Self { - match (self, other) { - (Some(x), Some(y)) => Some(x.merge(y)), - (None, y) => y, - (x, None) => x, + (v1 @ Value::Bool(false), v2) => { + *v1 = v2; } - } -} - -impl Merge for Box { - fn merge(mut self, other: Self) -> Self { - *self = (*self).merge(*other); - self - } -} - -impl Merge for Vec { - fn merge(mut self, other: Self) -> Self { - self.extend(other); - self - } -} - -impl Merge for Map -where - K: std::hash::Hash + Eq + Ord, -{ - fn merge(mut self, other: Self) -> Self { - self.extend(other); - self - } -} - -impl Merge for Set { - fn merge(mut self, other: Self) -> Self { - self.extend(other); - self - } -} - -impl Merge for SingleOrVec { - fn merge(self, other: Self) -> Self { - if self == other { - return self; + (Value::Object(o1), Value::Object(o2)) => { + o1.extend(o2); } - let mut vec = match (self, other) { - (SingleOrVec::Vec(v1), SingleOrVec::Vec(v2)) => v1.merge(v2), - (SingleOrVec::Vec(mut v), SingleOrVec::Single(s)) - | (SingleOrVec::Single(s), SingleOrVec::Vec(mut v)) => { - v.push(*s); - v - } - (SingleOrVec::Single(s1), SingleOrVec::Single(s2)) => vec![*s1, *s2], - }; - vec.sort(); - vec.dedup(); - SingleOrVec::Vec(vec) + _ => {} } } - -fn is_null_type(schema: &Schema) -> bool { - let s = match schema { - Schema::Object(s) => s, - _ => return false, - }; - let instance_type = match &s.instance_type { - Some(SingleOrVec::Single(t)) => t, - _ => return false, - }; - - **instance_type == InstanceType::Null -} diff --git a/schemars/src/gen.rs b/schemars/src/gen.rs index 9a528de..040f798 100644 --- a/schemars/src/gen.rs +++ b/schemars/src/gen.rs @@ -7,12 +7,12 @@ There are two main types in this module: * [`SchemaGenerator`], which manages the generation of a schema document. */ -use crate::schema::*; -use crate::{visit::*, JsonSchema, Map}; +use crate::Schema; +use crate::{visit::*, JsonSchema}; use dyn_clone::DynClone; use serde::Serialize; use std::borrow::Cow; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::{any::Any, collections::HashSet, fmt::Debug}; /// Settings to customize how Schemas are generated. @@ -76,7 +76,7 @@ impl SchemaSettings { option_add_null_type: true, definitions_path: "#/definitions/".to_owned(), meta_schema: Some("https://json-schema.org/draft/2019-09/schema".to_owned()), - visitors: Vec::default(), + visitors: Vec::new(), inline_subschemas: false, } } @@ -96,9 +96,7 @@ impl SchemaSettings { Box::new(ReplaceBoolSchemas { skip_additional_properties: true, }), - Box::new(SetSingleExample { - retain_examples: false, - }), + Box::new(SetSingleExample), ], inline_subschemas: false, } @@ -152,7 +150,7 @@ impl SchemaSettings { #[derive(Debug, Default)] pub struct SchemaGenerator { settings: SchemaSettings, - definitions: Map, + definitions: BTreeMap, pending_schema_ids: HashSet>, schema_id_to_name: HashMap, String>, used_schema_names: HashSet, @@ -201,19 +199,6 @@ impl SchemaGenerator { &self.settings } - #[deprecated = "This method no longer has any effect."] - pub fn make_extensible(&self, _schema: &mut SchemaObject) {} - - #[deprecated = "Use `Schema::Bool(true)` instead"] - pub fn schema_for_any(&self) -> Schema { - Schema::Bool(true) - } - - #[deprecated = "Use `Schema::Bool(false)` instead"] - pub fn schema_for_none(&self) -> Schema { - Schema::Bool(false) - } - /// Generates a JSON Schema for the type `T`, and returns either the schema itself or a `$ref` schema referencing `T`'s schema. /// /// If `T` is [referenceable](JsonSchema::is_referenceable), this will add `T`'s schema to this generator's definitions, and @@ -265,7 +250,7 @@ impl SchemaGenerator { name: String, id: Cow<'static, str>, ) { - let dummy = Schema::Bool(false); + let dummy = false.into(); // insert into definitions BEFORE calling json_schema to avoid infinite recursion self.definitions.insert(name.clone(), dummy); @@ -276,26 +261,26 @@ impl SchemaGenerator { /// Borrows the collection of all [referenceable](JsonSchema::is_referenceable) schemas that have been generated. /// - /// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the values are the schemas + /// The keys of the returned `BTreeMap` are the [schema names](JsonSchema::schema_name), and the values are the schemas /// themselves. - pub fn definitions(&self) -> &Map { + pub fn definitions(&self) -> &BTreeMap { &self.definitions } /// Mutably borrows the collection of all [referenceable](JsonSchema::is_referenceable) schemas that have been generated. /// - /// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the values are the schemas + /// The keys of the returned `BTreeMap` are the [schema names](JsonSchema::schema_name), and the values are the schemas /// themselves. - pub fn definitions_mut(&mut self) -> &mut Map { + pub fn definitions_mut(&mut self) -> &mut BTreeMap { &mut self.definitions } /// Returns the collection of all [referenceable](JsonSchema::is_referenceable) schemas that have been generated, /// leaving an empty map in its place. /// - /// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the values are the schemas + /// The keys of the returned `BTreeMap` are the [schema names](JsonSchema::schema_name), and the values are the schemas /// themselves. - pub fn take_definitions(&mut self) -> Map { + pub fn take_definitions(&mut self) -> BTreeMap { std::mem::take(&mut self.definitions) } @@ -309,40 +294,72 @@ 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) -> RootSchema { - let mut schema = self.json_schema_internal::(T::schema_id()).into_object(); - schema.metadata().title.get_or_insert_with(T::schema_name); - let mut root = RootSchema { - meta_schema: self.settings.meta_schema.clone(), - definitions: self.definitions.clone(), - schema, - }; + pub fn root_schema_for(&mut self) -> Schema { + let mut schema = self.json_schema_internal::(T::schema_id()); + + let object = schema.ensure_object(); + + object + .entry("title") + .or_insert_with(|| T::schema_name().into()); + + if let Some(meta_schema) = self.settings.meta_schema.as_deref() { + object.insert("$schema".into(), meta_schema.into()); + } + + if !self.definitions.is_empty() { + object.insert( + "definitions".into(), + serde_json::Value::Object( + self.definitions + .iter() + .map(|(k, v)| (k.clone(), v.clone().into())) + .collect(), + ), + ); + } for visitor in &mut self.settings.visitors { - visitor.visit_root_schema(&mut root) + visitor.visit_schema(&mut schema); } - root + 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) -> RootSchema { - let mut schema = self.json_schema_internal::(T::schema_id()).into_object(); - schema.metadata().title.get_or_insert_with(T::schema_name); - let mut root = RootSchema { - meta_schema: self.settings.meta_schema, - definitions: self.definitions, - schema, - }; + pub fn into_root_schema_for(mut self) -> Schema { + let mut schema = self.json_schema_internal::(T::schema_id()); + + let object = schema.ensure_object(); + + object + .entry("title") + .or_insert_with(|| T::schema_name().into()); + + if let Some(meta_schema) = self.settings.meta_schema { + object.insert("$schema".into(), meta_schema.into()); + } + + if !self.definitions.is_empty() { + object.insert( + "definitions".into(), + serde_json::Value::Object( + self.definitions + .into_iter() + .map(|(k, v)| (k, v.into())) + .collect(), + ), + ); + } for visitor in &mut self.settings.visitors { - visitor.visit_root_schema(&mut root) + visitor.visit_schema(&mut schema); } - root + schema } /// Generates a root JSON Schema for the given example value. @@ -352,29 +369,39 @@ impl SchemaGenerator { pub fn root_schema_for_value( &mut self, value: &T, - ) -> Result { - let mut schema = value - .serialize(crate::ser::Serializer { - gen: self, - include_title: true, - })? - .into_object(); + ) -> Result { + let mut schema = value.serialize(crate::ser::Serializer { + gen: self, + include_title: true, + })?; + + let object = schema.ensure_object(); if let Ok(example) = serde_json::to_value(value) { - schema.metadata().examples.push(example); + object.insert("examples".into(), vec![example].into()); } - let mut root = RootSchema { - meta_schema: self.settings.meta_schema.clone(), - definitions: self.definitions.clone(), - schema, - }; + if let Some(meta_schema) = self.settings.meta_schema.as_deref() { + object.insert("$schema".into(), meta_schema.into()); + } + + if !self.definitions.is_empty() { + object.insert( + "definitions".into(), + serde_json::Value::Object( + self.definitions + .iter() + .map(|(k, v)| (k.clone(), v.clone().into())) + .collect(), + ), + ); + } for visitor in &mut self.settings.visitors { - visitor.visit_root_schema(&mut root) + visitor.visit_schema(&mut schema); } - Ok(root) + Ok(schema) } /// Consumes `self` and generates a root JSON Schema for the given example value. @@ -384,73 +411,39 @@ impl SchemaGenerator { pub fn into_root_schema_for_value( mut self, value: &T, - ) -> Result { - let mut schema = value - .serialize(crate::ser::Serializer { - gen: &mut self, - include_title: true, - })? - .into_object(); + ) -> Result { + let mut schema = value.serialize(crate::ser::Serializer { + gen: &mut self, + include_title: true, + })?; + + let object = schema.ensure_object(); if let Ok(example) = serde_json::to_value(value) { - schema.metadata().examples.push(example); + object.insert("examples".into(), vec![example].into()); } - let mut root = RootSchema { - meta_schema: self.settings.meta_schema, - definitions: self.definitions, - schema, - }; - - for visitor in &mut self.settings.visitors { - visitor.visit_root_schema(&mut root) + if let Some(meta_schema) = self.settings.meta_schema { + object.insert("$schema".into(), meta_schema.into()); } - Ok(root) - } + if !self.definitions.is_empty() { + object.insert( + "definitions".into(), + serde_json::Value::Object( + self.definitions + .into_iter() + .map(|(k, v)| (k, v.into())) + .collect(), + ), + ); + } - /// Attemps to find the schema that the given `schema` is referencing. - /// - /// If the given `schema` has a [`$ref`](../schema/struct.SchemaObject.html#structfield.reference) property which refers - /// to another schema in `self`'s schema definitions, the referenced schema will be returned. Otherwise, returns `None`. - /// - /// # Example - /// ``` - ///# extern crate apistos_schemars as schemars; - /// use schemars::{JsonSchema, gen::SchemaGenerator}; - /// - /// #[derive(JsonSchema)] - /// struct MyStruct { - /// foo: i32, - /// } - /// - /// let mut gen = SchemaGenerator::default(); - /// let ref_schema = gen.subschema_for::(); - /// - /// assert!(ref_schema.is_ref()); - /// - /// let dereferenced = gen.dereference(&ref_schema); - /// - /// assert!(dereferenced.is_some()); - /// assert!(!dereferenced.unwrap().is_ref()); - /// assert_eq!(dereferenced, gen.definitions().get("MyStruct")); - /// ``` - pub fn dereference<'a>(&'a self, schema: &Schema) -> Option<&'a Schema> { - match schema { - Schema::Object(SchemaObject { - reference: Some(ref schema_ref), - .. - }) => { - let definitions_path = &self.settings().definitions_path; - if schema_ref.starts_with(definitions_path) { - let name = &schema_ref[definitions_path.len()..]; - self.definitions.get(name) - } else { - None - } - } - _ => None, + for visitor in &mut self.settings.visitors { + visitor.visit_schema(&mut schema); } + + Ok(schema) } fn json_schema_internal(&mut self, id: Cow<'static, str>) -> Schema { diff --git a/schemars/src/json_schema_impls/array.rs b/schemars/src/json_schema_impls/array.rs index 036d042..bacc079 100644 --- a/schemars/src/json_schema_impls/array.rs +++ b/schemars/src/json_schema_impls/array.rs @@ -1,6 +1,5 @@ use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; +use crate::{json_schema, JsonSchema, Schema}; use std::borrow::Cow; // Does not require T: JsonSchema. @@ -16,15 +15,10 @@ impl JsonSchema for [T; 0] { } fn json_schema(_: &mut SchemaGenerator) -> Schema { - SchemaObject { - instance_type: Some(InstanceType::Array.into()), - array: Some(Box::new(ArrayValidation { - max_items: Some(0), - ..Default::default() - })), - ..Default::default() - } - .into() + json_schema!({ + "type": "array", + "maxItems": 0, + }) } } @@ -44,17 +38,12 @@ macro_rules! array_impls { } fn json_schema(gen: &mut SchemaGenerator) -> Schema { - SchemaObject { - instance_type: Some(InstanceType::Array.into()), - array: Some(Box::new(ArrayValidation { - items: Some(gen.subschema_for::().into()), - max_items: Some($len), - min_items: Some($len), - ..Default::default() - })), - ..Default::default() - } - .into() + json_schema!({ + "type": "array", + "items": serde_json::Value::from(gen.subschema_for::()), + "minItems": $len, + "maxItems": $len, + }) } } )+ @@ -67,40 +56,3 @@ array_impls! { 21 22 23 24 25 26 27 28 29 30 31 32 } - -#[cfg(test)] -mod tests { - use super::*; - use crate::tests::{schema_for, schema_object_for}; - use pretty_assertions::assert_eq; - - #[test] - fn schema_for_array() { - let schema = schema_object_for::<[i32; 8]>(); - assert_eq!( - schema.instance_type, - Some(SingleOrVec::from(InstanceType::Array)) - ); - let array_validation = schema.array.unwrap(); - assert_eq!( - array_validation.items, - Some(SingleOrVec::from(schema_for::())) - ); - assert_eq!(array_validation.max_items, Some(8)); - assert_eq!(array_validation.min_items, Some(8)); - } - - // SomeStruct does not implement JsonSchema - struct SomeStruct; - - #[test] - fn schema_for_empty_array() { - let schema = schema_object_for::<[SomeStruct; 0]>(); - assert_eq!( - schema.instance_type, - Some(SingleOrVec::from(InstanceType::Array)) - ); - let array_validation = schema.array.unwrap(); - assert_eq!(array_validation.max_items, Some(0)); - } -} diff --git a/schemars/src/json_schema_impls/arrayvec05.rs b/schemars/src/json_schema_impls/arrayvec05.rs deleted file mode 100644 index 281bde7..0000000 --- a/schemars/src/json_schema_impls/arrayvec05.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; -use arrayvec05::{Array, ArrayString, ArrayVec}; -use std::convert::TryInto; - -// Do not set maxLength on the schema as that describes length in characters, but we only -// know max length in bytes. -forward_impl!(( JsonSchema for ArrayString where A: Array + Copy) => String); - -impl JsonSchema for ArrayVec -where - A::Item: JsonSchema, -{ - no_ref_schema!(); - - fn schema_name() -> String { - format!( - "Array_up_to_size_{}_of_{}", - A::CAPACITY, - A::Item::schema_name() - ) - } - - fn json_schema(gen: &mut SchemaGenerator) -> Schema { - SchemaObject { - instance_type: Some(InstanceType::Array.into()), - array: Some(Box::new(ArrayValidation { - items: Some(gen.subschema_for::().into()), - max_items: A::CAPACITY.try_into().ok(), - ..Default::default() - })), - ..Default::default() - } - .into() - } -} diff --git a/schemars/src/json_schema_impls/arrayvec07.rs b/schemars/src/json_schema_impls/arrayvec07.rs index e2d92c5..3d4d050 100644 --- a/schemars/src/json_schema_impls/arrayvec07.rs +++ b/schemars/src/json_schema_impls/arrayvec07.rs @@ -1,8 +1,6 @@ use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; +use crate::{json_schema, JsonSchema, Schema}; use arrayvec07::{ArrayString, ArrayVec}; -use std::convert::TryInto; // Do not set maxLength on the schema as that describes length in characters, but we only // know max length in bytes. @@ -19,15 +17,10 @@ where } fn json_schema(gen: &mut SchemaGenerator) -> Schema { - SchemaObject { - instance_type: Some(InstanceType::Array.into()), - array: Some(Box::new(ArrayValidation { - items: Some(gen.subschema_for::().into()), - max_items: CAP.try_into().ok(), - ..Default::default() - })), - ..Default::default() - } - .into() + json_schema!({ + "type": "array", + "items": gen.subschema_for::(), + "maxItems": CAP + }) } } diff --git a/schemars/src/json_schema_impls/atomic.rs b/schemars/src/json_schema_impls/atomic.rs index 5d8f6d9..53d55c6 100644 --- a/schemars/src/json_schema_impls/atomic.rs +++ b/schemars/src/json_schema_impls/atomic.rs @@ -1,6 +1,3 @@ -use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; use std::sync::atomic::*; forward_impl!(AtomicBool => bool); @@ -22,12 +19,12 @@ forward_impl!(AtomicUsize => usize); #[cfg(test)] mod tests { use super::*; - use crate::tests::schema_object_for; + use crate::schema_for; use pretty_assertions::assert_eq; #[test] fn schema_for_atomics() { - let atomic_schema = schema_object_for::<( + let atomic_schema = schema_for!(( AtomicBool, AtomicI8, AtomicI16, @@ -39,9 +36,8 @@ mod tests { AtomicU32, AtomicU64, AtomicUsize, - )>(); - let basic_schema = - schema_object_for::<(bool, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize)>(); + )); + let basic_schema = schema_for!((bool, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize)); assert_eq!(atomic_schema, basic_schema); } } diff --git a/schemars/src/json_schema_impls/bytes.rs b/schemars/src/json_schema_impls/bytes.rs deleted file mode 100644 index f1a0f29..0000000 --- a/schemars/src/json_schema_impls/bytes.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; -use bytes::{Bytes, BytesMut}; - -forward_impl!((JsonSchema for Bytes) => Vec); -forward_impl!((JsonSchema for BytesMut) => Vec); diff --git a/schemars/src/json_schema_impls/chrono.rs b/schemars/src/json_schema_impls/chrono04.rs similarity index 61% rename from schemars/src/json_schema_impls/chrono.rs rename to schemars/src/json_schema_impls/chrono04.rs index 3a1731b..5fd23e8 100644 --- a/schemars/src/json_schema_impls/chrono.rs +++ b/schemars/src/json_schema_impls/chrono04.rs @@ -1,8 +1,6 @@ use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; -use chrono::prelude::*; -use serde_json::json; +use crate::{json_schema, JsonSchema, Schema}; +use chrono04::prelude::*; use std::borrow::Cow; impl JsonSchema for Weekday { @@ -17,20 +15,18 @@ impl JsonSchema for Weekday { } fn json_schema(_: &mut SchemaGenerator) -> Schema { - SchemaObject { - instance_type: Some(InstanceType::String.into()), - enum_values: Some(vec![ - json!("Mon"), - json!("Tue"), - json!("Wed"), - json!("Thu"), - json!("Fri"), - json!("Sat"), - json!("Sun"), - ]), - ..Default::default() - } - .into() + json_schema!({ + "type": "string", + "enum": [ + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + "Sun", + ] + }) } } @@ -51,12 +47,10 @@ macro_rules! formatted_string_impl { } fn json_schema(_: &mut SchemaGenerator) -> Schema { - SchemaObject { - instance_type: Some(InstanceType::String.into()), - format: Some($format.to_owned()), - ..Default::default() - } - .into() + json_schema!({ + "type": "string", + "format": $format + }) } } }; diff --git a/schemars/src/json_schema_impls/core.rs b/schemars/src/json_schema_impls/core.rs index 955ead6..16104f8 100644 --- a/schemars/src/json_schema_impls/core.rs +++ b/schemars/src/json_schema_impls/core.rs @@ -1,7 +1,6 @@ use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; -use serde_json::json; +use crate::{json_schema, JsonSchema, Schema}; +use serde_json::Value; use std::borrow::Cow; use std::ops::{Bound, Range, RangeInclusive}; @@ -18,35 +17,45 @@ impl JsonSchema for Option { fn json_schema(gen: &mut SchemaGenerator) -> Schema { let mut schema = gen.subschema_for::(); + if gen.settings().option_add_null_type { - schema = match schema { - Schema::Bool(true) => Schema::Bool(true), - Schema::Bool(false) => <()>::json_schema(gen), - Schema::Object(SchemaObject { - instance_type: Some(ref mut instance_type), - .. - }) => { - add_null_type(instance_type); - schema - } - schema => SchemaObject { - // TODO technically the schema already accepts null, so this may be unnecessary - subschemas: Some(Box::new(SubschemaValidation { - any_of: Some(vec![schema, <()>::json_schema(gen)]), - ..Default::default() - })), - ..Default::default() + schema = match schema.try_to_object() { + Ok(mut obj) => { + let instance_type = obj.get_mut("type"); + match instance_type { + Some(Value::Array(array)) => { + let null = Value::from("null"); + if !array.contains(&null) { + array.push(null); + } + obj.into() + } + Some(Value::String(string)) => { + if string != "null" { + *instance_type.unwrap() = + Value::Array(vec![std::mem::take(string).into(), "null".into()]) + } + obj.into() + } + _ => json_schema!({ + "anyOf": [ + obj, + <()>::json_schema(gen) + ] + }), + } } - .into(), + Err(true) => true.into(), + Err(false) => <()>::json_schema(gen), } } + if gen.settings().option_nullable { - let mut schema_obj = schema.into_object(); - schema_obj - .extensions - .insert("nullable".to_owned(), json!(true)); - schema = Schema::Object(schema_obj); + schema + .ensure_object() + .insert("nullable".into(), true.into()); }; + schema } @@ -59,16 +68,6 @@ impl JsonSchema for Option { } } -fn add_null_type(instance_type: &mut SingleOrVec) { - match instance_type { - SingleOrVec::Single(ty) if **ty != InstanceType::Null => { - *instance_type = vec![**ty, InstanceType::Null].into() - } - SingleOrVec::Vec(ty) if !ty.contains(&InstanceType::Null) => ty.push(InstanceType::Null), - _ => {} - }; -} - impl JsonSchema for Result { fn schema_name() -> String { format!("Result_of_{}_or_{}", T::schema_name(), E::schema_name()) @@ -79,27 +78,24 @@ impl JsonSchema for Result { } fn json_schema(gen: &mut SchemaGenerator) -> Schema { - let mut ok_schema = SchemaObject { - instance_type: Some(InstanceType::Object.into()), - ..Default::default() - }; - let obj = ok_schema.object(); - obj.required.insert("Ok".to_owned()); - obj.properties - .insert("Ok".to_owned(), gen.subschema_for::()); - - let mut err_schema = SchemaObject { - instance_type: Some(InstanceType::Object.into()), - ..Default::default() - }; - let obj = err_schema.object(); - obj.required.insert("Err".to_owned()); - obj.properties - .insert("Err".to_owned(), gen.subschema_for::()); - - let mut schema = SchemaObject::default(); - schema.subschemas().one_of = Some(vec![ok_schema.into(), err_schema.into()]); - schema.into() + json_schema!({ + "oneOf": [ + { + "type": "object", + "properties": { + "Ok": gen.subschema_for::() + }, + "required": ["Ok"] + }, + { + "type": "object", + "properties": { + "Err": gen.subschema_for::() + }, + "required": ["Err"] + } + ] + }) } } @@ -113,37 +109,28 @@ impl JsonSchema for Bound { } fn json_schema(gen: &mut SchemaGenerator) -> Schema { - let mut included_schema = SchemaObject { - instance_type: Some(InstanceType::Object.into()), - ..Default::default() - }; - let obj = included_schema.object(); - obj.required.insert("Included".to_owned()); - obj.properties - .insert("Included".to_owned(), gen.subschema_for::()); - - let mut excluded_schema = SchemaObject { - instance_type: Some(InstanceType::Object.into()), - ..Default::default() - }; - let obj = excluded_schema.object(); - obj.required.insert("Excluded".to_owned()); - obj.properties - .insert("Excluded".to_owned(), gen.subschema_for::()); - - let unbounded_schema = SchemaObject { - instance_type: Some(InstanceType::String.into()), - const_value: Some(json!("Unbounded")), - ..Default::default() - }; - - let mut schema = SchemaObject::default(); - schema.subschemas().one_of = Some(vec![ - included_schema.into(), - excluded_schema.into(), - unbounded_schema.into(), - ]); - schema.into() + json_schema!({ + "oneOf": [ + { + "type": "object", + "properties": { + "Included": gen.subschema_for::() + }, + "required": ["Included"] + }, + { + "type": "object", + "properties": { + "Excluded": gen.subschema_for::() + }, + "required": ["Excluded"] + }, + { + "type": "string", + "const": "Unbounded" + } + ] + }) } } @@ -157,18 +144,15 @@ impl JsonSchema for Range { } fn json_schema(gen: &mut SchemaGenerator) -> Schema { - let mut schema = SchemaObject { - instance_type: Some(InstanceType::Object.into()), - ..Default::default() - }; - let obj = schema.object(); - obj.required.insert("start".to_owned()); - obj.required.insert("end".to_owned()); - obj.properties - .insert("start".to_owned(), gen.subschema_for::()); - obj.properties - .insert("end".to_owned(), gen.subschema_for::()); - schema.into() + let subschema = gen.subschema_for::(); + json_schema!({ + "type": "object", + "properties": { + "start": subschema, + "end": subschema + }, + "required": ["start", "end"] + }) } } @@ -177,54 +161,3 @@ forward_impl!(( JsonSchema for RangeInclusive) => Range); forward_impl!(( JsonSchema for std::marker::PhantomData) => ()); forward_impl!((<'a> JsonSchema for std::fmt::Arguments<'a>) => String); - -#[cfg(test)] -mod tests { - use super::*; - use crate::tests::{schema_for, schema_object_for}; - use pretty_assertions::assert_eq; - - #[test] - fn schema_for_option() { - let schema = schema_object_for::>(); - assert_eq!( - schema.instance_type, - Some(vec![InstanceType::Integer, InstanceType::Null].into()) - ); - assert_eq!(schema.extensions.get("nullable"), None); - assert_eq!(schema.subschemas.is_none(), true); - } - - #[test] - fn schema_for_option_with_ref() { - use crate as schemars; - #[derive(JsonSchema)] - struct Foo; - - let schema = schema_object_for::>(); - assert_eq!(schema.instance_type, None); - assert_eq!(schema.extensions.get("nullable"), None); - assert_eq!(schema.subschemas.is_some(), true); - let any_of = schema.subschemas.unwrap().any_of.unwrap(); - assert_eq!(any_of.len(), 2); - assert_eq!(any_of[0], Schema::new_ref("#/definitions/Foo".to_string())); - assert_eq!(any_of[1], schema_for::<()>()); - } - - #[test] - fn schema_for_result() { - let schema = schema_object_for::>(); - let one_of = schema.subschemas.unwrap().one_of.unwrap(); - assert_eq!(one_of.len(), 2); - - let ok_schema: SchemaObject = one_of[0].clone().into(); - let obj = ok_schema.object.unwrap(); - assert!(obj.required.contains("Ok")); - assert_eq!(obj.properties["Ok"], schema_for::()); - - let err_schema: SchemaObject = one_of[1].clone().into(); - let obj = err_schema.object.unwrap(); - assert!(obj.required.contains("Err")); - assert_eq!(obj.properties["Err"], schema_for::()); - } -} diff --git a/schemars/src/json_schema_impls/decimal.rs b/schemars/src/json_schema_impls/decimal.rs index 643ca44..462e3d5 100644 --- a/schemars/src/json_schema_impls/decimal.rs +++ b/schemars/src/json_schema_impls/decimal.rs @@ -1,6 +1,5 @@ use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; +use crate::{json_schema, JsonSchema, Schema}; use std::borrow::Cow; macro_rules! decimal_impl { @@ -17,23 +16,16 @@ macro_rules! decimal_impl { } fn json_schema(_: &mut SchemaGenerator) -> Schema { - SchemaObject { - instance_type: Some(InstanceType::String.into()), - string: Some(Box::new(StringValidation { - pattern: Some(r"^-?[0-9]+(\.[0-9]+)?$".to_owned()), - ..Default::default() - })), - ..Default::default() - } - .into() + json_schema!({ + "type": "string", + "pattern": r"^-?[0-9]+(\.[0-9]+)?$", + }) } } }; } -#[cfg(feature = "rust_decimal")] -decimal_impl!(rust_decimal::Decimal); -#[cfg(feature = "bigdecimal03")] -decimal_impl!(bigdecimal03::BigDecimal); +#[cfg(feature = "rust_decimal1")] +decimal_impl!(rust_decimal1::Decimal); #[cfg(feature = "bigdecimal04")] decimal_impl!(bigdecimal04::BigDecimal); diff --git a/schemars/src/json_schema_impls/either.rs b/schemars/src/json_schema_impls/either1.rs similarity index 67% rename from schemars/src/json_schema_impls/either.rs rename to schemars/src/json_schema_impls/either1.rs index 957fdd1..96ed8a8 100644 --- a/schemars/src/json_schema_impls/either.rs +++ b/schemars/src/json_schema_impls/either1.rs @@ -1,7 +1,6 @@ use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; -use either::Either; +use crate::{json_schema, JsonSchema, Schema}; +use either1::Either; use std::borrow::Cow; impl JsonSchema for Either { @@ -20,8 +19,8 @@ impl JsonSchema for Either { } fn json_schema(gen: &mut SchemaGenerator) -> Schema { - let mut schema = SchemaObject::default(); - schema.subschemas().any_of = Some(vec![gen.subschema_for::(), gen.subschema_for::()]); - schema.into() + json_schema!({ + "anyOf": [gen.subschema_for::(), gen.subschema_for::()], + }) } } diff --git a/schemars/src/json_schema_impls/enumset.rs b/schemars/src/json_schema_impls/enumset.rs deleted file mode 100644 index 22a3ebc..0000000 --- a/schemars/src/json_schema_impls/enumset.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; -use enumset::{EnumSet, EnumSetType}; - -forward_impl!(( JsonSchema for EnumSet where T: EnumSetType + JsonSchema) => std::collections::BTreeSet); diff --git a/schemars/src/json_schema_impls/ffi.rs b/schemars/src/json_schema_impls/ffi.rs index 55b5012..4ad85c2 100644 --- a/schemars/src/json_schema_impls/ffi.rs +++ b/schemars/src/json_schema_impls/ffi.rs @@ -1,6 +1,5 @@ use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; +use crate::{json_schema, JsonSchema, Schema}; use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; @@ -14,27 +13,24 @@ impl JsonSchema for OsString { } fn json_schema(gen: &mut SchemaGenerator) -> Schema { - let mut unix_schema = SchemaObject { - instance_type: Some(InstanceType::Object.into()), - ..Default::default() - }; - let obj = unix_schema.object(); - obj.required.insert("Unix".to_owned()); - obj.properties - .insert("Unix".to_owned(), >::json_schema(gen)); - - let mut win_schema = SchemaObject { - instance_type: Some(InstanceType::Object.into()), - ..Default::default() - }; - let obj = win_schema.object(); - obj.required.insert("Windows".to_owned()); - obj.properties - .insert("Windows".to_owned(), >::json_schema(gen)); - - let mut schema = SchemaObject::default(); - schema.subschemas().one_of = Some(vec![unix_schema.into(), win_schema.into()]); - schema.into() + json_schema!({ + "oneOf": [ + { + "type": "object", + "properties": { + "Unix": >::json_schema(gen) + }, + "required": ["Unix"] + }, + { + "type": "object", + "properties": { + "Windows": >::json_schema(gen) + }, + "required": ["Windows"] + }, + ] + }) } } diff --git a/schemars/src/json_schema_impls/indexmap.rs b/schemars/src/json_schema_impls/indexmap.rs deleted file mode 100644 index 3bd3e54..0000000 --- a/schemars/src/json_schema_impls/indexmap.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; -use indexmap::{IndexMap, IndexSet}; -use std::collections::{HashMap, HashSet}; - -forward_impl!(( JsonSchema for IndexMap) => HashMap); -forward_impl!(( JsonSchema for IndexSet) => HashSet); diff --git a/schemars/src/json_schema_impls/indexmap2.rs b/schemars/src/json_schema_impls/indexmap2.rs index 44eeedb..9473dea 100644 --- a/schemars/src/json_schema_impls/indexmap2.rs +++ b/schemars/src/json_schema_impls/indexmap2.rs @@ -1,5 +1,3 @@ -use crate::gen::SchemaGenerator; -use crate::schema::*; use crate::JsonSchema; use indexmap2::{IndexMap, IndexSet}; use std::collections::{HashMap, HashSet}; diff --git a/schemars/src/json_schema_impls/maps.rs b/schemars/src/json_schema_impls/maps.rs index 7c27808..b934f8b 100644 --- a/schemars/src/json_schema_impls/maps.rs +++ b/schemars/src/json_schema_impls/maps.rs @@ -1,6 +1,5 @@ use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; +use crate::{json_schema, JsonSchema, Schema}; use std::borrow::Cow; macro_rules! map_impl { @@ -20,16 +19,10 @@ macro_rules! map_impl { } fn json_schema(gen: &mut SchemaGenerator) -> Schema { - let subschema = gen.subschema_for::(); - SchemaObject { - instance_type: Some(InstanceType::Object.into()), - object: Some(Box::new(ObjectValidation { - additional_properties: Some(Box::new(subschema)), - ..Default::default() - })), - ..Default::default() - } - .into() + json_schema!({ + "type": "object", + "additionalProperties": gen.subschema_for::(), + }) } } }; diff --git a/schemars/src/json_schema_impls/mod.rs b/schemars/src/json_schema_impls/mod.rs index 5fcd971..ebcb61b 100644 --- a/schemars/src/json_schema_impls/mod.rs +++ b/schemars/src/json_schema_impls/mod.rs @@ -21,11 +21,11 @@ macro_rules! forward_impl { <$target>::schema_id() } - fn json_schema(gen: &mut SchemaGenerator) -> Schema { + fn json_schema(gen: &mut $crate::gen::SchemaGenerator) -> $crate::Schema { <$target>::json_schema(gen) } - fn _schemars_private_non_optional_json_schema(gen: &mut SchemaGenerator) -> Schema { + fn _schemars_private_non_optional_json_schema(gen: &mut $crate::gen::SchemaGenerator) -> $crate::Schema { <$target>::_schemars_private_non_optional_json_schema(gen) } @@ -35,57 +35,61 @@ macro_rules! forward_impl { } }; ($ty:ty => $target:ty) => { - forward_impl!((JsonSchema for $ty) => $target); + forward_impl!(($crate::JsonSchema for $ty) => $target); }; } mod array; -#[cfg(feature = "arrayvec05")] -mod arrayvec05; -#[cfg(feature = "arrayvec07")] -mod arrayvec07; -#[cfg(std_atomic)] -mod atomic; -#[cfg(feature = "bytes")] -mod bytes; -#[cfg(feature = "chrono")] -mod chrono; mod core; -#[cfg(any( - feature = "rust_decimal", - feature = "bigdecimal03", - feature = "bigdecimal04" -))] -mod decimal; -#[cfg(feature = "either")] -mod either; -#[cfg(feature = "enumset")] -mod enumset; mod ffi; -#[cfg(feature = "indexmap")] -mod indexmap; -#[cfg(feature = "indexmap2")] -mod indexmap2; -#[cfg(feature = "ipnetwork")] -mod ipnetwork; mod maps; mod nonzero_signed; mod nonzero_unsigned; mod primitives; -#[cfg(feature = "semver")] -mod semver; mod sequences; mod serdejson; -#[cfg(feature = "smallvec")] -mod smallvec; -#[cfg(feature = "smol_str")] -mod smol_str; mod time; mod tuple; -#[cfg(feature = "url")] -mod url; -#[cfg(feature = "uuid08")] -mod uuid08; +mod wrapper; + +#[cfg(std_atomic)] +mod atomic; + +#[cfg(feature = "arrayvec07")] +mod arrayvec07; + +#[cfg(feature = "bytes1")] +mod bytes1 { + forward_impl!(bytes1::Bytes => Vec); + forward_impl!(bytes1::BytesMut => Vec); +} + +#[cfg(feature = "chrono04")] +mod chrono04; + +#[cfg(any(feature = "rust_decimal1", feature = "bigdecimal04"))] +mod decimal; + +#[cfg(feature = "either1")] +mod either1; + +#[cfg(feature = "enumset1")] +forward_impl!(( crate::JsonSchema for enumset1::EnumSet) => std::collections::BTreeSet); + +#[cfg(feature = "indexmap2")] +mod indexmap2; + +#[cfg(feature = "semver1")] +mod semver1; + +#[cfg(feature = "smallvec1")] +forward_impl!(( crate::JsonSchema for smallvec1::SmallVec where A::Item: crate::JsonSchema) => Vec); + +#[cfg(feature = "smol_str02")] +forward_impl!(smol_str02::SmolStr => String); + +#[cfg(feature = "url2")] +mod url2; + #[cfg(feature = "uuid1")] mod uuid1; -mod wrapper; diff --git a/schemars/src/json_schema_impls/nonzero_signed.rs b/schemars/src/json_schema_impls/nonzero_signed.rs index c2fba2b..d1b5142 100644 --- a/schemars/src/json_schema_impls/nonzero_signed.rs +++ b/schemars/src/json_schema_impls/nonzero_signed.rs @@ -1,6 +1,5 @@ use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; +use crate::{JsonSchema, Schema}; use std::borrow::Cow; use std::num::*; @@ -18,14 +17,12 @@ macro_rules! nonzero_unsigned_impl { } fn json_schema(gen: &mut SchemaGenerator) -> Schema { - let zero_schema: Schema = SchemaObject { - const_value: Some(0.into()), - ..Default::default() - } - .into(); - let mut schema: SchemaObject = <$primitive>::json_schema(gen).into(); - schema.subschemas().not = Some(Box::from(zero_schema)); - schema.into() + let mut schema = <$primitive>::json_schema(gen); + let object = schema.ensure_object(); + object.insert("not".to_owned(), serde_json::json!({ + "const": 0 + })); + schema } } }; diff --git a/schemars/src/json_schema_impls/nonzero_unsigned.rs b/schemars/src/json_schema_impls/nonzero_unsigned.rs index 1963d56..e3c6d1d 100644 --- a/schemars/src/json_schema_impls/nonzero_unsigned.rs +++ b/schemars/src/json_schema_impls/nonzero_unsigned.rs @@ -1,5 +1,5 @@ use crate::gen::SchemaGenerator; -use crate::schema::*; +use crate::Schema; use crate::JsonSchema; use std::borrow::Cow; use std::num::*; @@ -18,9 +18,10 @@ macro_rules! nonzero_unsigned_impl { } fn json_schema(gen: &mut SchemaGenerator) -> Schema { - let mut schema: SchemaObject = <$primitive>::json_schema(gen).into(); - schema.number().minimum = Some(1.0); - schema.into() + let mut schema = <$primitive>::json_schema(gen); + let object = schema.ensure_object(); + object.insert("minimum".to_owned(), 1.into()); + schema } } }; @@ -32,18 +33,3 @@ nonzero_unsigned_impl!(NonZeroU32 => u32); nonzero_unsigned_impl!(NonZeroU64 => u64); nonzero_unsigned_impl!(NonZeroU128 => u128); nonzero_unsigned_impl!(NonZeroUsize => usize); - -#[cfg(test)] -mod tests { - use super::*; - use crate::tests::schema_object_for; - use pretty_assertions::assert_eq; - - #[test] - fn schema_for_nonzero_u32() { - let schema = schema_object_for::(); - assert_eq!(schema.number.unwrap().minimum, Some(1.0)); - assert_eq!(schema.instance_type, Some(InstanceType::Integer.into())); - assert_eq!(schema.format, Some("uint32".to_owned())); - } -} diff --git a/schemars/src/json_schema_impls/primitives.rs b/schemars/src/json_schema_impls/primitives.rs index d182984..63121fe 100644 --- a/schemars/src/json_schema_impls/primitives.rs +++ b/schemars/src/json_schema_impls/primitives.rs @@ -1,67 +1,77 @@ use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; +use crate::{json_schema, JsonSchema, Schema}; use std::borrow::Cow; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use std::path::{Path, PathBuf}; macro_rules! simple_impl { - ($type:ty => $instance_type:ident) => { - simple_impl!($type => $instance_type, stringify!($instance_type), None); - }; - ($type:ty => $instance_type:ident, $format:literal) => { - simple_impl!($type => $instance_type, $format, Some($format.to_owned())); + ($type:ty => $instance_type:literal) => { + impl JsonSchema for $type { + no_ref_schema!(); + + fn schema_name() -> String { + $instance_type.to_owned() + } + + fn schema_id() -> Cow<'static, str> { + Cow::Borrowed($instance_type) + } + + fn json_schema(_: &mut SchemaGenerator) -> Schema { + json_schema!({ + "type": $instance_type + }) + } + } }; - ($type:ty => $instance_type:ident, $name:expr, $format:expr) => { + ($type:ty => $instance_type:literal, $format:literal) => { impl JsonSchema for $type { no_ref_schema!(); fn schema_name() -> String { - $name.to_owned() + $format.to_owned() } fn schema_id() -> Cow<'static, str> { - Cow::Borrowed($name) + Cow::Borrowed($format) } fn json_schema(_: &mut SchemaGenerator) -> Schema { - SchemaObject { - instance_type: Some(InstanceType::$instance_type.into()), - format: $format, - ..Default::default() - } - .into() + json_schema!({ + "type": $instance_type, + "format": $format + }) } } }; } -simple_impl!(str => String); -simple_impl!(String => String); -simple_impl!(bool => Boolean); -simple_impl!(f32 => Number, "float"); -simple_impl!(f64 => Number, "double"); -simple_impl!(i8 => Integer, "int8"); -simple_impl!(i16 => Integer, "int16"); -simple_impl!(i32 => Integer, "int32"); -simple_impl!(i64 => Integer, "int64"); -simple_impl!(i128 => Integer, "int128"); -simple_impl!(isize => Integer, "int"); -simple_impl!(() => Null); - -simple_impl!(Path => String); -simple_impl!(PathBuf => String); - -simple_impl!(Ipv4Addr => String, "ipv4"); -simple_impl!(Ipv6Addr => String, "ipv6"); -simple_impl!(IpAddr => String, "ip"); - -simple_impl!(SocketAddr => String); -simple_impl!(SocketAddrV4 => String); -simple_impl!(SocketAddrV6 => String); +simple_impl!(str => "string"); +simple_impl!(String => "string"); +simple_impl!(bool => "boolean"); +simple_impl!(f32 => "number", "float"); +simple_impl!(f64 => "number", "double"); +simple_impl!(i8 => "integer", "int8"); +simple_impl!(i16 => "integer", "int16"); +simple_impl!(i32 => "integer", "int32"); +simple_impl!(i64 => "integer", "int64"); +simple_impl!(i128 => "integer", "int128"); +simple_impl!(isize => "integer", "int"); +simple_impl!(() => "null"); + +simple_impl!(Path => "string"); +simple_impl!(PathBuf => "string"); + +simple_impl!(Ipv4Addr => "string", "ipv4"); +simple_impl!(Ipv6Addr => "string", "ipv6"); +simple_impl!(IpAddr => "string", "ip"); + +simple_impl!(SocketAddr => "string"); +simple_impl!(SocketAddrV4 => "string"); +simple_impl!(SocketAddrV6 => "string"); macro_rules! unsigned_impl { - ($type:ty => $instance_type:ident, $format:expr) => { + ($type:ty => $instance_type:literal, $format:literal) => { impl JsonSchema for $type { no_ref_schema!(); @@ -74,24 +84,22 @@ macro_rules! unsigned_impl { } fn json_schema(_: &mut SchemaGenerator) -> Schema { - let mut schema = SchemaObject { - instance_type: Some(InstanceType::$instance_type.into()), - format: Some($format.to_owned()), - ..Default::default() - }; - schema.number().minimum = Some(0.0); - schema.into() + json_schema!({ + "type": $instance_type, + "format": $format, + "minimum": 0 + }) } } }; } -unsigned_impl!(u8 => Integer, "uint8"); -unsigned_impl!(u16 => Integer, "uint16"); -unsigned_impl!(u32 => Integer, "uint32"); -unsigned_impl!(u64 => Integer, "uint64"); -unsigned_impl!(u128 => Integer, "uint128"); -unsigned_impl!(usize => Integer, "uint"); +unsigned_impl!(u8 => "integer", "uint8"); +unsigned_impl!(u16 => "integer", "uint16"); +unsigned_impl!(u32 => "integer", "uint32"); +unsigned_impl!(u64 => "integer", "uint64"); +unsigned_impl!(u128 => "integer", "uint128"); +unsigned_impl!(usize => "integer", "uint"); impl JsonSchema for char { no_ref_schema!(); @@ -105,15 +113,10 @@ impl JsonSchema for char { } fn json_schema(_: &mut SchemaGenerator) -> Schema { - SchemaObject { - instance_type: Some(InstanceType::String.into()), - string: Some(Box::new(StringValidation { - min_length: Some(1), - max_length: Some(1), - ..Default::default() - })), - ..Default::default() - } - .into() + json_schema!({ + "type": "string", + "minLength": 1, + "maxLength": 1, + }) } } diff --git a/schemars/src/json_schema_impls/semver.rs b/schemars/src/json_schema_impls/semver.rs deleted file mode 100644 index dd8ed8e..0000000 --- a/schemars/src/json_schema_impls/semver.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; -use semver::Version; -use std::borrow::Cow; - -impl JsonSchema for Version { - no_ref_schema!(); - - fn schema_name() -> String { - "Version".to_owned() - } - - fn schema_id() -> Cow<'static, str> { - Cow::Borrowed("semver::Version") - } - - fn json_schema(_: &mut SchemaGenerator) -> Schema { - SchemaObject { - instance_type: Some(InstanceType::String.into()), - string: Some(Box::new(StringValidation { - // https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string - pattern: Some(r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$".to_owned()), - ..Default::default() - })), - ..Default::default() - } - .into() - } -} diff --git a/schemars/src/json_schema_impls/semver1.rs b/schemars/src/json_schema_impls/semver1.rs new file mode 100644 index 0000000..fb47f78 --- /dev/null +++ b/schemars/src/json_schema_impls/semver1.rs @@ -0,0 +1,24 @@ +use crate::gen::SchemaGenerator; +use crate::{json_schema, JsonSchema, Schema}; +use semver1::Version; +use std::borrow::Cow; + +impl JsonSchema for Version { + no_ref_schema!(); + + fn schema_name() -> String { + "Version".to_owned() + } + + fn schema_id() -> Cow<'static, str> { + Cow::Borrowed("semver::Version") + } + + fn json_schema(_: &mut SchemaGenerator) -> Schema { + json_schema!({ + "type": "string", + // https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string + "pattern": r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$" + }) + } +} diff --git a/schemars/src/json_schema_impls/sequences.rs b/schemars/src/json_schema_impls/sequences.rs index 780307f..d14d4d3 100644 --- a/schemars/src/json_schema_impls/sequences.rs +++ b/schemars/src/json_schema_impls/sequences.rs @@ -1,6 +1,5 @@ use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; +use crate::{json_schema, JsonSchema, Schema}; use std::borrow::Cow; macro_rules! seq_impl { @@ -21,15 +20,10 @@ macro_rules! seq_impl { } fn json_schema(gen: &mut SchemaGenerator) -> Schema { - SchemaObject { - instance_type: Some(InstanceType::Array.into()), - array: Some(Box::new(ArrayValidation { - items: Some(gen.subschema_for::().into()), - ..Default::default() - })), - ..Default::default() - } - .into() + json_schema!({ + "type": "array", + "items": gen.subschema_for::(), + }) } } }; @@ -53,16 +47,11 @@ macro_rules! set_impl { } fn json_schema(gen: &mut SchemaGenerator) -> Schema { - SchemaObject { - instance_type: Some(InstanceType::Array.into()), - array: Some(Box::new(ArrayValidation { - unique_items: Some(true), - items: Some(gen.subschema_for::().into()), - ..Default::default() - })), - ..Default::default() - } - .into() + json_schema!({ + "type": "array", + "uniqueItems": true, + "items": gen.subschema_for::(), + }) } } }; diff --git a/schemars/src/json_schema_impls/serdejson.rs b/schemars/src/json_schema_impls/serdejson.rs index 41eafd5..5c6b58d 100644 --- a/schemars/src/json_schema_impls/serdejson.rs +++ b/schemars/src/json_schema_impls/serdejson.rs @@ -1,6 +1,5 @@ use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; +use crate::{json_schema, JsonSchema, Schema}; use serde_json::{Map, Number, Value}; use std::borrow::Cow; use std::collections::BTreeMap; @@ -17,7 +16,7 @@ impl JsonSchema for Value { } fn json_schema(_: &mut SchemaGenerator) -> Schema { - Schema::Bool(true) + true.into() } } @@ -35,11 +34,9 @@ impl JsonSchema for Number { } fn json_schema(_: &mut SchemaGenerator) -> Schema { - SchemaObject { - instance_type: Some(InstanceType::Number.into()), - ..Default::default() - } - .into() + json_schema!({ + "type": "number" + }) } } diff --git a/schemars/src/json_schema_impls/smallvec.rs b/schemars/src/json_schema_impls/smallvec.rs deleted file mode 100644 index f7a75e3..0000000 --- a/schemars/src/json_schema_impls/smallvec.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; -use smallvec::{Array, SmallVec}; - -forward_impl!(( JsonSchema for SmallVec where A::Item: JsonSchema) => Vec); diff --git a/schemars/src/json_schema_impls/smol_str.rs b/schemars/src/json_schema_impls/smol_str.rs deleted file mode 100644 index cbca4a1..0000000 --- a/schemars/src/json_schema_impls/smol_str.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; -use smol_str::SmolStr; - -forward_impl!(SmolStr => String); diff --git a/schemars/src/json_schema_impls/time.rs b/schemars/src/json_schema_impls/time.rs index 767a1d2..7317cfd 100644 --- a/schemars/src/json_schema_impls/time.rs +++ b/schemars/src/json_schema_impls/time.rs @@ -1,6 +1,5 @@ use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; +use crate::{json_schema, JsonSchema, Schema}; use std::borrow::Cow; use std::time::{Duration, SystemTime}; @@ -14,18 +13,14 @@ impl JsonSchema for Duration { } fn json_schema(gen: &mut SchemaGenerator) -> Schema { - let mut schema = SchemaObject { - instance_type: Some(InstanceType::Object.into()), - ..Default::default() - }; - let obj = schema.object(); - obj.required.insert("secs".to_owned()); - obj.required.insert("nanos".to_owned()); - obj.properties - .insert("secs".to_owned(), ::json_schema(gen)); - obj.properties - .insert("nanos".to_owned(), ::json_schema(gen)); - schema.into() + json_schema!({ + "type": "object", + "required": ["secs", "nanos"], + "properties": { + "secs": u64::json_schema(gen), + "nanos": u32::json_schema(gen), + } + }) } } @@ -39,17 +34,13 @@ impl JsonSchema for SystemTime { } fn json_schema(gen: &mut SchemaGenerator) -> Schema { - let mut schema = SchemaObject { - instance_type: Some(InstanceType::Object.into()), - ..Default::default() - }; - let obj = schema.object(); - obj.required.insert("secs_since_epoch".to_owned()); - obj.required.insert("nanos_since_epoch".to_owned()); - obj.properties - .insert("secs_since_epoch".to_owned(), ::json_schema(gen)); - obj.properties - .insert("nanos_since_epoch".to_owned(), ::json_schema(gen)); - schema.into() + json_schema!({ + "type": "object", + "required": ["secs_since_epoch", "nanos_since_epoch"], + "properties": { + "secs_since_epoch": u64::json_schema(gen), + "nanos_since_epoch": u32::json_schema(gen), + } + }) } } diff --git a/schemars/src/json_schema_impls/tuple.rs b/schemars/src/json_schema_impls/tuple.rs index f67f06a..ab9a36d 100644 --- a/schemars/src/json_schema_impls/tuple.rs +++ b/schemars/src/json_schema_impls/tuple.rs @@ -1,6 +1,5 @@ use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; +use crate::{json_schema, JsonSchema, Schema}; use std::borrow::Cow; macro_rules! tuple_impls { @@ -24,20 +23,14 @@ macro_rules! tuple_impls { } fn json_schema(gen: &mut SchemaGenerator) -> Schema { - let items = vec![ - $(gen.subschema_for::<$name>()),+ - ]; - SchemaObject { - instance_type: Some(InstanceType::Array.into()), - array: Some(Box::new(ArrayValidation { - items: Some(items.into()), - max_items: Some($len), - min_items: Some($len), - ..Default::default() - })), - ..Default::default() - } - .into() + json_schema!({ + "type": "array", + "items": [ + $(gen.subschema_for::<$name>()),+ + ], + "minItems": $len, + "maxItems": $len, + }) } } )+ @@ -62,29 +55,3 @@ tuple_impls! { 15 => (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14) 16 => (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::tests::{schema_for, schema_object_for}; - use pretty_assertions::assert_eq; - - #[test] - fn schema_for_map_any_value() { - let schema = schema_object_for::<(i32, bool)>(); - assert_eq!( - schema.instance_type, - Some(SingleOrVec::from(InstanceType::Array)) - ); - let array_validation = schema.array.unwrap(); - assert_eq!( - array_validation.items, - Some(SingleOrVec::Vec(vec![ - schema_for::(), - schema_for::() - ])) - ); - assert_eq!(array_validation.max_items, Some(2)); - assert_eq!(array_validation.min_items, Some(2)); - } -} diff --git a/schemars/src/json_schema_impls/url.rs b/schemars/src/json_schema_impls/url2.rs similarity index 56% rename from schemars/src/json_schema_impls/url.rs rename to schemars/src/json_schema_impls/url2.rs index be18612..2fcfd04 100644 --- a/schemars/src/json_schema_impls/url.rs +++ b/schemars/src/json_schema_impls/url2.rs @@ -1,8 +1,7 @@ use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; +use crate::{json_schema, JsonSchema, Schema}; use std::borrow::Cow; -use url::Url; +use url2::Url; impl JsonSchema for Url { no_ref_schema!(); @@ -16,11 +15,9 @@ impl JsonSchema for Url { } fn json_schema(_: &mut SchemaGenerator) -> Schema { - SchemaObject { - instance_type: Some(InstanceType::String.into()), - format: Some("uri".to_owned()), - ..Default::default() - } - .into() + json_schema!({ + "type": "string", + "format": "uri", + }) } } diff --git a/schemars/src/json_schema_impls/uuid08.rs b/schemars/src/json_schema_impls/uuid08.rs deleted file mode 100644 index b3b18f8..0000000 --- a/schemars/src/json_schema_impls/uuid08.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; -use std::borrow::Cow; -use uuid08::Uuid; - -impl JsonSchema for Uuid { - no_ref_schema!(); - - fn schema_name() -> String { - "Uuid".to_string() - } - - fn schema_id() -> Cow<'static, str> { - Cow::Borrowed("uuid::Uuid") - } - - fn json_schema(_: &mut SchemaGenerator) -> Schema { - SchemaObject { - instance_type: Some(InstanceType::String.into()), - format: Some("uuid".to_string()), - ..Default::default() - } - .into() - } -} diff --git a/schemars/src/json_schema_impls/uuid1.rs b/schemars/src/json_schema_impls/uuid1.rs index 2e0c6e9..825f7a2 100644 --- a/schemars/src/json_schema_impls/uuid1.rs +++ b/schemars/src/json_schema_impls/uuid1.rs @@ -1,6 +1,5 @@ use crate::gen::SchemaGenerator; -use crate::schema::*; -use crate::JsonSchema; +use crate::{json_schema, JsonSchema, Schema}; use std::borrow::Cow; use uuid1::Uuid; @@ -16,11 +15,9 @@ impl JsonSchema for Uuid { } fn json_schema(_: &mut SchemaGenerator) -> Schema { - SchemaObject { - instance_type: Some(InstanceType::String.into()), - format: Some("uuid".to_string()), - ..Default::default() - } - .into() + json_schema!({ + "type": "string", + "format": "uuid", + }) } } diff --git a/schemars/src/json_schema_impls/wrapper.rs b/schemars/src/json_schema_impls/wrapper.rs index 243a06b..e7e233c 100644 --- a/schemars/src/json_schema_impls/wrapper.rs +++ b/schemars/src/json_schema_impls/wrapper.rs @@ -1,5 +1,3 @@ -use crate::gen::SchemaGenerator; -use crate::schema::Schema; use crate::JsonSchema; macro_rules! wrapper_impl { diff --git a/schemars/src/lib.rs b/schemars/src/lib.rs index 1c35909..55d36c8 100644 --- a/schemars/src/lib.rs +++ b/schemars/src/lib.rs @@ -1,32 +1,9 @@ -#![forbid(unsafe_code)] +#![deny(unsafe_code)] #![doc = include_str!("../README.md")] -/// The map type used by schemars types. -/// -/// Currently a `BTreeMap` or `IndexMap` can be used, but this may change to a different implementation -/// with a similar interface in a future version of schemars. -/// The `IndexMap` will be used when the `preserve_order` feature flag is set. -#[cfg(not(feature = "preserve_order"))] -pub type Map = std::collections::BTreeMap; -#[cfg(feature = "preserve_order")] -pub type Map = indexmap::IndexMap; -/// The set type used by schemars types. -/// -/// Currently a `BTreeSet`, but this may change to a different implementation -/// with a similar interface in a future version of schemars. -pub type Set = std::collections::BTreeSet; - -/// A view into a single entry in a map, which may either be vacant or occupied. -// -/// This is constructed from the `entry` method on `BTreeMap` or `IndexMap`, -/// depending on whether the `preserve_order` feature flag is set. -#[cfg(not(feature = "preserve_order"))] -pub type MapEntry<'a, K, V> = std::collections::btree_map::Entry<'a, K, V>; -#[cfg(feature = "preserve_order")] -pub type MapEntry<'a, K, V> = indexmap::map::Entry<'a, K, V>; - mod flatten; mod json_schema_impls; +mod schema; mod ser; #[macro_use] mod macros; @@ -36,7 +13,6 @@ mod macros; #[doc(hidden)] pub mod _private; pub mod gen; -pub mod schema; pub mod visit; #[cfg(feature = "schemars_derive")] @@ -50,7 +26,7 @@ pub use schemars_derive::*; #[doc(hidden)] pub use serde_json as _serde_json; -use schema::Schema; +pub use schema::Schema; /// A type which can be described as a JSON Schema document. /// @@ -77,7 +53,7 @@ use schema::Schema; /// For non-generic types, the type name/path are suitable for this: /// ``` ///# extern crate apistos_schemars as schemars; -/// use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema}; +/// use schemars::{gen::SchemaGenerator, Schema, JsonSchema}; /// use std::borrow::Cow; /// /// struct NonGenericType; @@ -104,7 +80,7 @@ use schema::Schema; /// But generic type parameters which may affect the generated schema should typically be included in the name/ID: /// ``` ///# extern crate apistos_schemars as schemars; -/// use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema}; +/// use schemars::{gen::SchemaGenerator, Schema, JsonSchema}; /// use std::{borrow::Cow, marker::PhantomData}; /// /// struct GenericType(PhantomData); @@ -178,24 +154,3 @@ pub trait JsonSchema { false } } - -#[cfg(test)] -pub mod tests { - use super::*; - - pub fn schema_object_for() -> schema::SchemaObject { - schema_object(schema_for::()) - } - - pub fn schema_for() -> schema::Schema { - let mut gen = gen::SchemaGenerator::default(); - T::json_schema(&mut gen) - } - - pub fn schema_object(schema: schema::Schema) -> schema::SchemaObject { - match schema { - schema::Schema::Object(o) => o, - s => panic!("Schema was not an object: {:?}", s), - } - } -} diff --git a/schemars/src/macros.rs b/schemars/src/macros.rs index 131fe3e..bae7ce0 100644 --- a/schemars/src/macros.rs +++ b/schemars/src/macros.rs @@ -79,3 +79,19 @@ macro_rules! schema_for_value { .unwrap() }; } + +// TODO doc +#[macro_export] +macro_rules! json_schema { + ( + {$($json_object:tt)*} + ) => { + $crate::Schema::try_from($crate::_serde_json::json!({$($json_object)*})).unwrap() + }; + (true) => { + $crate::Schema::from(true) + }; + (false) => { + $crate::Schema::from(false) + }; +} diff --git a/schemars/src/schema.rs b/schemars/src/schema.rs index 664f68b..6a88158 100644 --- a/schemars/src/schema.rs +++ b/schemars/src/schema.rs @@ -2,552 +2,229 @@ JSON Schema types. */ -#[cfg(feature = "impl_json_schema")] -use crate as schemars; -#[cfg(feature = "impl_json_schema")] -use crate::JsonSchema; -use crate::{Map, Set}; +use ref_cast::ref_cast_custom; +use ref_cast::RefCastCustom; use serde::{Deserialize, Serialize}; -use serde_json::Value; -use std::ops::Deref; +use serde_json::{Map, Value}; /// A JSON Schema. -#[allow(clippy::large_enum_variant)] -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))] -#[serde(untagged)] -pub enum Schema { - /// A trivial boolean JSON Schema. - /// - /// The schema `true` matches everything (always passes validation), whereas the schema `false` - /// matches nothing (always fails validation). - Bool(bool), - /// A JSON Schema object. - Object(SchemaObject), +#[derive(Debug, Clone, PartialEq, RefCastCustom)] +#[repr(transparent)] +pub struct Schema(Value); + +impl<'de> Deserialize<'de> for Schema { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let value = Value::deserialize(deserializer)?; + Schema::validate(&value)?; + Ok(Schema(value)) + } } -impl Schema { - /// Creates a new `$ref` schema. - /// - /// The given reference string should be a URI reference. This will usually be a JSON Pointer - /// in [URI Fragment representation](https://tools.ietf.org/html/rfc6901#section-6). - pub fn new_ref(reference: String) -> Self { - SchemaObject::new_ref(reference).into() +impl Serialize for Schema { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + ser::OrderedKeywordWrapper(&self.0).serialize(serializer) } +} - /// Returns `true` if `self` is a `$ref` schema. - /// - /// If `self` is a [`SchemaObject`] with `Some` [`reference`](struct.SchemaObject.html#structfield.reference) set, this returns `true`. - /// Otherwise, returns `false`. - pub fn is_ref(&self) -> bool { - match self { - Schema::Object(o) => o.is_ref(), - _ => false, - } +impl Schema { + pub fn new() -> Self { + Self(Value::Object(Map::new())) } - /// Converts the given schema (if it is a boolean schema) into an equivalent schema object. - /// - /// If the given schema is already a schema object, this has no effect. - /// - /// # Example - /// ``` - ///# extern crate apistos_schemars as schemars; - /// use schemars::schema::{Schema, SchemaObject}; - /// - /// let bool_schema = Schema::Bool(true); - /// - /// assert_eq!(bool_schema.into_object(), SchemaObject::default()); - /// ``` - pub fn into_object(self) -> SchemaObject { - match self { - Schema::Object(o) => o, - Schema::Bool(true) => SchemaObject::default(), - Schema::Bool(false) => SchemaObject { - subschemas: Some(Box::new(SubschemaValidation { - not: Some(Schema::Object(Default::default()).into()), - ..Default::default() - })), - ..Default::default() - }, - } + pub fn new_ref(reference: String) -> Self { + let mut map = Map::new(); + map.insert("$ref".to_owned(), Value::String(reference)); + Self(Value::Object(map)) } -} -impl From for Schema { - fn from(o: SchemaObject) -> Self { - Schema::Object(o) + pub fn as_value(&self) -> &Value { + &self.0 } -} -impl From for Schema { - fn from(b: bool) -> Self { - Schema::Bool(b) + pub fn as_bool(&self) -> Option { + self.0.as_bool() } -} -/// The root object of a JSON Schema document. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] -#[cfg_attr(feature = "impl_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 `definitions` keyword. - /// - /// In JSON Schema draft 2019-09 this was replaced by $defs, but in Schemars this is still - /// serialized as `definitions` for backward-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), - /// and [JSON Schema (draft 07) 9. Schema Re-Use With "definitions"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-9). - #[serde(alias = "$defs", skip_serializing_if = "Map::is_empty")] - pub definitions: Map, -} + pub fn as_object(&self) -> Option<&Map> { + self.0.as_object() + } -/// A JSON Schema object. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] -#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))] -#[serde(rename_all = "camelCase", default)] -pub struct SchemaObject { - /// Properties which annotate the [`SchemaObject`] which typically have no effect when an object is being validated against the schema. - #[serde(flatten, deserialize_with = "skip_if_default")] - pub metadata: Option>, - /// The `type` keyword. - /// - /// See [JSON Schema Validation 6.1.1. "type"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.1) - /// and [JSON Schema 4.2.1. Instance Data Model](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-4.2.1). - #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - pub instance_type: Option>, - /// The `format` keyword. - /// - /// See [JSON Schema Validation 7. A Vocabulary for Semantic Content With "format"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-7). - #[serde(skip_serializing_if = "Option::is_none")] - pub format: Option, - /// The `enum` keyword. - /// - /// See [JSON Schema Validation 6.1.2. "enum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.2) - #[serde(rename = "enum", skip_serializing_if = "Option::is_none")] - pub enum_values: Option>, - /// The `const` keyword. - /// - /// See [JSON Schema Validation 6.1.3. "const"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.3) - #[serde( - rename = "const", - skip_serializing_if = "Option::is_none", - deserialize_with = "allow_null" - )] - pub const_value: Option, - /// Properties of the [`SchemaObject`] which define validation assertions in terms of other schemas. - #[serde(flatten, deserialize_with = "skip_if_default")] - pub subschemas: Option>, - /// Properties of the [`SchemaObject`] which define validation assertions for numbers. - #[serde(flatten, deserialize_with = "skip_if_default")] - pub number: Option>, - /// Properties of the [`SchemaObject`] which define validation assertions for strings. - #[serde(flatten, deserialize_with = "skip_if_default")] - pub string: Option>, - /// Properties of the [`SchemaObject`] which define validation assertions for arrays. - #[serde(flatten, deserialize_with = "skip_if_default")] - pub array: Option>, - /// Properties of the [`SchemaObject`] which define validation assertions for objects. - #[serde(flatten, deserialize_with = "skip_if_default")] - pub object: Option>, - /// The `$ref` keyword. - /// - /// See [JSON Schema 8.2.4.1. Direct References with "$ref"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.4.1). - #[serde(rename = "$ref", skip_serializing_if = "Option::is_none")] - pub reference: Option, - /// Arbitrary extra properties which are not part of the JSON Schema specification, or which `schemars` does not support. - #[serde(flatten)] - pub extensions: Map, -} + pub fn as_object_mut(&mut self) -> Option<&mut Map> { + self.0.as_object_mut() + } -// Deserializing "null" to `Option` directly results in `None`, -// this function instead makes it deserialize to `Some(Value::Null)`. -fn allow_null<'de, D>(de: D) -> Result, D::Error> -where - D: serde::Deserializer<'de>, -{ - Value::deserialize(de).map(Option::Some) -} + pub(crate) fn try_to_object(self) -> Result, bool> { + match self.0 { + Value::Object(m) => Ok(m), + Value::Bool(b) => Err(b), + _ => unreachable!(), + } + } -fn skip_if_default<'de, D, T>(deserializer: D) -> Result>, D::Error> -where - D: serde::Deserializer<'de>, - T: Deserialize<'de> + Default + PartialEq, -{ - let value = T::deserialize(deserializer)?; - if value == T::default() { - Ok(None) - } else { - Ok(Some(Box::new(value))) + pub fn to_value(self) -> Value { + self.0 } -} -macro_rules! get_or_insert_default_fn { - ($name:ident, $ret:ty) => { - get_or_insert_default_fn!( - concat!( - "Returns a mutable reference to this schema's [`", - stringify!($ret), - "`](#structfield.", - stringify!($name), - "), creating it if it was `None`." - ), - $name, - $ret - ); - }; - ($doc:expr, $name:ident, $ret:ty) => { - #[doc = $doc] - pub fn $name(&mut self) -> &mut $ret { - self.$name.get_or_insert_with(Default::default) + pub fn ensure_object(&mut self) -> &mut Map { + if let Some(b) = self.as_bool() { + let mut map = Map::new(); + if !b { + map.insert("not".into(), Value::Object(Map::new())); + } + self.0 = Value::Object(map); } - }; -} -impl SchemaObject { - /// Creates a new `$ref` schema. - /// - /// The given reference string should be a URI reference. This will usually be a JSON Pointer - /// in [URI Fragment representation](https://tools.ietf.org/html/rfc6901#section-6). - pub fn new_ref(reference: String) -> Self { - SchemaObject { - reference: Some(reference), - ..Default::default() - } + self.as_object_mut() + .expect("Schema value should be of type Object.") } - /// Returns `true` if `self` is a `$ref` schema. - /// - /// If `self` has `Some` [`reference`](struct.SchemaObject.html#structfield.reference) set, this returns `true`. - /// Otherwise, returns `false`. - pub fn is_ref(&self) -> bool { - self.reference.is_some() + pub(crate) fn has_type(&self, ty: &str) -> bool { + match self.0.get("type") { + Some(Value::Array(values)) => values.iter().any(|v| v.as_str() == Some(ty)), + Some(Value::String(s)) => s == ty, + _ => false, + } } - /// Returns `true` if `self` accepts values of the given type, according to the [`instance_type`](struct.SchemaObject.html#structfield.instance_type) field. - /// - /// This is a basic check that always returns `true` if no `instance_type` is specified on the schema, - /// and does not check any subschemas. Because of this, both `{}` and `{"not": {}}` accept any type according - /// to this method. - pub fn has_type(&self, ty: InstanceType) -> bool { - self.instance_type - .as_ref() - .map_or(true, |x| x.contains(&ty)) + fn validate(value: &Value) -> Result<(), E> { + use serde::de::Unexpected; + let unexpected = match value { + Value::Bool(_) | Value::Object(_) => return Ok(()), + Value::Null => Unexpected::Unit, + Value::Number(n) => { + if let Some(u) = n.as_u64() { + Unexpected::Unsigned(u) + } else if let Some(i) = n.as_i64() { + Unexpected::Signed(i) + } else if let Some(f) = n.as_f64() { + Unexpected::Float(f) + } else { + unreachable!() + } + } + Value::String(s) => Unexpected::Str(s), + Value::Array(_) => Unexpected::Seq, + }; + + Err(E::invalid_type(unexpected, &"object or boolean")) } - get_or_insert_default_fn!(metadata, Metadata); - get_or_insert_default_fn!(subschemas, SubschemaValidation); - get_or_insert_default_fn!(number, NumberValidation); - get_or_insert_default_fn!(string, StringValidation); - get_or_insert_default_fn!(array, ArrayValidation); - get_or_insert_default_fn!(object, ObjectValidation); -} - -impl From for SchemaObject { - fn from(schema: Schema) -> Self { - schema.into_object() - } -} + #[allow(unsafe_code)] + #[ref_cast_custom] + fn ref_cast(value: &Value) -> &Self; -/// Properties which annotate a [`SchemaObject`] which typically have no effect when an object is being validated against the schema. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] -#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))] -#[serde(rename_all = "camelCase", default)] -pub struct Metadata { - /// 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. - /// - /// 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 `default` keyword. - /// - /// See [JSON Schema Validation 9.2. "default"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.2). - #[serde( - skip_serializing_if = "Option::is_none", - deserialize_with = "allow_null" - )] - pub default: Option, - /// The `deprecated` keyword. - /// - /// See [JSON Schema Validation 9.3. "deprecated"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.3). - #[serde(skip_serializing_if = "is_false")] - pub deprecated: bool, - /// The `readOnly` keyword. - /// - /// See [JSON Schema Validation 9.4. "readOnly" and "writeOnly"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4). - #[serde(skip_serializing_if = "is_false")] - pub read_only: bool, - /// The `writeOnly` keyword. - /// - /// See [JSON Schema Validation 9.4. "readOnly" and "writeOnly"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4). - #[serde(skip_serializing_if = "is_false")] - pub write_only: bool, - /// The `examples` keyword. - /// - /// See [JSON Schema Validation 9.5. "examples"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.5). - #[serde(skip_serializing_if = "Vec::is_empty")] - pub examples: Vec, + #[allow(unsafe_code)] + #[ref_cast_custom] + fn ref_cast_mut(value: &mut Value) -> &mut Self; } -#[allow(clippy::trivially_copy_pass_by_ref)] -fn is_false(b: &bool) -> bool { - !b +impl From for Value { + fn from(v: Schema) -> Value { + v.0 + } } -/// Properties of a [`SchemaObject`] which define validation assertions in terms of other schemas. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] -#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))] -#[serde(rename_all = "camelCase", default)] -pub struct SubschemaValidation { - /// The `allOf` keyword. - /// - /// See [JSON Schema 9.2.1.1. "allOf"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.1). - #[serde(skip_serializing_if = "Option::is_none")] - pub all_of: Option>, - /// The `anyOf` keyword. - /// - /// See [JSON Schema 9.2.1.2. "anyOf"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.2). - #[serde(skip_serializing_if = "Option::is_none")] - pub any_of: Option>, - /// The `oneOf` keyword. - /// - /// See [JSON Schema 9.2.1.3. "oneOf"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.3). - #[serde(skip_serializing_if = "Option::is_none")] - pub one_of: Option>, - /// The `not` keyword. - /// - /// See [JSON Schema 9.2.1.4. "not"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.4). - #[serde(skip_serializing_if = "Option::is_none")] - pub not: Option>, - /// The `if` keyword. - /// - /// See [JSON Schema 9.2.2.1. "if"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.1). - #[serde(rename = "if", skip_serializing_if = "Option::is_none")] - pub if_schema: Option>, - /// The `then` keyword. - /// - /// See [JSON Schema 9.2.2.2. "then"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.2). - #[serde(rename = "then", skip_serializing_if = "Option::is_none")] - pub then_schema: Option>, - /// The `else` keyword. - /// - /// See [JSON Schema 9.2.2.3. "else"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.3). - #[serde(rename = "else", skip_serializing_if = "Option::is_none")] - pub else_schema: Option>, -} +impl std::convert::TryFrom for Schema { + type Error = serde_json::Error; -/// Properties of a [`SchemaObject`] which define validation assertions for numbers. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] -#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))] -#[serde(rename_all = "camelCase", default)] -pub struct NumberValidation { - /// The `multipleOf` keyword. - /// - /// See [JSON Schema Validation 6.2.1. "multipleOf"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.1). - #[serde(skip_serializing_if = "Option::is_none")] - pub multiple_of: Option, - /// The `maximum` keyword. - /// - /// See [JSON Schema Validation 6.2.2. "maximum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.2). - #[serde(skip_serializing_if = "Option::is_none")] - pub maximum: Option, - /// The `exclusiveMaximum` keyword. - /// - /// See [JSON Schema Validation 6.2.3. "exclusiveMaximum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.3). - #[serde(skip_serializing_if = "Option::is_none")] - pub exclusive_maximum: Option, - /// The `minimum` keyword. - /// - /// See [JSON Schema Validation 6.2.4. "minimum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.4). - #[serde(skip_serializing_if = "Option::is_none")] - pub minimum: Option, - /// The `exclusiveMinimum` keyword. - /// - /// See [JSON Schema Validation 6.2.5. "exclusiveMinimum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.5). - #[serde(skip_serializing_if = "Option::is_none")] - pub exclusive_minimum: Option, + fn try_from(value: Value) -> serde_json::Result { + Schema::validate(&value)?; + Ok(Schema(value)) + } } -/// Properties of a [`SchemaObject`] which define validation assertions for strings. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] -#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))] -#[serde(rename_all = "camelCase", default)] -pub struct StringValidation { - /// The `maxLength` keyword. - /// - /// See [JSON Schema Validation 6.3.1. "maxLength"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.1). - #[serde(skip_serializing_if = "Option::is_none")] - pub max_length: Option, - /// The `minLength` keyword. - /// - /// See [JSON Schema Validation 6.3.2. "minLength"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.2). - #[serde(skip_serializing_if = "Option::is_none")] - pub min_length: Option, - /// The `pattern` keyword. - /// - /// See [JSON Schema Validation 6.3.3. "pattern"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.3). - #[serde(skip_serializing_if = "Option::is_none")] - pub pattern: Option, -} +impl<'a> std::convert::TryFrom<&'a Value> for &'a Schema { + type Error = serde_json::Error; -/// Properties of a [`SchemaObject`] which define validation assertions for arrays. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] -#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))] -#[serde(rename_all = "camelCase", default)] -pub struct ArrayValidation { - /// The `items` keyword. - /// - /// See [JSON Schema 9.3.1.1. "items"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.1). - #[serde(skip_serializing_if = "Option::is_none")] - pub items: Option>, - /// The `additionalItems` keyword. - /// - /// See [JSON Schema 9.3.1.2. "additionalItems"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.2). - #[serde(skip_serializing_if = "Option::is_none")] - pub additional_items: Option>, - /// The `maxItems` keyword. - /// - /// See [JSON Schema Validation 6.4.1. "maxItems"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.1). - #[serde(skip_serializing_if = "Option::is_none")] - pub max_items: Option, - /// The `minItems` keyword. - /// - /// See [JSON Schema Validation 6.4.2. "minItems"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.2). - #[serde(skip_serializing_if = "Option::is_none")] - pub min_items: Option, - /// The `uniqueItems` keyword. - /// - /// See [JSON Schema Validation 6.4.3. "uniqueItems"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.3). - #[serde(skip_serializing_if = "Option::is_none")] - pub unique_items: Option, - /// The `contains` keyword. - /// - /// See [JSON Schema 9.3.1.4. "contains"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.4). - #[serde(skip_serializing_if = "Option::is_none")] - pub contains: Option>, + fn try_from(value: &Value) -> serde_json::Result<&Schema> { + Schema::validate(value)?; + Ok(Schema::ref_cast(value)) + } } -/// Properties of a [`SchemaObject`] which define validation assertions for objects. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] -#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))] -#[serde(rename_all = "camelCase", default)] -pub struct ObjectValidation { - /// The `maxProperties` keyword. - /// - /// See [JSON Schema Validation 6.5.1. "maxProperties"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.1). - #[serde(skip_serializing_if = "Option::is_none")] - pub max_properties: Option, - /// The `minProperties` keyword. - /// - /// See [JSON Schema Validation 6.5.2. "minProperties"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.2). - #[serde(skip_serializing_if = "Option::is_none")] - pub min_properties: Option, - /// The `required` keyword. - /// - /// See [JSON Schema Validation 6.5.3. "required"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.3). - #[serde(skip_serializing_if = "Set::is_empty")] - pub required: Set, - /// The `properties` keyword. - /// - /// See [JSON Schema 9.3.2.1. "properties"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.1). - #[serde(skip_serializing_if = "Map::is_empty")] - pub properties: Map, - /// The `patternProperties` keyword. - /// - /// See [JSON Schema 9.3.2.2. "patternProperties"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.2). - #[serde(skip_serializing_if = "Map::is_empty")] - pub pattern_properties: Map, - /// The `additionalProperties` keyword. - /// - /// See [JSON Schema 9.3.2.3. "additionalProperties"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.3). - #[serde(skip_serializing_if = "Option::is_none")] - pub additional_properties: Option>, - /// The `propertyNames` keyword. - /// - /// See [JSON Schema 9.3.2.5. "propertyNames"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.5). - #[serde(skip_serializing_if = "Option::is_none")] - pub property_names: Option>, -} +impl<'a> std::convert::TryFrom<&'a mut Value> for &'a mut Schema { + type Error = serde_json::Error; -/// The possible types of values in JSON Schema documents. -/// -/// See [JSON Schema 4.2.1. Instance Data Model](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-4.2.1). -#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))] -#[serde(rename_all = "camelCase")] -pub enum InstanceType { - Null, - Boolean, - Object, - Array, - Number, - String, - Integer, + fn try_from(value: &mut Value) -> serde_json::Result<&mut Schema> { + Schema::validate(value)?; + Ok(Schema::ref_cast_mut(value)) + } } -/// A type which can be serialized as a single item, or multiple items. -/// -/// In some contexts, a `Single` may be semantically distinct from a `Vec` containing only item. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))] -#[serde(untagged)] -pub enum SingleOrVec { - Single(Box), - Vec(Vec), +impl Default for Schema { + fn default() -> Self { + Self(Value::Object(Map::new())) + } } -impl From for SingleOrVec { - fn from(single: T) -> Self { - SingleOrVec::Single(Box::new(single)) +impl From> for Schema { + fn from(o: Map) -> Self { + Schema(Value::Object(o)) } } -impl From> for SingleOrVec { - fn from(vec: Vec) -> Self { - SingleOrVec::Vec(vec) +impl From for Schema { + fn from(b: bool) -> Self { + Schema(Value::Bool(b)) } } -impl SingleOrVec { - /// Returns `true` if `self` is either a `Single` equal to `x`, or a `Vec` containing `x`. - /// - /// # Examples - /// - /// ``` - ///# extern crate apistos_schemars as schemars; - /// use schemars::schema::SingleOrVec; - /// - /// let s = SingleOrVec::from(10); - /// assert!(s.contains(&10)); - /// assert!(!s.contains(&20)); - /// - /// let v = SingleOrVec::from(vec![10, 20]); - /// assert!(v.contains(&10)); - /// assert!(v.contains(&20)); - /// assert!(!v.contains(&30)); - /// ``` - pub fn contains(&self, x: &T) -> bool { - match self { - SingleOrVec::Single(s) => s.deref() == x, - SingleOrVec::Vec(v) => v.contains(x), +mod ser { + use serde::ser::{Serialize, SerializeMap, SerializeSeq}; + use serde_json::Value; + + const ORDERED_KEYWORDS_START: [&str; 6] = + ["$id", "$schema", "title", "description", "type", "format"]; + const ORDERED_KEYWORDS_END: [&str; 2] = ["$defs", "definitions"]; + + pub(super) struct OrderedKeywordWrapper<'a>(pub &'a Value); + + impl Serialize for OrderedKeywordWrapper<'_> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self.0 { + Value::Array(array) => { + let mut seq = serializer.serialize_seq(Some(array.len()))?; + for value in array { + seq.serialize_element(&OrderedKeywordWrapper(value))?; + } + seq.end() + } + Value::Object(object) => { + let mut map = serializer.serialize_map(Some(object.len()))?; + + for key in ORDERED_KEYWORDS_START { + if let Some(value) = object.get(key) { + map.serialize_entry(key, &OrderedKeywordWrapper(value))?; + } + } + + for (key, value) in object { + if !ORDERED_KEYWORDS_START.contains(&key.as_str()) + && !ORDERED_KEYWORDS_END.contains(&key.as_str()) + { + map.serialize_entry(key, &OrderedKeywordWrapper(value))?; + } + } + + for key in ORDERED_KEYWORDS_END { + if let Some(value) = object.get(key) { + map.serialize_entry(key, &OrderedKeywordWrapper(value))?; + } + } + + map.end() + } + _ => self.0.serialize(serializer), + } } } } diff --git a/schemars/src/ser.rs b/schemars/src/ser.rs index 3f69bef..8be7b34 100644 --- a/schemars/src/ser.rs +++ b/schemars/src/ser.rs @@ -1,8 +1,7 @@ -use crate::schema::*; -use crate::JsonSchema; -use crate::{gen::SchemaGenerator, Map}; -use serde_json::{Error, Value}; -use std::{convert::TryInto, fmt::Display}; +use crate::gen::SchemaGenerator; +use crate::{json_schema, JsonSchema, Schema}; +use serde_json::{Error, Map, Value}; +use std::fmt::Display; pub(crate) struct Serializer<'a> { pub(crate) gen: &'a mut SchemaGenerator, @@ -22,7 +21,7 @@ pub(crate) struct SerializeTuple<'a> { pub(crate) struct SerializeMap<'a> { gen: &'a mut SchemaGenerator, - properties: Map, + properties: Map, current_key: Option, title: &'static str, } @@ -36,13 +35,11 @@ macro_rules! forward_to_subschema_for { } macro_rules! return_instance_type { - ($fn:ident, $ty:ty, $instance_type:ident) => { + ($fn:ident, $ty:ty, $instance_type:expr) => { fn $fn(self, _value: $ty) -> Result { - Ok(SchemaObject { - instance_type: Some(InstanceType::$instance_type.into()), - ..Default::default() - } - .into()) + Ok(json_schema!({ + "type": $instance_type + })) } }; } @@ -59,18 +56,18 @@ impl<'a> serde::Serializer for Serializer<'a> { type SerializeStruct = SerializeMap<'a>; type SerializeStructVariant = Self; - return_instance_type!(serialize_i8, i8, Integer); - return_instance_type!(serialize_i16, i16, Integer); - return_instance_type!(serialize_i32, i32, Integer); - return_instance_type!(serialize_i64, i64, Integer); - return_instance_type!(serialize_i128, i128, Integer); - return_instance_type!(serialize_u8, u8, Integer); - return_instance_type!(serialize_u16, u16, Integer); - return_instance_type!(serialize_u32, u32, Integer); - return_instance_type!(serialize_u64, u64, Integer); - return_instance_type!(serialize_u128, u128, Integer); - return_instance_type!(serialize_f32, f32, Number); - return_instance_type!(serialize_f64, f64, Number); + return_instance_type!(serialize_i8, i8, "integer"); + return_instance_type!(serialize_i16, i16, "integer"); + return_instance_type!(serialize_i32, i32, "integer"); + return_instance_type!(serialize_i64, i64, "integer"); + return_instance_type!(serialize_i128, i128, "integer"); + return_instance_type!(serialize_u8, u8, "integer"); + return_instance_type!(serialize_u16, u16, "integer"); + return_instance_type!(serialize_u32, u32, "integer"); + return_instance_type!(serialize_u64, u64, "integer"); + return_instance_type!(serialize_u128, u128, "integer"); + return_instance_type!(serialize_f32, f32, "number"); + return_instance_type!(serialize_f64, f64, "number"); forward_to_subschema_for!(serialize_bool, bool); forward_to_subschema_for!(serialize_char, char); @@ -93,7 +90,7 @@ impl<'a> serde::Serializer for Serializer<'a> { let value_schema = iter .into_iter() .try_fold(None, |acc, (_, v)| { - if acc == Some(Schema::Bool(true)) { + if acc == Some(true.into()) { return Ok(acc); } @@ -103,21 +100,16 @@ impl<'a> serde::Serializer for Serializer<'a> { })?; Ok(match &acc { None => Some(schema), - Some(items) if items != &schema => Some(Schema::Bool(true)), + Some(items) if items != &schema => Some(true.into()), _ => acc, }) })? - .unwrap_or(Schema::Bool(true)); - - Ok(SchemaObject { - instance_type: Some(InstanceType::Object.into()), - object: Some(Box::new(ObjectValidation { - additional_properties: Some(Box::new(value_schema)), - ..ObjectValidation::default() - })), - ..SchemaObject::default() - } - .into()) + .unwrap_or(true.into()); + + Ok(json_schema!({ + "type": "object", + "additionalProperties": value_schema, + })) } fn serialize_none(self) -> Result { @@ -132,52 +124,47 @@ impl<'a> serde::Serializer for Serializer<'a> { where T: serde::Serialize, { - // FIXME nasty duplication of `impl JsonSchema for Option` - fn add_null_type(instance_type: &mut SingleOrVec) { - match instance_type { - SingleOrVec::Single(ty) if **ty != InstanceType::Null => { - *instance_type = vec![**ty, InstanceType::Null].into() - } - SingleOrVec::Vec(ty) if !ty.contains(&InstanceType::Null) => { - ty.push(InstanceType::Null) - } - _ => {} - }; - } - let mut schema = value.serialize(Serializer { gen: self.gen, include_title: false, })?; if self.gen.settings().option_add_null_type { - schema = match schema { - Schema::Bool(true) => Schema::Bool(true), - Schema::Bool(false) => <()>::json_schema(self.gen), - Schema::Object(SchemaObject { - instance_type: Some(ref mut instance_type), - .. - }) => { - add_null_type(instance_type); - schema - } - schema => SchemaObject { - subschemas: Some(Box::new(SubschemaValidation { - any_of: Some(vec![schema, <()>::json_schema(self.gen)]), - ..Default::default() - })), - ..Default::default() + schema = match schema.try_to_object() { + Ok(mut obj) => { + let value = obj.get_mut("type"); + match value { + Some(Value::Array(array)) => { + let null = Value::from("null"); + if !array.contains(&null) { + array.push(null); + } + obj.into() + } + Some(Value::String(string)) => { + if string != "null" { + *value.unwrap() = + Value::Array(vec![std::mem::take(string).into(), "null".into()]) + } + obj.into() + } + _ => json_schema!({ + "anyOf": [ + obj, + <()>::json_schema(self.gen) + ] + }), + } } - .into(), + Err(true) => true.into(), + Err(false) => <()>::json_schema(self.gen), } } if self.gen.settings().option_nullable { - let mut schema_obj = schema.into_object(); - schema_obj - .extensions - .insert("nullable".to_owned(), serde_json::json!(true)); - schema = Schema::Object(schema_obj); + schema + .ensure_object() + .insert("nullable".into(), true.into()); }; Ok(schema) @@ -193,7 +180,7 @@ impl<'a> serde::Serializer for Serializer<'a> { _variant_index: u32, _variant: &'static str, ) -> Result { - Ok(Schema::Bool(true)) + Ok(true.into()) } fn serialize_newtype_struct( @@ -205,15 +192,13 @@ impl<'a> serde::Serializer for Serializer<'a> { T: serde::Serialize, { let include_title = self.include_title; - let mut result = value.serialize(self); + let mut schema = value.serialize(self)?; - if include_title { - if let Ok(Schema::Object(ref mut object)) = result { - object.metadata().title = Some(name.to_string()); - } + if include_title && !name.is_empty() { + schema.ensure_object().insert("title".into(), name.into()); } - result + Ok(schema) } fn serialize_newtype_variant( @@ -226,7 +211,7 @@ impl<'a> serde::Serializer for Serializer<'a> { where T: serde::Serialize, { - Ok(Schema::Bool(true)) + Ok(true.into()) } fn serialize_seq(self, _len: Option) -> Result { @@ -313,7 +298,7 @@ impl serde::ser::SerializeTupleVariant for Serializer<'_> { } fn end(self) -> Result { - Ok(Schema::Bool(true)) + Ok(true.into()) } } @@ -333,7 +318,7 @@ impl serde::ser::SerializeStructVariant for Serializer<'_> { } fn end(self) -> Result { - Ok(Schema::Bool(true)) + Ok(true.into()) } } @@ -345,7 +330,7 @@ impl serde::ser::SerializeSeq for SerializeSeq<'_> { where T: serde::Serialize, { - if self.items != Some(Schema::Bool(true)) { + if self.items != Some(true.into()) { let schema = value.serialize(Serializer { gen: self.gen, include_title: false, @@ -354,7 +339,7 @@ impl serde::ser::SerializeSeq for SerializeSeq<'_> { None => self.items = Some(schema), Some(items) => { if items != &schema { - self.items = Some(Schema::Bool(true)) + self.items = Some(true.into()) } } } @@ -364,16 +349,12 @@ impl serde::ser::SerializeSeq for SerializeSeq<'_> { } fn end(self) -> Result { - let items = self.items.unwrap_or(Schema::Bool(true)); - Ok(SchemaObject { - instance_type: Some(InstanceType::Array.into()), - array: Some(Box::new(ArrayValidation { - items: Some(items.into()), - ..ArrayValidation::default() - })), - ..SchemaObject::default() - } - .into()) + let items = self.items.unwrap_or(true.into()); + + Ok(json_schema!({ + "type": "array", + "items": items + })) } } @@ -394,23 +375,21 @@ impl serde::ser::SerializeTuple for SerializeTuple<'_> { } fn end(self) -> Result { - let len = self.items.len().try_into().ok(); - let mut schema = SchemaObject { - instance_type: Some(InstanceType::Array.into()), - array: Some(Box::new(ArrayValidation { - items: Some(SingleOrVec::Vec(self.items)), - max_items: len, - min_items: len, - ..ArrayValidation::default() - })), - ..SchemaObject::default() - }; + let len = self.items.len(); + let mut schema = json_schema!({ + "type": "array", + "items": self.items, + "maxItems": len, + "minItems": len, + }); if !self.title.is_empty() { - schema.metadata().title = Some(self.title.to_owned()); + schema + .ensure_object() + .insert("title".into(), self.title.into()); } - Ok(schema.into()) + Ok(schema) } } @@ -459,26 +438,24 @@ impl serde::ser::SerializeMap for SerializeMap<'_> { gen: self.gen, include_title: false, })?; - self.properties.insert(key, schema); + self.properties.insert(key, schema.into()); Ok(()) } fn end(self) -> Result { - let mut schema = SchemaObject { - instance_type: Some(InstanceType::Object.into()), - object: Some(Box::new(ObjectValidation { - properties: self.properties, - ..ObjectValidation::default() - })), - ..SchemaObject::default() - }; + let mut schema = json_schema!({ + "type": "object", + "properties": self.properties, + }); if !self.title.is_empty() { - schema.metadata().title = Some(self.title.to_owned()); + schema + .ensure_object() + .insert("title".into(), self.title.into()); } - Ok(schema.into()) + Ok(schema) } } @@ -498,7 +475,7 @@ impl serde::ser::SerializeStruct for SerializeMap<'_> { gen: self.gen, include_title: false, })?; - self.properties.insert(key.to_string(), prop_schema); + self.properties.insert(key.to_string(), prop_schema.into()); Ok(()) } diff --git a/schemars/src/visit.rs b/schemars/src/visit.rs index 049fc18..d228f2e 100644 --- a/schemars/src/visit.rs +++ b/schemars/src/visit.rs @@ -7,117 +7,88 @@ All methods of `Visitor` have a default implementation that makes no change but When overriding one of these methods, you will *usually* want to still call this default implementation. # Example -To add a custom property to all schemas: +To add a custom property to all object schemas: ``` # extern crate apistos_schemars as schemars; -use schemars::schema::SchemaObject; -use schemars::visit::{Visitor, visit_schema_object}; +use schemars::Schema; +use schemars::visit::{Visitor, visit_schema}; pub struct MyVisitor; impl Visitor for MyVisitor { - fn visit_schema_object(&mut self, schema: &mut SchemaObject) { + fn visit_schema(&mut self, schema: &mut Schema) { // First, make our change to this schema - schema.extensions.insert("my_property".to_string(), serde_json::json!("hello world")); + if let Some(obj) = schema.as_object_mut() { + obj.insert("my_property".to_string(), serde_json::json!("hello world")); + } // Then delegate to default implementation to visit any subschemas - visit_schema_object(self, schema); + visit_schema(self, schema); } } ``` */ -use crate::schema::{RootSchema, Schema, SchemaObject, SingleOrVec}; +use serde_json::{json, Value}; + +use crate::Schema; /// Trait used to recursively modify a constructed schema and its subschemas. pub trait Visitor { - /// Override this method to modify a [`RootSchema`] and (optionally) its subschemas. - /// - /// When overriding this method, you will usually want to call the [`visit_root_schema`] function to visit subschemas. - fn visit_root_schema(&mut self, root: &mut RootSchema) { - visit_root_schema(self, root) - } - /// Override this method to modify a [`Schema`] and (optionally) its subschemas. /// /// When overriding this method, you will usually want to call the [`visit_schema`] function to visit subschemas. fn visit_schema(&mut self, schema: &mut Schema) { visit_schema(self, schema) } - - /// Override this method to modify a [`SchemaObject`] and (optionally) its subschemas. - /// - /// When overriding this method, you will usually want to call the [`visit_schema_object`] function to visit subschemas. - fn visit_schema_object(&mut self, schema: &mut SchemaObject) { - visit_schema_object(self, schema) - } -} - -/// Visits all subschemas of the [`RootSchema`]. -pub fn visit_root_schema(v: &mut V, root: &mut RootSchema) { - v.visit_schema_object(&mut root.schema); - visit_map_values(v, &mut root.definitions); } /// Visits all subschemas of the [`Schema`]. pub fn visit_schema(v: &mut V, schema: &mut Schema) { - if let Schema::Object(schema) = schema { - v.visit_schema_object(schema) - } -} - -/// Visits all subschemas of the [`SchemaObject`]. -pub fn visit_schema_object(v: &mut V, schema: &mut SchemaObject) { - if let Some(sub) = &mut schema.subschemas { - visit_vec(v, &mut sub.all_of); - visit_vec(v, &mut sub.any_of); - visit_vec(v, &mut sub.one_of); - visit_box(v, &mut sub.not); - visit_box(v, &mut sub.if_schema); - visit_box(v, &mut sub.then_schema); - visit_box(v, &mut sub.else_schema); - } - - if let Some(arr) = &mut schema.array { - visit_single_or_vec(v, &mut arr.items); - visit_box(v, &mut arr.additional_items); - visit_box(v, &mut arr.contains); - } - - if let Some(obj) = &mut schema.object { - visit_map_values(v, &mut obj.properties); - visit_map_values(v, &mut obj.pattern_properties); - visit_box(v, &mut obj.additional_properties); - visit_box(v, &mut obj.property_names); - } -} - -fn visit_box(v: &mut V, target: &mut Option>) { - if let Some(s) = target { - v.visit_schema(s) - } -} - -fn visit_vec(v: &mut V, target: &mut Option>) { - if let Some(vec) = target { - for s in vec { - v.visit_schema(s) - } - } -} - -fn visit_map_values(v: &mut V, target: &mut crate::Map) { - for s in target.values_mut() { - v.visit_schema(s) - } -} - -fn visit_single_or_vec(v: &mut V, target: &mut Option>) { - match target { - None => {} - Some(SingleOrVec::Single(s)) => v.visit_schema(s), - Some(SingleOrVec::Vec(vec)) => { - for s in vec { - v.visit_schema(s) + if let Some(obj) = schema.as_object_mut() { + for (key, value) in obj { + match key.as_str() { + "not" + | "if" + | "then" + | "else" + | "additionalItems" + | "contains" + | "additionalProperties" + | "propertyNames" => { + if let Ok(subschema) = value.try_into() { + v.visit_schema(subschema) + } + } + "allOf" | "anyOf" | "oneOf" => { + if let Some(array) = value.as_array_mut() { + for value in array { + if let Ok(subschema) = value.try_into() { + v.visit_schema(subschema) + } + } + } + } + "items" => { + if let Some(array) = value.as_array_mut() { + for value in array { + if let Ok(subschema) = value.try_into() { + v.visit_schema(subschema) + } + } + } else if let Ok(subschema) = value.try_into() { + v.visit_schema(subschema) + } + } + "properties" | "patternProperties" | "definitions" | "$defs" => { + if let Some(obj) = value.as_object_mut() { + for value in obj.values_mut() { + if let Ok(subschema) = value.try_into() { + v.visit_schema(subschema) + } + } + } + } + _ => {} } } } @@ -134,29 +105,23 @@ pub struct ReplaceBoolSchemas { impl Visitor for ReplaceBoolSchemas { fn visit_schema(&mut self, schema: &mut Schema) { - visit_schema(self, schema); - - if let Schema::Bool(b) = *schema { - *schema = Schema::Bool(b).into_object().into() - } - } + if let Some(obj) = schema.as_object_mut() { + if self.skip_additional_properties { + if let Some((ap_key, ap_value)) = obj.remove_entry("additionalProperties") { + visit_schema(self, schema); - fn visit_schema_object(&mut self, schema: &mut SchemaObject) { - if self.skip_additional_properties { - if let Some(obj) = &mut schema.object { - if let Some(ap) = &obj.additional_properties { - if let Schema::Bool(_) = ap.as_ref() { - let additional_properties = obj.additional_properties.take(); - visit_schema_object(self, schema); - schema.object().additional_properties = additional_properties; - - return; + if let Some(obj) = schema.as_object_mut() { + obj.insert(ap_key, ap_value); } + + return; } } - } - visit_schema_object(self, schema); + visit_schema(self, schema); + } else { + schema.ensure_object(); + } } } @@ -167,18 +132,19 @@ impl Visitor for ReplaceBoolSchemas { pub struct RemoveRefSiblings; impl Visitor for RemoveRefSiblings { - fn visit_schema_object(&mut self, schema: &mut SchemaObject) { - visit_schema_object(self, schema); - - if let Some(reference) = schema.reference.take() { - if schema == &SchemaObject::default() { - schema.reference = Some(reference); - } else { - let ref_schema = Schema::new_ref(reference); - let all_of = &mut schema.subschemas().all_of; - match all_of { - Some(vec) => vec.push(ref_schema), - None => *all_of = Some(vec![ref_schema]), + fn visit_schema(&mut self, schema: &mut Schema) { + visit_schema(self, schema); + + if let Some(obj) = schema.as_object_mut() { + if obj.len() > 1 { + if let Some(ref_value) = obj.remove("$ref") { + if let Value::Array(all_of) = + obj.entry("allOf").or_insert(Value::Array(Vec::new())) + { + all_of.push(json!({ + "$ref": ref_value + })); + } } } } @@ -189,25 +155,18 @@ impl Visitor for RemoveRefSiblings { /// /// This is useful for dialects of JSON Schema (e.g. OpenAPI 3.0) that do not support the `examples` property. #[derive(Debug, Clone)] -pub struct SetSingleExample { - /// When set to `true`, the `examples` property will not be removed, but its first value will still be copied to `example`. - pub retain_examples: bool, -} +pub struct SetSingleExample; impl Visitor for SetSingleExample { - fn visit_schema_object(&mut self, schema: &mut SchemaObject) { - visit_schema_object(self, schema); - - let first_example = schema.metadata.as_mut().and_then(|m| { - if self.retain_examples { - m.examples.first().cloned() - } else { - m.examples.drain(..).next() - } - }); + fn visit_schema(&mut self, schema: &mut Schema) { + visit_schema(self, schema); - if let Some(example) = first_example { - schema.extensions.insert("example".to_owned(), example); + if let Some(obj) = schema.as_object_mut() { + if let Some(Value::Array(examples)) = obj.remove("examples") { + if let Some(first_example) = examples.into_iter().next() { + obj.insert("example".into(), first_example); + } + } } } } diff --git a/schemars/tests/arrayvec.rs b/schemars/tests/arrayvec.rs index b00b395..e32d1a7 100644 --- a/schemars/tests/arrayvec.rs +++ b/schemars/tests/arrayvec.rs @@ -1,16 +1,6 @@ mod util; use util::*; -#[test] -fn arrayvec05() -> TestResult { - test_default_generated_schema::>("arrayvec") -} - -#[test] -fn arrayvec05_string() -> TestResult { - test_default_generated_schema::>("arrayvec_string") -} - #[test] fn arrayvec07() -> TestResult { test_default_generated_schema::>("arrayvec") diff --git a/schemars/tests/bytes.rs b/schemars/tests/bytes.rs index 688ab21..5797411 100644 --- a/schemars/tests/bytes.rs +++ b/schemars/tests/bytes.rs @@ -1,5 +1,5 @@ mod util; -use bytes::{Bytes, BytesMut}; +use bytes1::{Bytes, BytesMut}; use util::*; #[test] diff --git a/schemars/tests/chrono.rs b/schemars/tests/chrono.rs index 31bb3f1..79eaf02 100644 --- a/schemars/tests/chrono.rs +++ b/schemars/tests/chrono.rs @@ -1,5 +1,5 @@ mod util; -use chrono::prelude::*; +use chrono04::prelude::*; extern crate apistos_schemars as schemars; use schemars::JsonSchema; use util::*; diff --git a/schemars/tests/decimal.rs b/schemars/tests/decimal.rs index d245583..246e813 100644 --- a/schemars/tests/decimal.rs +++ b/schemars/tests/decimal.rs @@ -3,12 +3,7 @@ use util::*; #[test] fn rust_decimal() -> TestResult { - test_default_generated_schema::("rust_decimal") -} - -#[test] -fn bigdecimal03() -> TestResult { - test_default_generated_schema::("bigdecimal03") + test_default_generated_schema::("rust_decimal") } #[test] diff --git a/schemars/tests/dereference.rs b/schemars/tests/dereference.rs deleted file mode 100644 index 62c8d0e..0000000 --- a/schemars/tests/dereference.rs +++ /dev/null @@ -1,24 +0,0 @@ -extern crate apistos_schemars as schemars; -use schemars::{gen::SchemaGenerator, JsonSchema}; -use std::ptr; - -#[allow(dead_code)] -#[derive(JsonSchema)] -struct Struct { - foo: i32, - bar: bool, -} - -#[test] -fn dereference_struct() { - let mut gen = SchemaGenerator::default(); - let struct_ref_schema = gen.subschema_for::(); - let struct_schema = gen.definitions().get(&::schema_name()).unwrap(); - - assert!(struct_ref_schema.is_ref()); - assert!(!struct_schema.is_ref()); - - let dereferenced = gen.dereference(&struct_ref_schema); - assert!(dereferenced.is_some()); - assert!(ptr::eq(dereferenced.unwrap(), struct_schema)); -} diff --git a/schemars/tests/either.rs b/schemars/tests/either.rs index 16a2e59..5dcd079 100644 --- a/schemars/tests/either.rs +++ b/schemars/tests/either.rs @@ -1,5 +1,5 @@ mod util; -use either::Either; +use either1::Either; use util::*; #[test] diff --git a/schemars/tests/enum.rs b/schemars/tests/enum.rs index dede863..244307d 100644 --- a/schemars/tests/enum.rs +++ b/schemars/tests/enum.rs @@ -1,6 +1,7 @@ mod util; extern crate apistos_schemars as schemars; -use schemars::{JsonSchema, Map}; +use std::collections::BTreeMap; +use schemars::JsonSchema; use util::*; // Ensure that schemars_derive uses the full path to std::string::String @@ -21,7 +22,7 @@ struct Struct { #[schemars(rename_all = "camelCase")] enum External { UnitOne, - StringMap(Map<&'static str, &'static str>), + StringMap(BTreeMap<&'static str, &'static str>), UnitStructNewType(UnitStruct), StructNewType(Struct), Struct { @@ -30,6 +31,7 @@ enum External { }, UnitTwo, Tuple(i32, bool), + // FIXME this should probably only replace the "payload" of the enum #[schemars(with = "i32")] WithInt, } @@ -44,7 +46,7 @@ fn enum_external_tag() -> TestResult { #[schemars(tag = "typeProperty")] enum Internal { UnitOne, - StringMap(Map<&'static str, &'static str>), + StringMap(BTreeMap<&'static str, &'static str>), UnitStructNewType(UnitStruct), StructNewType(Struct), Struct { @@ -52,6 +54,7 @@ enum Internal { bar: bool, }, UnitTwo, + // FIXME this should probably only replace the "payload" of the enum #[schemars(with = "i32")] WithInt, } @@ -66,7 +69,7 @@ fn enum_internal_tag() -> TestResult { #[schemars(untagged)] enum Untagged { UnitOne, - StringMap(Map<&'static str, &'static str>), + StringMap(BTreeMap<&'static str, &'static str>), UnitStructNewType(UnitStruct), StructNewType(Struct), Struct { @@ -74,6 +77,7 @@ enum Untagged { bar: bool, }, Tuple(i32, bool), + // FIXME this should probably only replace the "payload" of the enum #[schemars(with = "i32")] WithInt, } @@ -88,7 +92,7 @@ fn enum_untagged() -> TestResult { #[schemars(tag = "t", content = "c")] enum Adjacent { UnitOne, - StringMap(Map<&'static str, &'static str>), + StringMap(BTreeMap<&'static str, &'static str>), UnitStructNewType(UnitStruct), StructNewType(Struct), Struct { @@ -97,6 +101,7 @@ enum Adjacent { }, Tuple(i32, bool), UnitTwo, + // FIXME this should probably only replace the "payload" of the enum #[schemars(with = "i32")] WithInt, } diff --git a/schemars/tests/enum_deny_unknown_fields.rs b/schemars/tests/enum_deny_unknown_fields.rs index 7e868fe..7ab2eef 100644 --- a/schemars/tests/enum_deny_unknown_fields.rs +++ b/schemars/tests/enum_deny_unknown_fields.rs @@ -1,6 +1,7 @@ mod util; extern crate apistos_schemars as schemars; -use schemars::{JsonSchema, Map}; +use std::collections::BTreeMap; +use schemars::JsonSchema; use util::*; // Ensure that schemars_derive uses the full path to std::string::String @@ -23,7 +24,7 @@ struct Struct { #[schemars(rename_all = "camelCase", deny_unknown_fields)] enum External { UnitOne, - StringMap(Map<&'static str, &'static str>), + StringMap(BTreeMap<&'static str, &'static str>), UnitStructNewType(UnitStruct), StructNewType(Struct), Struct { @@ -32,6 +33,7 @@ enum External { }, UnitTwo, Tuple(i32, bool), + // FIXME this should probably only replace the "payload" of the enum #[schemars(with = "i32")] WithInt, } @@ -47,7 +49,7 @@ fn enum_external_tag() -> TestResult { #[schemars(tag = "typeProperty", deny_unknown_fields)] enum Internal { UnitOne, - StringMap(Map<&'static str, &'static str>), + StringMap(BTreeMap<&'static str, &'static str>), UnitStructNewType(UnitStruct), StructNewType(Struct), Struct { @@ -55,6 +57,7 @@ enum Internal { bar: bool, }, UnitTwo, + // FIXME this should only replace the "payload" of the enum (which doesn't even make sense for unit enums!) #[schemars(with = "i32")] WithInt, } @@ -70,7 +73,7 @@ fn enum_internal_tag() -> TestResult { #[schemars(untagged, deny_unknown_fields)] enum Untagged { UnitOne, - StringMap(Map<&'static str, &'static str>), + StringMap(BTreeMap<&'static str, &'static str>), UnitStructNewType(UnitStruct), StructNewType(Struct), Struct { @@ -78,6 +81,7 @@ enum Untagged { bar: bool, }, Tuple(i32, bool), + // FIXME this should probably only replace the "payload" of the enum #[schemars(with = "i32")] WithInt, } @@ -93,7 +97,7 @@ fn enum_untagged() -> TestResult { #[schemars(tag = "t", content = "c", deny_unknown_fields)] enum Adjacent { UnitOne, - StringMap(Map<&'static str, &'static str>), + StringMap(BTreeMap<&'static str, &'static str>), UnitStructNewType(UnitStruct), StructNewType(Struct), Struct { @@ -102,6 +106,7 @@ enum Adjacent { }, Tuple(i32, bool), UnitTwo, + // FIXME this should probably only replace the "payload" of the enum #[schemars(with = "i32")] WithInt, } diff --git a/schemars/tests/enumset.rs b/schemars/tests/enumset.rs index 1f7ee6c..caf4480 100644 --- a/schemars/tests/enumset.rs +++ b/schemars/tests/enumset.rs @@ -1,9 +1,12 @@ mod util; extern crate apistos_schemars as schemars; -use enumset::{EnumSet, EnumSetType}; +use enumset1::{EnumSet, EnumSetType}; use schemars::JsonSchema; use util::*; +// needed to derive EnumSetType when using a crate alias +extern crate enumset1 as enumset; + #[derive(EnumSetType, JsonSchema)] enum Foo { Bar, diff --git a/schemars/tests/expected/arrayvec_string.json b/schemars/tests/expected/arrayvec_string.json index 42f099a..ad174d8 100644 --- a/schemars/tests/expected/arrayvec_string.json +++ b/schemars/tests/expected/arrayvec_string.json @@ -1,5 +1,5 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "String", + "title": "string", "type": "string" } \ No newline at end of file diff --git a/schemars/tests/expected/bigdecimal03.json b/schemars/tests/expected/bigdecimal03.json deleted file mode 100644 index 855db6f..0000000 --- a/schemars/tests/expected/bigdecimal03.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Decimal", - "type": "string", - "pattern": "^-?[0-9]+(\\.[0-9]+)?$" -} \ No newline at end of file diff --git a/schemars/tests/expected/either.json b/schemars/tests/expected/either.json index b028057..807c9c9 100644 --- a/schemars/tests/expected/either.json +++ b/schemars/tests/expected/either.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Either_int32_or_Either_Boolean_or_Null", + "title": "Either_int32_or_Either_boolean_or_null", "anyOf": [ { "type": "integer", diff --git a/schemars/tests/expected/enum-internal-duf.json b/schemars/tests/expected/enum-internal-duf.json index fc36644..501f13b 100644 --- a/schemars/tests/expected/enum-internal-duf.json +++ b/schemars/tests/expected/enum-internal-duf.json @@ -127,8 +127,7 @@ "WithInt" ] } - }, - "additionalProperties": false + } } ] } \ No newline at end of file diff --git a/schemars/tests/expected/range.json b/schemars/tests/expected/range.json index a3b14af..0bb3aeb 100644 --- a/schemars/tests/expected/range.json +++ b/schemars/tests/expected/range.json @@ -15,7 +15,7 @@ "$ref": "#/definitions/Range_of_double" }, "bound": { - "$ref": "#/definitions/Bound_of_String" + "$ref": "#/definitions/Bound_of_string" } }, "definitions": { @@ -55,7 +55,7 @@ } } }, - "Bound_of_String": { + "Bound_of_string": { "oneOf": [ { "type": "object", diff --git a/schemars/tests/expected/remote_derive_generic.json b/schemars/tests/expected/remote_derive_generic.json index 2fea80a..5a65f3b 100644 --- a/schemars/tests/expected/remote_derive_generic.json +++ b/schemars/tests/expected/remote_derive_generic.json @@ -10,10 +10,10 @@ ], "properties": { "byte_or_bool2": { - "$ref": "#/definitions/Or_for_uint8_and_Boolean" + "$ref": "#/definitions/Or_for_uint8_and_boolean" }, "unit_or_t2": { - "$ref": "#/definitions/Or_for_Null_and_int32" + "$ref": "#/definitions/Or_for_null_and_int32" }, "s": { "$ref": "#/definitions/Str" @@ -30,7 +30,7 @@ } }, "definitions": { - "Or_for_uint8_and_Boolean": { + "Or_for_uint8_and_boolean": { "anyOf": [ { "type": "integer", @@ -42,7 +42,7 @@ } ] }, - "Or_for_Null_and_int32": { + "Or_for_null_and_int32": { "anyOf": [ { "type": "null" diff --git a/schemars/tests/expected/result.json b/schemars/tests/expected/result.json index d8d6ec1..2a01573 100644 --- a/schemars/tests/expected/result.json +++ b/schemars/tests/expected/result.json @@ -8,14 +8,14 @@ ], "properties": { "result1": { - "$ref": "#/definitions/Result_of_MyStruct_or_Array_of_String" + "$ref": "#/definitions/Result_of_MyStruct_or_Array_of_string" }, "result2": { - "$ref": "#/definitions/Result_of_Boolean_or_Null" + "$ref": "#/definitions/Result_of_boolean_or_null" } }, "definitions": { - "Result_of_MyStruct_or_Array_of_String": { + "Result_of_MyStruct_or_Array_of_string": { "oneOf": [ { "type": "object", @@ -56,7 +56,7 @@ } } }, - "Result_of_Boolean_or_Null": { + "Result_of_boolean_or_null": { "oneOf": [ { "type": "object", diff --git a/schemars/tests/expected/schema-name-custom.json b/schemars/tests/expected/schema-name-custom.json index 866fb9d..844699a 100644 --- a/schemars/tests/expected/schema-name-custom.json +++ b/schemars/tests/expected/schema-name-custom.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "a-new-name-Array_of_String-int32-int32", + "title": "a-new-name-Array_of_string-int32-int32", "type": "object", "required": [ "inner", diff --git a/schemars/tests/expected/schema-name-default.json b/schemars/tests/expected/schema-name-default.json index 31f7f26..39c39a9 100644 --- a/schemars/tests/expected/schema-name-default.json +++ b/schemars/tests/expected/schema-name-default.json @@ -1,15 +1,11 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "MyStruct_for_int32_and_Null_and_Boolean_and_Array_of_String", + "title": "MyStruct_for_int32_and_null_and_boolean_and_Array_of_string", "type": "object", - "required": [ - "inner", - "t", - "u", - "v", - "w" - ], "properties": { + "inner": { + "$ref": "#/definitions/MySimpleStruct" + }, "t": { "type": "integer", "format": "int32" @@ -25,23 +21,27 @@ "items": { "type": "string" } - }, - "inner": { - "$ref": "#/definitions/MySimpleStruct" } }, + "required": [ + "inner", + "t", + "u", + "v", + "w" + ], "definitions": { "MySimpleStruct": { "type": "object", - "required": [ - "foo" - ], "properties": { "foo": { "type": "integer", "format": "int32" } - } + }, + "required": [ + "foo" + ] } } } \ No newline at end of file diff --git a/schemars/tests/expected/schema-name-mixed-generics.json b/schemars/tests/expected/schema-name-mixed-generics.json index 32ac797..bddd083 100644 --- a/schemars/tests/expected/schema-name-mixed-generics.json +++ b/schemars/tests/expected/schema-name-mixed-generics.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "MixedGenericStruct_for_MyStruct_for_int32_and_Null_and_Boolean_and_Array_of_String_and_42_and_z", + "title": "MixedGenericStruct_for_MyStruct_for_int32_and_null_and_boolean_and_Array_of_string_and_42_and_z", "type": "object", "required": [ "foo", @@ -12,7 +12,7 @@ "format": "int32" }, "generic": { - "$ref": "#/definitions/MyStruct_for_int32_and_Null_and_Boolean_and_Array_of_String" + "$ref": "#/definitions/MyStruct_for_int32_and_null_and_boolean_and_Array_of_string" } }, "definitions": { @@ -28,7 +28,7 @@ } } }, - "MyStruct_for_int32_and_Null_and_Boolean_and_Array_of_String": { + "MyStruct_for_int32_and_null_and_boolean_and_Array_of_string": { "type": "object", "required": [ "inner", diff --git a/schemars/tests/expected/schema_with-enum-internal.json b/schemars/tests/expected/schema_with-enum-internal.json index 7ede7e6..75b28dc 100644 --- a/schemars/tests/expected/schema_with-enum-internal.json +++ b/schemars/tests/expected/schema_with-enum-internal.json @@ -22,8 +22,8 @@ }, { "type": [ - "boolean", - "object" + "object", + "boolean" ], "required": [ "typeProperty" @@ -39,8 +39,8 @@ }, { "type": [ - "boolean", - "object" + "object", + "boolean" ], "required": [ "typeProperty" diff --git a/schemars/tests/expected/smallvec.json b/schemars/tests/expected/smallvec.json index eab45c0..7ce011d 100644 --- a/schemars/tests/expected/smallvec.json +++ b/schemars/tests/expected/smallvec.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Array_of_String", + "title": "Array_of_string", "type": "array", "items": { "type": "string" diff --git a/schemars/tests/expected/smol_str.json b/schemars/tests/expected/smol_str.json index 42f099a..ad174d8 100644 --- a/schemars/tests/expected/smol_str.json +++ b/schemars/tests/expected/smol_str.json @@ -1,5 +1,5 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "String", + "title": "string", "type": "string" } \ No newline at end of file diff --git a/schemars/tests/indexmap.rs b/schemars/tests/indexmap.rs index 28a8c36..76c0bf0 100644 --- a/schemars/tests/indexmap.rs +++ b/schemars/tests/indexmap.rs @@ -1,14 +1,16 @@ mod util; extern crate apistos_schemars as schemars; -use indexmap::{IndexMap, IndexSet}; +use std::hash::RandomState; + +use indexmap2::{IndexMap, IndexSet}; use schemars::JsonSchema; use util::*; #[allow(dead_code)] #[derive(JsonSchema)] struct IndexMapTypes { - map: IndexMap, - set: IndexSet, + map: IndexMap, + set: IndexSet, } #[test] diff --git a/schemars/tests/indexmap2.rs b/schemars/tests/indexmap2.rs deleted file mode 100644 index 1808294..0000000 --- a/schemars/tests/indexmap2.rs +++ /dev/null @@ -1,17 +0,0 @@ -mod util; -extern crate apistos_schemars as schemars; -use indexmap2::{IndexMap, IndexSet}; -use schemars::JsonSchema; -use util::*; - -#[allow(dead_code)] -#[derive(JsonSchema)] -struct IndexMapTypes { - map: IndexMap, - set: IndexSet, -} - -#[test] -fn indexmap_types() -> TestResult { - test_default_generated_schema::("indexmap") -} diff --git a/schemars/tests/schema_for_schema.rs b/schemars/tests/schema_for_schema.rs deleted file mode 100644 index 1e270d0..0000000 --- a/schemars/tests/schema_for_schema.rs +++ /dev/null @@ -1,20 +0,0 @@ -mod util; -extern crate apistos_schemars as schemars; -use schemars::gen::SchemaSettings; -use schemars::schema::RootSchema; -use util::*; - -#[test] -fn schema_matches_draft07() -> TestResult { - test_generated_schema::("schema", SchemaSettings::draft07()) -} - -#[test] -fn schema_matches_2019_09() -> TestResult { - test_generated_schema::("schema-2019_09", SchemaSettings::draft2019_09()) -} - -#[test] -fn schema_matches_openapi3() -> TestResult { - test_generated_schema::("schema-openapi3", SchemaSettings::openapi3()) -} diff --git a/schemars/tests/schema_with_enum.rs b/schemars/tests/schema_with_enum.rs index e3baf02..7a6fc65 100644 --- a/schemars/tests/schema_with_enum.rs +++ b/schemars/tests/schema_with_enum.rs @@ -3,7 +3,7 @@ extern crate apistos_schemars as schemars; use schemars::JsonSchema; use util::*; -fn schema_fn(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { +fn schema_fn(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema { ::json_schema(gen) } @@ -22,6 +22,7 @@ pub enum External { #[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema, i32, ), + // FIXME this should probably only replace the "payload" of the enum #[schemars(schema_with = "schema_fn")] Unit, } @@ -39,6 +40,7 @@ pub enum Internal { foo: DoesntImplementJsonSchema, }, NewType(#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema), + // FIXME this should probably only replace the "payload" of the enum #[schemars(schema_with = "schema_fn")] Unit, } @@ -60,6 +62,7 @@ pub enum Untagged { #[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema, i32, ), + // FIXME this should probably only replace the "payload" of the enum #[schemars(schema_with = "schema_fn")] Unit, } @@ -81,6 +84,7 @@ pub enum Adjacent { #[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema, i32, ), + // FIXME this should probably only replace the "payload" of the enum #[schemars(schema_with = "schema_fn")] Unit, } diff --git a/schemars/tests/schema_with_struct.rs b/schemars/tests/schema_with_struct.rs index 7d86a1f..19b6e67 100644 --- a/schemars/tests/schema_with_struct.rs +++ b/schemars/tests/schema_with_struct.rs @@ -3,7 +3,7 @@ extern crate apistos_schemars as schemars; use schemars::JsonSchema; use util::*; -fn schema_fn(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { +fn schema_fn(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema { ::json_schema(gen) } diff --git a/schemars/tests/semver.rs b/schemars/tests/semver.rs index 348cb45..d5450e8 100644 --- a/schemars/tests/semver.rs +++ b/schemars/tests/semver.rs @@ -1,7 +1,7 @@ mod util; extern crate apistos_schemars as schemars; use schemars::JsonSchema; -use semver::Version; +use semver1::Version; use util::*; #[allow(dead_code)] diff --git a/schemars/tests/smallvec.rs b/schemars/tests/smallvec.rs index bc9c6ef..8412a6a 100644 --- a/schemars/tests/smallvec.rs +++ b/schemars/tests/smallvec.rs @@ -1,5 +1,5 @@ mod util; -use smallvec::SmallVec; +use smallvec1::SmallVec; use util::*; #[test] diff --git a/schemars/tests/smol_str.rs b/schemars/tests/smol_str.rs index 1e48196..43fad30 100644 --- a/schemars/tests/smol_str.rs +++ b/schemars/tests/smol_str.rs @@ -1,5 +1,5 @@ mod util; -use smol_str::SmolStr; +use smol_str02::SmolStr; use util::*; #[test] diff --git a/schemars/tests/url.rs b/schemars/tests/url.rs index 40780a4..fcc4eeb 100644 --- a/schemars/tests/url.rs +++ b/schemars/tests/url.rs @@ -1,7 +1,7 @@ mod util; extern crate apistos_schemars as schemars; use schemars::JsonSchema; -use url::Url; +use url2::Url; use util::*; #[allow(dead_code)] diff --git a/schemars/tests/util/mod.rs b/schemars/tests/util/mod.rs index 0d4287e..3a977fd 100644 --- a/schemars/tests/util/mod.rs +++ b/schemars/tests/util/mod.rs @@ -1,6 +1,7 @@ use pretty_assertions::assert_eq; extern crate apistos_schemars as schemars; -use schemars::{gen::SchemaSettings, schema::RootSchema, schema_for, JsonSchema}; +use schemars::visit::Visitor; +use schemars::{gen::SchemaSettings, schema_for, JsonSchema, Schema}; use std::error::Error; use std::fs; @@ -18,7 +19,16 @@ pub fn test_default_generated_schema(file: &str) -> TestResult { test_schema(&actual, file) } -pub fn test_schema(actual: &RootSchema, file: &str) -> TestResult { +pub fn test_schema(actual: &Schema, file: &str) -> TestResult { + // TEMP for easier comparison of schemas handling changes that don't actually affect a schema: + // - `required` ordering has changed + // - previously `f64` properties may now be integers + let actual = &{ + let mut actual = actual.clone(); + TempFixupForTests.visit_schema(&mut actual); + actual + }; + let expected_json = match fs::read_to_string(format!("tests/expected/{}.json", file)) { Ok(j) => j, Err(e) => { @@ -36,8 +46,30 @@ pub fn test_schema(actual: &RootSchema, file: &str) -> TestResult { Ok(()) } -fn write_actual_to_file(schema: &RootSchema, file: &str) -> TestResult { +fn write_actual_to_file(schema: &Schema, file: &str) -> TestResult { let actual_json = serde_json::to_string_pretty(&schema)?; fs::write(format!("tests/actual/{}.json", file), actual_json)?; Ok(()) } + +struct TempFixupForTests; + +impl schemars::visit::Visitor for TempFixupForTests { + fn visit_schema(&mut self, schema: &mut Schema) { + schemars::visit::visit_schema(self, schema); + + if let Some(object) = schema.as_object_mut() { + if let Some(serde_json::Value::Array(required)) = object.get_mut("required") { + required.sort_unstable_by(|a, b| a.as_str().cmp(&b.as_str())); + } + + for (key, value) in object { + if key == "multipleOf" || key.ends_with("aximum") || key.ends_with("inimum") { + if let Some(f) = value.as_f64() { + *value = f.into(); + } + } + } + } + } +} diff --git a/schemars/tests/uuid.rs b/schemars/tests/uuid.rs index bd673b5..77e92c2 100644 --- a/schemars/tests/uuid.rs +++ b/schemars/tests/uuid.rs @@ -1,11 +1,6 @@ mod util; use util::*; -#[test] -fn uuid08() -> TestResult { - test_default_generated_schema::("uuid") -} - #[test] fn uuid1() -> TestResult { test_default_generated_schema::("uuid") diff --git a/schemars_derive/src/attr/validation.rs b/schemars_derive/src/attr/validation.rs index a511ab4..8d90302 100644 --- a/schemars_derive/src/attr/validation.rs +++ b/schemars_derive/src/attr/validation.rs @@ -325,119 +325,85 @@ impl ValidationAttrs { } pub fn apply_to_schema(&self, schema_expr: &mut TokenStream) { - if let Some(apply_expr) = self.apply_to_schema_expr() { - *schema_expr = quote! { - { - let mut schema = #schema_expr; - #apply_expr - schema - } - } + let setters = self.make_setters(quote!(&mut schema)); + if !setters.is_empty() { + *schema_expr = quote!({ + let mut schema = #schema_expr; + #(#setters)* + schema + }); } } - fn apply_to_schema_expr(&self) -> Option { - let mut array_validation = Vec::new(); - let mut number_validation = Vec::new(); - let mut object_validation = Vec::new(); - let mut string_validation = Vec::new(); + fn make_setters(&self, mut_schema: impl ToTokens) -> Vec { + let mut result = Vec::new(); if let Some(length_min) = self.length_min.as_ref().or(self.length_equal.as_ref()) { - string_validation.push(quote! { - validation.min_length = Some(#length_min as u32); + result.push(quote! { + schemars::_private::insert_validation_property(#mut_schema, "string", "minLength", #length_min); }); - array_validation.push(quote! { - validation.min_items = Some(#length_min as u32); + result.push(quote! { + schemars::_private::insert_validation_property(#mut_schema, "array", "minItems", #length_min); }); } if let Some(length_max) = self.length_max.as_ref().or(self.length_equal.as_ref()) { - string_validation.push(quote! { - validation.max_length = Some(#length_max as u32); + result.push(quote! { + schemars::_private::insert_validation_property(#mut_schema, "string", "maxLength", #length_max); }); - array_validation.push(quote! { - validation.max_items = Some(#length_max as u32); + result.push(quote! { + schemars::_private::insert_validation_property(#mut_schema, "array", "maxItems", #length_max); }); } if let Some(range_min) = &self.range_min { - number_validation.push(quote! { - validation.minimum = Some(#range_min as f64); + result.push(quote! { + schemars::_private::insert_validation_property(#mut_schema, "number", "minimum", #range_min); }); } if let Some(range_max) = &self.range_max { - number_validation.push(quote! { - validation.maximum = Some(#range_max as f64); + result.push(quote! { + schemars::_private::insert_validation_property(#mut_schema, "number", "maximum", #range_max); }); } if let Some(regex) = &self.regex { - string_validation.push(quote! { - validation.pattern = Some(#regex.to_string()); + result.push(quote! { + schemars::_private::insert_validation_property(#mut_schema, "string", "pattern", #regex); }); } if let Some(contains) = &self.contains { - object_validation.push(quote! { - validation.required.insert(#contains.to_string()); + result.push(quote! { + schemars::_private::append_required(#mut_schema, #contains); }); if self.regex.is_none() { let pattern = crate::regex_syntax::escape(contains); - string_validation.push(quote! { - validation.pattern = Some(#pattern.to_string()); + result.push(quote! { + schemars::_private::insert_validation_property(#mut_schema, "string", "pattern", #pattern); }); } } - let format = self.format.as_ref().map(|f| { - let f = f.schema_str(); - quote! { - schema_object.format = Some(#f.to_string()); - } - }); - - let inner_validation = self - .inner - .as_deref() - .and_then(|inner| inner.apply_to_schema_expr()) - .map(|apply_expr| { - quote! { - if schema_object.has_type(schemars::schema::InstanceType::Array) { - if let Some(schemars::schema::SingleOrVec::Single(inner_schema)) = &mut schema_object.array().items { - let mut schema = &mut **inner_schema; - #apply_expr - } - } - } - }); - - let array_validation = wrap_array_validation(array_validation); - let number_validation = wrap_number_validation(number_validation); - let object_validation = wrap_object_validation(object_validation); - let string_validation = wrap_string_validation(string_validation); - - if array_validation.is_some() - || number_validation.is_some() - || object_validation.is_some() - || string_validation.is_some() - || format.is_some() - || inner_validation.is_some() - { - Some(quote! { - if let schemars::schema::Schema::Object(schema_object) = &mut schema { - #array_validation - #number_validation - #object_validation - #string_validation - #format - #inner_validation - } + if let Some(format) = &self.format { + let f = format.schema_str(); + result.push(quote! { + schema.ensure_object().insert("format".to_owned(), #f.into()); }) - } else { - None + }; + + if let Some(inner) = &self.inner { + let inner_setters = inner.make_setters(quote!(schema)); + if !inner_setters.is_empty() { + result.push(quote! { + schemars::_private::apply_inner_validation(#mut_schema, |schema| { #(#inner_setters)* }); + }) + } } + + result } } @@ -456,59 +422,6 @@ fn parse_lit_into_expr_path( }) } -fn wrap_array_validation(v: Vec) -> Option { - if v.is_empty() { - None - } else { - Some(quote! { - if schema_object.has_type(schemars::schema::InstanceType::Array) { - let validation = schema_object.array(); - #(#v)* - } - }) - } -} - -fn wrap_number_validation(v: Vec) -> Option { - if v.is_empty() { - None - } else { - Some(quote! { - if schema_object.has_type(schemars::schema::InstanceType::Integer) - || schema_object.has_type(schemars::schema::InstanceType::Number) { - let validation = schema_object.number(); - #(#v)* - } - }) - } -} - -fn wrap_object_validation(v: Vec) -> Option { - if v.is_empty() { - None - } else { - Some(quote! { - if schema_object.has_type(schemars::schema::InstanceType::Object) { - let validation = schema_object.object(); - #(#v)* - } - }) - } -} - -fn wrap_string_validation(v: Vec) -> Option { - if v.is_empty() { - None - } else { - Some(quote! { - if schema_object.has_type(schemars::schema::InstanceType::String) { - let validation = schema_object.string(); - #(#v)* - } - }) - } -} - fn str_or_num_to_expr(cx: &Ctxt, meta_item_name: &str, expr: Expr) -> Option { // this odd double-parsing is to make `-10` parsed as an Lit instead of an Expr::Unary let lit: Lit = match syn::parse2(expr.to_token_stream()) { diff --git a/schemars_derive/src/lib.rs b/schemars_derive/src/lib.rs index ea6c0a6..c890f75 100644 --- a/schemars_derive/src/lib.rs +++ b/schemars_derive/src/lib.rs @@ -68,11 +68,11 @@ fn derive_json_schema(mut input: syn::DeriveInput, repr: bool) -> syn::Result::schema_id() } - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema { <#ty as schemars::JsonSchema>::json_schema(gen) } - fn _schemars_private_non_optional_json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + fn _schemars_private_non_optional_json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema { <#ty as schemars::JsonSchema>::_schemars_private_non_optional_json_schema(gen) } @@ -182,7 +182,7 @@ fn derive_json_schema(mut input: syn::DeriveInput, repr: bool) -> syn::Result schemars::schema::Schema { + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema { #schema_expr } }; diff --git a/schemars_derive/src/metadata.rs b/schemars_derive/src/metadata.rs index 3dd77a0..6de6834 100644 --- a/schemars_derive/src/metadata.rs +++ b/schemars_derive/src/metadata.rs @@ -13,32 +13,46 @@ pub struct SchemaMetadata<'a> { impl<'a> SchemaMetadata<'a> { pub fn apply_to_schema(&self, schema_expr: &mut TokenStream) { + let setters = self.make_setters(); + if !setters.is_empty() { + *schema_expr = quote! {{ + let mut schema = #schema_expr; + let obj = schema.ensure_object(); + #(#setters)* + schema + }} + } + } + + fn make_setters(&self) -> Vec { + let mut setters = Vec::::new(); + if let Some(title) = &self.title { - *schema_expr = quote! { - schemars::_private::metadata::add_title(#schema_expr, #title) - }; + setters.push(quote! { + obj.insert("title".to_owned(), #title.into()); + }); } if let Some(description) = &self.description { - *schema_expr = quote! { - schemars::_private::metadata::add_description(#schema_expr, #description) - }; + setters.push(quote! { + obj.insert("description".to_owned(), #description.into()); + }); } if self.deprecated { - *schema_expr = quote! { - schemars::_private::metadata::add_deprecated(#schema_expr, true) - }; + setters.push(quote! { + obj.insert("deprecated".to_owned(), true.into()); + }); } if self.read_only { - *schema_expr = quote! { - schemars::_private::metadata::add_read_only(#schema_expr, true) - }; + setters.push(quote! { + obj.insert("readOnly".to_owned(), true.into()); + }); } if self.write_only { - *schema_expr = quote! { - schemars::_private::metadata::add_write_only(#schema_expr, true) - }; + setters.push(quote! { + obj.insert("writeOnly".to_owned(), true.into()); + }); } if !self.examples.is_empty() { @@ -47,16 +61,19 @@ impl<'a> SchemaMetadata<'a> { schemars::_serde_json::value::to_value(#eg()) } }); - - *schema_expr = quote! { - schemars::_private::metadata::add_examples(#schema_expr, [#(#examples),*].into_iter().flatten()) - }; + setters.push(quote! { + obj.insert("examples".to_owned(), schemars::_serde_json::Value::Array([#(#examples),*].into_iter().flatten().collect())); + }); } if let Some(default) = &self.default { - *schema_expr = quote! { - schemars::_private::metadata::add_default(#schema_expr, #default.and_then(|d| schemars::_schemars_maybe_to_value!(d))) - }; + setters.push(quote! { + if let Some(default) = #default.and_then(|d| schemars::_schemars_maybe_to_value!(d)) { + obj.insert("default".to_owned(), default); + } + }); } + + setters } } diff --git a/schemars_derive/src/schema_exprs.rs b/schemars_derive/src/schema_exprs.rs index b625114..585184c 100644 --- a/schemars_derive/src/schema_exprs.rs +++ b/schemars_derive/src/schema_exprs.rs @@ -49,9 +49,18 @@ pub fn expr_for_repr(cont: &Container) -> Result { let enum_ident = &cont.ident; let variant_idents = variants.iter().map(|v| &v.ident); - let mut schema_expr = schema_object(quote! { - instance_type: Some(schemars::schema::InstanceType::Integer.into()), - enum_values: Some(vec![#((#enum_ident::#variant_idents as #repr_type).into()),*]), + let mut schema_expr = quote!({ + let mut map = schemars::_serde_json::Map::new(); + map.insert("type".to_owned(), "integer".into()); + map.insert( + "enum".to_owned(), + schemars::_serde_json::Value::Array({ + let mut enum_values = Vec::new(); + #(enum_values.push((#enum_ident::#variant_idents as #repr_type).into());)* + enum_values + }), + ); + schemars::Schema::from(map) }); cont.attrs.as_metadata().apply_to_schema(&mut schema_expr); @@ -118,7 +127,7 @@ fn type_for_schema(with_attr: &WithAttr) -> (syn::Type, Option) { )) } - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema { #fun(gen) } } @@ -160,9 +169,18 @@ fn expr_for_external_tagged_enum<'a>( }) .partition(|v| v.is_unit() && v.attrs.is_default()); let unit_names = unit_variants.iter().map(|v| v.name()); - let unit_schema = schema_object(quote! { - instance_type: Some(schemars::schema::InstanceType::String.into()), - enum_values: Some(vec![#(#unit_names.into()),*]), + let unit_schema = quote!({ + let mut map = schemars::_serde_json::Map::new(); + map.insert("type".to_owned(), "string".into()); + map.insert( + "enum".to_owned(), + schemars::_serde_json::Value::Array({ + let mut enum_values = Vec::new(); + #(enum_values.push((#unit_names).into());)* + enum_values + }), + ); + schemars::Schema::from(map) }); if complex_variants.is_empty() { @@ -276,47 +294,44 @@ fn expr_for_adjacent_tagged_enum<'a>( let (add_content_to_props, add_content_to_required) = content_schema .map(|content_schema| { ( - quote!(props.insert(#content_name.to_owned(), #content_schema);), - quote!(required.insert(#content_name.to_owned());), + quote!(#content_name: (#content_schema),), + quote!(#content_name,), ) }) .unwrap_or_default(); let name = variant.name(); - let tag_schema = schema_object(quote! { - instance_type: Some(schemars::schema::InstanceType::String.into()), - enum_values: Some(vec![#name.into()]), - }); + let tag_schema = quote! { + schemars::json_schema!({ + "type": "string", + "enum": [#name], + }) + }; let set_additional_properties = if deny_unknown_fields { quote! { - additional_properties: Some(Box::new(false.into())), + "additionalProperties": false, } } else { TokenStream::new() }; - let mut outer_schema = schema_object(quote! { - instance_type: Some(schemars::schema::InstanceType::Object.into()), - object: Some(Box::new(schemars::schema::ObjectValidation { - properties: { - let mut props = schemars::Map::new(); - props.insert(#tag_name.to_owned(), #tag_schema); + let mut outer_schema = quote! { + schemars::json_schema!({ + "type": "object", + "properties": { + #tag_name: (#tag_schema), #add_content_to_props - props }, - required: { - let mut required = schemars::Set::new(); - required.insert(#tag_name.to_owned()); + "required": [ + #tag_name, #add_content_to_required - required - }, + ], // As we're creating a "wrapper" object, we can honor the // disposition of deny_unknown_fields. #set_additional_properties - ..Default::default() - })), - }); + }) + }; variant .attrs @@ -333,21 +348,19 @@ fn expr_for_adjacent_tagged_enum<'a>( /// Callers must determine if all subschemas are mutually exclusive. This can /// be done for most tagging regimes by checking that all tag names are unique. fn variant_subschemas(unique: bool, schemas: Vec) -> TokenStream { - if unique { - schema_object(quote! { - subschemas: Some(Box::new(schemars::schema::SubschemaValidation { - one_of: Some(vec![#(#schemas),*]), - ..Default::default() - })), - }) - } else { - schema_object(quote! { - subschemas: Some(Box::new(schemars::schema::SubschemaValidation { - any_of: Some(vec![#(#schemas),*]), - ..Default::default() - })), - }) - } + let keyword = if unique { "oneOf" } else { "anyOf" }; + quote!({ + let mut map = schemars::_serde_json::Map::new(); + map.insert( + #keyword.to_owned(), + schemars::_serde_json::Value::Array({ + let mut enum_values = Vec::new(); + #(enum_values.push(#schemas.to_value());)* + enum_values + }), + ); + schemars::Schema::from(map) + }) } fn expr_for_untagged_enum_variant(variant: &Variant, deny_unknown_fields: bool) -> TokenStream { @@ -412,16 +425,11 @@ fn expr_for_tuple_struct(fields: &[Field]) -> TokenStream { let len = fields.len() as u32; quote! { - schemars::schema::Schema::Object( - schemars::schema::SchemaObject { - instance_type: Some(schemars::schema::InstanceType::Array.into()), - array: Some(Box::new(schemars::schema::ArrayValidation { - items: Some(vec![#(#fields),*].into()), - max_items: Some(#len), - min_items: Some(#len), - ..Default::default() - })), - ..Default::default() + schemars::json_schema!({ + "type": "array", + "items": [#((#fields)),*], + "minItems": #len, + "maxItems": #len, }) } } @@ -477,7 +485,7 @@ fn expr_for_struct( quote! { { #type_def - schemars::_private::insert_object_property::<#ty>(object_validation, #name, #has_default, #required, #schema_expr); + schemars::_private::insert_object_property::<#ty>(&mut schema, #name, #has_default, #required, #schema_expr); } } }) @@ -502,7 +510,7 @@ fn expr_for_struct( let set_additional_properties = if deny_unknown_fields { quote! { - object_validation.additional_properties = Some(Box::new(false.into())); + "additionalProperties": false, } } else { TokenStream::new() @@ -510,15 +518,12 @@ fn expr_for_struct( quote! { { #set_container_default - let mut schema_object = schemars::schema::SchemaObject { - instance_type: Some(schemars::schema::InstanceType::Object.into()), - ..Default::default() - }; - let object_validation = schema_object.object(); - #set_additional_properties + let mut schema = schemars::json_schema!({ + "type": "object", + #set_additional_properties + }); #(#properties)* - schemars::schema::Schema::Object(schema_object) - #(.flatten(#flattens))* + schema #(.flatten(#flattens))* } } } @@ -578,16 +583,6 @@ fn field_default_expr(field: &Field, container_has_default: bool) -> Option TokenStream { - quote! { - schemars::schema::Schema::Object( - schemars::schema::SchemaObject { - #properties - ..Default::default() - }) - } -} - fn prepend_type_def(type_def: Option, schema_expr: &mut TokenStream) { if let Some(type_def) = type_def { *schema_expr = quote! { From fdb5f53b2b2ed77c5fc04c1ae7a516246da41b32 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Mon, 13 May 2024 10:52:42 +0100 Subject: [PATCH 02/31] Simplify `flatten` No longer use it for internally-tagged enums. Instead, use a private helper that adds the tag property. --- schemars/src/_private.rs | 40 ++++++++++++++++++ schemars/src/flatten.rs | 42 +------------------ .../tests/expected/enum-internal-duf.json | 5 +-- schemars/tests/expected/enum-internal.json | 8 ++-- .../expected/schema_with-enum-internal.json | 10 +---- schemars_derive/src/schema_exprs.rs | 33 +++++++-------- 6 files changed, 62 insertions(+), 76 deletions(-) diff --git a/schemars/src/_private.rs b/schemars/src/_private.rs index 26c23a8..1d08260 100644 --- a/schemars/src/_private.rs +++ b/schemars/src/_private.rs @@ -2,6 +2,7 @@ use crate::gen::SchemaGenerator; use crate::JsonSchema; use crate::Schema; use serde::Serialize; +use serde_json::json; use serde_json::Map; use serde_json::Value; @@ -73,6 +74,45 @@ pub fn new_externally_tagged_enum(variant: &str, sub_schema: Schema) -> Schema { }) } +pub fn apply_internal_enum_tag( + schema: &mut Schema, + tag_name: &str, + variant: &str, + deny_unknown_fields: bool, +) { + let obj = schema.ensure_object(); + let is_unit = obj.get("type").is_some_and(|t| t.as_str() == Some("null")); + + obj.insert("type".to_owned(), "object".into()); + + if let Some(properties) = obj + .entry("properties") + .or_insert(Value::Object(Map::new())) + .as_object_mut() + { + properties.insert( + tag_name.to_string(), + json!({ + "type": "string", + // TODO switch from single-valued "enum" to "const" + "enum": [variant] + }), + ); + } + + if let Some(required) = obj + .entry("required") + .or_insert(Value::Array(Vec::new())) + .as_array_mut() + { + required.insert(0, tag_name.into()); + } + + if deny_unknown_fields && is_unit { + obj.entry("additionalProperties").or_insert(false.into()); + } +} + /// Create a schema for an internally tagged enum pub fn new_internally_tagged_enum( tag_name: &str, diff --git a/schemars/src/flatten.rs b/schemars/src/flatten.rs index fc66b74..69b0361 100644 --- a/schemars/src/flatten.rs +++ b/schemars/src/flatten.rs @@ -9,33 +9,9 @@ impl Schema { /// It should not be considered part of the public API. #[doc(hidden)] pub fn flatten(mut self, other: Self) -> Schema { - // This special null-type-schema handling is here for backward-compatibility, but needs reviewing. - // I think it's only needed to make internally-tagged enum unit variants behave correctly, but that - // should be handled entirely within schemars_derive. - if other - .as_object() - .and_then(|o| o.get("type")) - .and_then(|t| t.as_str()) - == Some("null") - { - return self; - } - - if let Value::Object(mut obj2) = other.to_value() { + if let Value::Object(obj2) = other.to_value() { let obj1 = self.ensure_object(); - let ap2 = obj2.remove("additionalProperties"); - if let Entry::Occupied(mut ap1) = obj1.entry("additionalProperties") { - match ap2 { - Some(ap2) => { - flatten_additional_properties(ap1.get_mut(), ap2); - } - None => { - ap1.remove(); - } - } - } - for (key, value2) in obj2 { match obj1.entry(key) { Entry::Vacant(vacant) => { @@ -93,19 +69,3 @@ impl Schema { self } } - -// TODO validate behaviour when flattening a normal struct into a struct with deny_unknown_fields -fn flatten_additional_properties(v1: &mut Value, v2: Value) { - match (v1, v2) { - (v1, Value::Bool(true)) => { - *v1 = Value::Bool(true); - } - (v1 @ Value::Bool(false), v2) => { - *v1 = v2; - } - (Value::Object(o1), Value::Object(o2)) => { - o1.extend(o2); - } - _ => {} - } -} diff --git a/schemars/tests/expected/enum-internal-duf.json b/schemars/tests/expected/enum-internal-duf.json index 501f13b..7a9bcd7 100644 --- a/schemars/tests/expected/enum-internal-duf.json +++ b/schemars/tests/expected/enum-internal-duf.json @@ -112,10 +112,7 @@ "additionalProperties": false }, { - "type": [ - "object", - "integer" - ], + "type": "object", "format": "int32", "required": [ "typeProperty" diff --git a/schemars/tests/expected/enum-internal.json b/schemars/tests/expected/enum-internal.json index 37739b0..115dbf3 100644 --- a/schemars/tests/expected/enum-internal.json +++ b/schemars/tests/expected/enum-internal.json @@ -28,6 +28,9 @@ "StringMap" ] } + }, + "additionalProperties": { + "type": "string" } }, { @@ -105,10 +108,7 @@ } }, { - "type": [ - "object", - "integer" - ], + "type": "object", "format": "int32", "required": [ "typeProperty" diff --git a/schemars/tests/expected/schema_with-enum-internal.json b/schemars/tests/expected/schema_with-enum-internal.json index 75b28dc..c4a0cc1 100644 --- a/schemars/tests/expected/schema_with-enum-internal.json +++ b/schemars/tests/expected/schema_with-enum-internal.json @@ -21,10 +21,7 @@ } }, { - "type": [ - "object", - "boolean" - ], + "type": "object", "required": [ "typeProperty" ], @@ -38,10 +35,7 @@ } }, { - "type": [ - "object", - "boolean" - ], + "type": "object", "required": [ "typeProperty" ], diff --git a/schemars_derive/src/schema_exprs.rs b/schemars_derive/src/schema_exprs.rs index 585184c..f22831e 100644 --- a/schemars_derive/src/schema_exprs.rs +++ b/schemars_derive/src/schema_exprs.rs @@ -231,19 +231,14 @@ fn expr_for_internal_tagged_enum<'a>( let name = variant.name(); - let mut tag_schema = quote! { - schemars::_private::new_internally_tagged_enum(#tag_name, #name, #deny_unknown_fields) - }; - - variant.attrs.as_metadata().apply_to_schema(&mut tag_schema); - - if let Some(variant_schema) = - expr_for_untagged_enum_variant_for_flatten(variant, deny_unknown_fields) - { - tag_schema.extend(quote!(.flatten(#variant_schema))) - } - - tag_schema + let mut schema_expr = expr_for_internal_tagged_enum_variant(variant, deny_unknown_fields); + variant.attrs.as_metadata().apply_to_schema(&mut schema_expr); + + quote!({ + let mut schema = #schema_expr; + schemars::_private::apply_internal_enum_tag(&mut schema, #tag_name, #name, #deny_unknown_fields); + schema + }) }) .collect(); @@ -383,10 +378,10 @@ fn expr_for_untagged_enum_variant(variant: &Variant, deny_unknown_fields: bool) } } -fn expr_for_untagged_enum_variant_for_flatten( +fn expr_for_internal_tagged_enum_variant( variant: &Variant, deny_unknown_fields: bool, -) -> Option { +) -> TokenStream { if let Some(with_attr) = &variant.attrs.with { let (ty, type_def) = type_for_schema(with_attr); let gen = quote!(gen); @@ -395,15 +390,15 @@ fn expr_for_untagged_enum_variant_for_flatten( }; prepend_type_def(type_def, &mut schema_expr); - return Some(schema_expr); + return schema_expr; } - Some(match variant.style { - Style::Unit => return None, + match variant.style { + Style::Unit => expr_for_unit_struct(), Style::Newtype => expr_for_field(&variant.fields[0], false), Style::Tuple => expr_for_tuple_struct(&variant.fields), Style::Struct => expr_for_struct(&variant.fields, &SerdeDefault::None, deny_unknown_fields), - }) + } } fn expr_for_unit_struct() -> TokenStream { From 724f8e550d5432432035b935be226bcab2afe3fc Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Mon, 13 May 2024 10:56:55 +0100 Subject: [PATCH 03/31] Remove usage of `is_some_and` (not supported in rustc 1.60) --- schemars/src/_private.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemars/src/_private.rs b/schemars/src/_private.rs index 1d08260..f3160e9 100644 --- a/schemars/src/_private.rs +++ b/schemars/src/_private.rs @@ -81,7 +81,7 @@ pub fn apply_internal_enum_tag( deny_unknown_fields: bool, ) { let obj = schema.ensure_object(); - let is_unit = obj.get("type").is_some_and(|t| t.as_str() == Some("null")); + let is_unit = obj.get("type").and_then(|t| t.as_str()) == Some("null"); obj.insert("type".to_owned(), "object".into()); From ca625573610d99fab78f96ff4ad738a9e8f6a7a9 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Mon, 13 May 2024 17:58:09 +0100 Subject: [PATCH 04/31] Remove test schemas for now-removed `RootSchema` --- schemars/tests/expected/schema-2019_09.json | 758 ------------------ schemars/tests/expected/schema-openapi3.json | 636 ---------------- schemars/tests/expected/schema.json | 762 ------------------- 3 files changed, 2156 deletions(-) delete mode 100644 schemars/tests/expected/schema-2019_09.json delete mode 100644 schemars/tests/expected/schema-openapi3.json delete mode 100644 schemars/tests/expected/schema.json diff --git a/schemars/tests/expected/schema-2019_09.json b/schemars/tests/expected/schema-2019_09.json deleted file mode 100644 index cf11d35..0000000 --- a/schemars/tests/expected/schema-2019_09.json +++ /dev/null @@ -1,758 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2019-09/schema", - "title": "RootSchema", - "description": "The root object of a JSON Schema document.", - "type": "object", - "properties": { - "$schema": { - "description": "The `$schema` keyword.\n\nSee [JSON Schema 8.1.1. The \"$schema\" Keyword](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.1.1).", - "type": [ - "string", - "null" - ] - }, - "definitions": { - "description": "The `definitions` keyword.\n\nIn JSON Schema draft 2019-09 this was replaced by $defs, but in Schemars this is still serialized as `definitions` for backward-compatibility.\n\nSee [JSON Schema 8.2.5. Schema Re-Use With \"$defs\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.5), and [JSON Schema (draft 07) 9. Schema Re-Use With \"definitions\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-9).", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Schema" - } - }, - "type": { - "description": "The `type` keyword.\n\nSee [JSON Schema Validation 6.1.1. \"type\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.1) and [JSON Schema 4.2.1. Instance Data Model](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-4.2.1).", - "anyOf": [ - { - "$ref": "#/definitions/SingleOrVec_for_InstanceType" - }, - { - "type": "null" - } - ] - }, - "format": { - "description": "The `format` keyword.\n\nSee [JSON Schema Validation 7. A Vocabulary for Semantic Content With \"format\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-7).", - "type": [ - "string", - "null" - ] - }, - "enum": { - "description": "The `enum` keyword.\n\nSee [JSON Schema Validation 6.1.2. \"enum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.2)", - "type": [ - "array", - "null" - ], - "items": true - }, - "const": { - "description": "The `const` keyword.\n\nSee [JSON Schema Validation 6.1.3. \"const\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.3)" - }, - "$ref": { - "description": "The `$ref` keyword.\n\nSee [JSON Schema 8.2.4.1. Direct References with \"$ref\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.4.1).", - "type": [ - "string", - "null" - ] - }, - "$id": { - "description": "The `$id` keyword.\n\nSee [JSON Schema 8.2.2. The \"$id\" Keyword](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.2).", - "type": [ - "string", - "null" - ] - }, - "title": { - "description": "The `title` keyword.\n\nSee [JSON Schema Validation 9.1. \"title\" and \"description\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).", - "type": [ - "string", - "null" - ] - }, - "description": { - "description": "The `description` keyword.\n\nSee [JSON Schema Validation 9.1. \"title\" and \"description\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).", - "type": [ - "string", - "null" - ] - }, - "default": { - "description": "The `default` keyword.\n\nSee [JSON Schema Validation 9.2. \"default\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.2)." - }, - "deprecated": { - "description": "The `deprecated` keyword.\n\nSee [JSON Schema Validation 9.3. \"deprecated\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.3).", - "type": "boolean" - }, - "readOnly": { - "description": "The `readOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", - "type": "boolean" - }, - "writeOnly": { - "description": "The `writeOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", - "type": "boolean" - }, - "examples": { - "description": "The `examples` keyword.\n\nSee [JSON Schema Validation 9.5. \"examples\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.5).", - "type": "array", - "items": true - }, - "allOf": { - "description": "The `allOf` keyword.\n\nSee [JSON Schema 9.2.1.1. \"allOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.1).", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Schema" - } - }, - "anyOf": { - "description": "The `anyOf` keyword.\n\nSee [JSON Schema 9.2.1.2. \"anyOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.2).", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Schema" - } - }, - "oneOf": { - "description": "The `oneOf` keyword.\n\nSee [JSON Schema 9.2.1.3. \"oneOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.3).", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Schema" - } - }, - "not": { - "description": "The `not` keyword.\n\nSee [JSON Schema 9.2.1.4. \"not\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.4).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "if": { - "description": "The `if` keyword.\n\nSee [JSON Schema 9.2.2.1. \"if\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.1).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "then": { - "description": "The `then` keyword.\n\nSee [JSON Schema 9.2.2.2. \"then\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.2).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "else": { - "description": "The `else` keyword.\n\nSee [JSON Schema 9.2.2.3. \"else\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.3).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "multipleOf": { - "description": "The `multipleOf` keyword.\n\nSee [JSON Schema Validation 6.2.1. \"multipleOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.1).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "maximum": { - "description": "The `maximum` keyword.\n\nSee [JSON Schema Validation 6.2.2. \"maximum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.2).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "exclusiveMaximum": { - "description": "The `exclusiveMaximum` keyword.\n\nSee [JSON Schema Validation 6.2.3. \"exclusiveMaximum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.3).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "minimum": { - "description": "The `minimum` keyword.\n\nSee [JSON Schema Validation 6.2.4. \"minimum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.4).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "exclusiveMinimum": { - "description": "The `exclusiveMinimum` keyword.\n\nSee [JSON Schema Validation 6.2.5. \"exclusiveMinimum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.5).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "maxLength": { - "description": "The `maxLength` keyword.\n\nSee [JSON Schema Validation 6.3.1. \"maxLength\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.1).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "minLength": { - "description": "The `minLength` keyword.\n\nSee [JSON Schema Validation 6.3.2. \"minLength\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.2).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "pattern": { - "description": "The `pattern` keyword.\n\nSee [JSON Schema Validation 6.3.3. \"pattern\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.3).", - "type": [ - "string", - "null" - ] - }, - "items": { - "description": "The `items` keyword.\n\nSee [JSON Schema 9.3.1.1. \"items\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.1).", - "anyOf": [ - { - "$ref": "#/definitions/SingleOrVec_for_Schema" - }, - { - "type": "null" - } - ] - }, - "additionalItems": { - "description": "The `additionalItems` keyword.\n\nSee [JSON Schema 9.3.1.2. \"additionalItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.2).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "maxItems": { - "description": "The `maxItems` keyword.\n\nSee [JSON Schema Validation 6.4.1. \"maxItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.1).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "minItems": { - "description": "The `minItems` keyword.\n\nSee [JSON Schema Validation 6.4.2. \"minItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.2).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "uniqueItems": { - "description": "The `uniqueItems` keyword.\n\nSee [JSON Schema Validation 6.4.3. \"uniqueItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.3).", - "type": [ - "boolean", - "null" - ] - }, - "contains": { - "description": "The `contains` keyword.\n\nSee [JSON Schema 9.3.1.4. \"contains\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.4).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "maxProperties": { - "description": "The `maxProperties` keyword.\n\nSee [JSON Schema Validation 6.5.1. \"maxProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.1).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "minProperties": { - "description": "The `minProperties` keyword.\n\nSee [JSON Schema Validation 6.5.2. \"minProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.2).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "required": { - "description": "The `required` keyword.\n\nSee [JSON Schema Validation 6.5.3. \"required\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.3).", - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "properties": { - "description": "The `properties` keyword.\n\nSee [JSON Schema 9.3.2.1. \"properties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.1).", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Schema" - } - }, - "patternProperties": { - "description": "The `patternProperties` keyword.\n\nSee [JSON Schema 9.3.2.2. \"patternProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.2).", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Schema" - } - }, - "additionalProperties": { - "description": "The `additionalProperties` keyword.\n\nSee [JSON Schema 9.3.2.3. \"additionalProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.3).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "propertyNames": { - "description": "The `propertyNames` keyword.\n\nSee [JSON Schema 9.3.2.5. \"propertyNames\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.5).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": true, - "definitions": { - "Schema": { - "description": "A JSON Schema.", - "anyOf": [ - { - "description": "A trivial boolean JSON Schema.\n\nThe schema `true` matches everything (always passes validation), whereas the schema `false` matches nothing (always fails validation).", - "type": "boolean" - }, - { - "description": "A JSON Schema object.", - "$ref": "#/definitions/SchemaObject" - } - ] - }, - "SchemaObject": { - "description": "A JSON Schema object.", - "type": "object", - "properties": { - "type": { - "description": "The `type` keyword.\n\nSee [JSON Schema Validation 6.1.1. \"type\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.1) and [JSON Schema 4.2.1. Instance Data Model](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-4.2.1).", - "anyOf": [ - { - "$ref": "#/definitions/SingleOrVec_for_InstanceType" - }, - { - "type": "null" - } - ] - }, - "format": { - "description": "The `format` keyword.\n\nSee [JSON Schema Validation 7. A Vocabulary for Semantic Content With \"format\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-7).", - "type": [ - "string", - "null" - ] - }, - "enum": { - "description": "The `enum` keyword.\n\nSee [JSON Schema Validation 6.1.2. \"enum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.2)", - "type": [ - "array", - "null" - ], - "items": true - }, - "const": { - "description": "The `const` keyword.\n\nSee [JSON Schema Validation 6.1.3. \"const\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.3)" - }, - "$ref": { - "description": "The `$ref` keyword.\n\nSee [JSON Schema 8.2.4.1. Direct References with \"$ref\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.4.1).", - "type": [ - "string", - "null" - ] - }, - "$id": { - "description": "The `$id` keyword.\n\nSee [JSON Schema 8.2.2. The \"$id\" Keyword](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.2).", - "type": [ - "string", - "null" - ] - }, - "title": { - "description": "The `title` keyword.\n\nSee [JSON Schema Validation 9.1. \"title\" and \"description\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).", - "type": [ - "string", - "null" - ] - }, - "description": { - "description": "The `description` keyword.\n\nSee [JSON Schema Validation 9.1. \"title\" and \"description\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).", - "type": [ - "string", - "null" - ] - }, - "default": { - "description": "The `default` keyword.\n\nSee [JSON Schema Validation 9.2. \"default\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.2)." - }, - "deprecated": { - "description": "The `deprecated` keyword.\n\nSee [JSON Schema Validation 9.3. \"deprecated\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.3).", - "type": "boolean" - }, - "readOnly": { - "description": "The `readOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", - "type": "boolean" - }, - "writeOnly": { - "description": "The `writeOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", - "type": "boolean" - }, - "examples": { - "description": "The `examples` keyword.\n\nSee [JSON Schema Validation 9.5. \"examples\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.5).", - "type": "array", - "items": true - }, - "allOf": { - "description": "The `allOf` keyword.\n\nSee [JSON Schema 9.2.1.1. \"allOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.1).", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Schema" - } - }, - "anyOf": { - "description": "The `anyOf` keyword.\n\nSee [JSON Schema 9.2.1.2. \"anyOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.2).", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Schema" - } - }, - "oneOf": { - "description": "The `oneOf` keyword.\n\nSee [JSON Schema 9.2.1.3. \"oneOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.3).", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Schema" - } - }, - "not": { - "description": "The `not` keyword.\n\nSee [JSON Schema 9.2.1.4. \"not\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.4).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "if": { - "description": "The `if` keyword.\n\nSee [JSON Schema 9.2.2.1. \"if\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.1).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "then": { - "description": "The `then` keyword.\n\nSee [JSON Schema 9.2.2.2. \"then\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.2).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "else": { - "description": "The `else` keyword.\n\nSee [JSON Schema 9.2.2.3. \"else\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.3).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "multipleOf": { - "description": "The `multipleOf` keyword.\n\nSee [JSON Schema Validation 6.2.1. \"multipleOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.1).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "maximum": { - "description": "The `maximum` keyword.\n\nSee [JSON Schema Validation 6.2.2. \"maximum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.2).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "exclusiveMaximum": { - "description": "The `exclusiveMaximum` keyword.\n\nSee [JSON Schema Validation 6.2.3. \"exclusiveMaximum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.3).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "minimum": { - "description": "The `minimum` keyword.\n\nSee [JSON Schema Validation 6.2.4. \"minimum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.4).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "exclusiveMinimum": { - "description": "The `exclusiveMinimum` keyword.\n\nSee [JSON Schema Validation 6.2.5. \"exclusiveMinimum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.5).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "maxLength": { - "description": "The `maxLength` keyword.\n\nSee [JSON Schema Validation 6.3.1. \"maxLength\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.1).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "minLength": { - "description": "The `minLength` keyword.\n\nSee [JSON Schema Validation 6.3.2. \"minLength\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.2).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "pattern": { - "description": "The `pattern` keyword.\n\nSee [JSON Schema Validation 6.3.3. \"pattern\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.3).", - "type": [ - "string", - "null" - ] - }, - "items": { - "description": "The `items` keyword.\n\nSee [JSON Schema 9.3.1.1. \"items\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.1).", - "anyOf": [ - { - "$ref": "#/definitions/SingleOrVec_for_Schema" - }, - { - "type": "null" - } - ] - }, - "additionalItems": { - "description": "The `additionalItems` keyword.\n\nSee [JSON Schema 9.3.1.2. \"additionalItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.2).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "maxItems": { - "description": "The `maxItems` keyword.\n\nSee [JSON Schema Validation 6.4.1. \"maxItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.1).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "minItems": { - "description": "The `minItems` keyword.\n\nSee [JSON Schema Validation 6.4.2. \"minItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.2).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "uniqueItems": { - "description": "The `uniqueItems` keyword.\n\nSee [JSON Schema Validation 6.4.3. \"uniqueItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.3).", - "type": [ - "boolean", - "null" - ] - }, - "contains": { - "description": "The `contains` keyword.\n\nSee [JSON Schema 9.3.1.4. \"contains\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.4).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "maxProperties": { - "description": "The `maxProperties` keyword.\n\nSee [JSON Schema Validation 6.5.1. \"maxProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.1).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "minProperties": { - "description": "The `minProperties` keyword.\n\nSee [JSON Schema Validation 6.5.2. \"minProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.2).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "required": { - "description": "The `required` keyword.\n\nSee [JSON Schema Validation 6.5.3. \"required\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.3).", - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "properties": { - "description": "The `properties` keyword.\n\nSee [JSON Schema 9.3.2.1. \"properties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.1).", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Schema" - } - }, - "patternProperties": { - "description": "The `patternProperties` keyword.\n\nSee [JSON Schema 9.3.2.2. \"patternProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.2).", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Schema" - } - }, - "additionalProperties": { - "description": "The `additionalProperties` keyword.\n\nSee [JSON Schema 9.3.2.3. \"additionalProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.3).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "propertyNames": { - "description": "The `propertyNames` keyword.\n\nSee [JSON Schema 9.3.2.5. \"propertyNames\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.5).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": true - }, - "SingleOrVec_for_InstanceType": { - "description": "A type which can be serialized as a single item, or multiple items.\n\nIn some contexts, a `Single` may be semantically distinct from a `Vec` containing only item.", - "anyOf": [ - { - "$ref": "#/definitions/InstanceType" - }, - { - "type": "array", - "items": { - "$ref": "#/definitions/InstanceType" - } - } - ] - }, - "InstanceType": { - "description": "The possible types of values in JSON Schema documents.\n\nSee [JSON Schema 4.2.1. Instance Data Model](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-4.2.1).", - "type": "string", - "enum": [ - "null", - "boolean", - "object", - "array", - "number", - "string", - "integer" - ] - }, - "SingleOrVec_for_Schema": { - "description": "A type which can be serialized as a single item, or multiple items.\n\nIn some contexts, a `Single` may be semantically distinct from a `Vec` containing only item.", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "array", - "items": { - "$ref": "#/definitions/Schema" - } - } - ] - } - } -} \ No newline at end of file diff --git a/schemars/tests/expected/schema-openapi3.json b/schemars/tests/expected/schema-openapi3.json deleted file mode 100644 index 0548828..0000000 --- a/schemars/tests/expected/schema-openapi3.json +++ /dev/null @@ -1,636 +0,0 @@ -{ - "$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema", - "title": "RootSchema", - "description": "The root object of a JSON Schema document.", - "type": "object", - "properties": { - "$schema": { - "description": "The `$schema` keyword.\n\nSee [JSON Schema 8.1.1. The \"$schema\" Keyword](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.1.1).", - "type": "string", - "nullable": true - }, - "definitions": { - "description": "The `definitions` keyword.\n\nIn JSON Schema draft 2019-09 this was replaced by $defs, but in Schemars this is still serialized as `definitions` for backward-compatibility.\n\nSee [JSON Schema 8.2.5. Schema Re-Use With \"$defs\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.5), and [JSON Schema (draft 07) 9. Schema Re-Use With \"definitions\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-9).", - "type": "object", - "additionalProperties": { - "$ref": "#/components/schemas/Schema" - } - }, - "type": { - "description": "The `type` keyword.\n\nSee [JSON Schema Validation 6.1.1. \"type\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.1) and [JSON Schema 4.2.1. Instance Data Model](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-4.2.1).", - "allOf": [ - { - "$ref": "#/components/schemas/SingleOrVec_for_InstanceType" - } - ], - "nullable": true - }, - "format": { - "description": "The `format` keyword.\n\nSee [JSON Schema Validation 7. A Vocabulary for Semantic Content With \"format\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-7).", - "type": "string", - "nullable": true - }, - "enum": { - "description": "The `enum` keyword.\n\nSee [JSON Schema Validation 6.1.2. \"enum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.2)", - "type": "array", - "items": {}, - "nullable": true - }, - "const": { - "description": "The `const` keyword.\n\nSee [JSON Schema Validation 6.1.3. \"const\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.3)", - "nullable": true - }, - "$ref": { - "description": "The `$ref` keyword.\n\nSee [JSON Schema 8.2.4.1. Direct References with \"$ref\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.4.1).", - "type": "string", - "nullable": true - }, - "$id": { - "description": "The `$id` keyword.\n\nSee [JSON Schema 8.2.2. The \"$id\" Keyword](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.2).", - "type": "string", - "nullable": true - }, - "title": { - "description": "The `title` keyword.\n\nSee [JSON Schema Validation 9.1. \"title\" and \"description\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).", - "type": "string", - "nullable": true - }, - "description": { - "description": "The `description` keyword.\n\nSee [JSON Schema Validation 9.1. \"title\" and \"description\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).", - "type": "string", - "nullable": true - }, - "default": { - "description": "The `default` keyword.\n\nSee [JSON Schema Validation 9.2. \"default\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.2).", - "nullable": true - }, - "deprecated": { - "description": "The `deprecated` keyword.\n\nSee [JSON Schema Validation 9.3. \"deprecated\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.3).", - "type": "boolean" - }, - "readOnly": { - "description": "The `readOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", - "type": "boolean" - }, - "writeOnly": { - "description": "The `writeOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", - "type": "boolean" - }, - "examples": { - "description": "The `examples` keyword.\n\nSee [JSON Schema Validation 9.5. \"examples\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.5).", - "type": "array", - "items": {} - }, - "allOf": { - "description": "The `allOf` keyword.\n\nSee [JSON Schema 9.2.1.1. \"allOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.1).", - "type": "array", - "items": { - "$ref": "#/components/schemas/Schema" - }, - "nullable": true - }, - "anyOf": { - "description": "The `anyOf` keyword.\n\nSee [JSON Schema 9.2.1.2. \"anyOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.2).", - "type": "array", - "items": { - "$ref": "#/components/schemas/Schema" - }, - "nullable": true - }, - "oneOf": { - "description": "The `oneOf` keyword.\n\nSee [JSON Schema 9.2.1.3. \"oneOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.3).", - "type": "array", - "items": { - "$ref": "#/components/schemas/Schema" - }, - "nullable": true - }, - "not": { - "description": "The `not` keyword.\n\nSee [JSON Schema 9.2.1.4. \"not\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.4).", - "allOf": [ - { - "$ref": "#/components/schemas/Schema" - } - ], - "nullable": true - }, - "if": { - "description": "The `if` keyword.\n\nSee [JSON Schema 9.2.2.1. \"if\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.1).", - "allOf": [ - { - "$ref": "#/components/schemas/Schema" - } - ], - "nullable": true - }, - "then": { - "description": "The `then` keyword.\n\nSee [JSON Schema 9.2.2.2. \"then\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.2).", - "allOf": [ - { - "$ref": "#/components/schemas/Schema" - } - ], - "nullable": true - }, - "else": { - "description": "The `else` keyword.\n\nSee [JSON Schema 9.2.2.3. \"else\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.3).", - "allOf": [ - { - "$ref": "#/components/schemas/Schema" - } - ], - "nullable": true - }, - "multipleOf": { - "description": "The `multipleOf` keyword.\n\nSee [JSON Schema Validation 6.2.1. \"multipleOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.1).", - "type": "number", - "format": "double", - "nullable": true - }, - "maximum": { - "description": "The `maximum` keyword.\n\nSee [JSON Schema Validation 6.2.2. \"maximum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.2).", - "type": "number", - "format": "double", - "nullable": true - }, - "exclusiveMaximum": { - "description": "The `exclusiveMaximum` keyword.\n\nSee [JSON Schema Validation 6.2.3. \"exclusiveMaximum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.3).", - "type": "number", - "format": "double", - "nullable": true - }, - "minimum": { - "description": "The `minimum` keyword.\n\nSee [JSON Schema Validation 6.2.4. \"minimum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.4).", - "type": "number", - "format": "double", - "nullable": true - }, - "exclusiveMinimum": { - "description": "The `exclusiveMinimum` keyword.\n\nSee [JSON Schema Validation 6.2.5. \"exclusiveMinimum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.5).", - "type": "number", - "format": "double", - "nullable": true - }, - "maxLength": { - "description": "The `maxLength` keyword.\n\nSee [JSON Schema Validation 6.3.1. \"maxLength\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.1).", - "type": "integer", - "format": "uint32", - "minimum": 0.0, - "nullable": true - }, - "minLength": { - "description": "The `minLength` keyword.\n\nSee [JSON Schema Validation 6.3.2. \"minLength\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.2).", - "type": "integer", - "format": "uint32", - "minimum": 0.0, - "nullable": true - }, - "pattern": { - "description": "The `pattern` keyword.\n\nSee [JSON Schema Validation 6.3.3. \"pattern\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.3).", - "type": "string", - "nullable": true - }, - "items": { - "description": "The `items` keyword.\n\nSee [JSON Schema 9.3.1.1. \"items\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.1).", - "allOf": [ - { - "$ref": "#/components/schemas/SingleOrVec_for_Schema" - } - ], - "nullable": true - }, - "additionalItems": { - "description": "The `additionalItems` keyword.\n\nSee [JSON Schema 9.3.1.2. \"additionalItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.2).", - "allOf": [ - { - "$ref": "#/components/schemas/Schema" - } - ], - "nullable": true - }, - "maxItems": { - "description": "The `maxItems` keyword.\n\nSee [JSON Schema Validation 6.4.1. \"maxItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.1).", - "type": "integer", - "format": "uint32", - "minimum": 0.0, - "nullable": true - }, - "minItems": { - "description": "The `minItems` keyword.\n\nSee [JSON Schema Validation 6.4.2. \"minItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.2).", - "type": "integer", - "format": "uint32", - "minimum": 0.0, - "nullable": true - }, - "uniqueItems": { - "description": "The `uniqueItems` keyword.\n\nSee [JSON Schema Validation 6.4.3. \"uniqueItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.3).", - "type": "boolean", - "nullable": true - }, - "contains": { - "description": "The `contains` keyword.\n\nSee [JSON Schema 9.3.1.4. \"contains\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.4).", - "allOf": [ - { - "$ref": "#/components/schemas/Schema" - } - ], - "nullable": true - }, - "maxProperties": { - "description": "The `maxProperties` keyword.\n\nSee [JSON Schema Validation 6.5.1. \"maxProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.1).", - "type": "integer", - "format": "uint32", - "minimum": 0.0, - "nullable": true - }, - "minProperties": { - "description": "The `minProperties` keyword.\n\nSee [JSON Schema Validation 6.5.2. \"minProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.2).", - "type": "integer", - "format": "uint32", - "minimum": 0.0, - "nullable": true - }, - "required": { - "description": "The `required` keyword.\n\nSee [JSON Schema Validation 6.5.3. \"required\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.3).", - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "properties": { - "description": "The `properties` keyword.\n\nSee [JSON Schema 9.3.2.1. \"properties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.1).", - "type": "object", - "additionalProperties": { - "$ref": "#/components/schemas/Schema" - } - }, - "patternProperties": { - "description": "The `patternProperties` keyword.\n\nSee [JSON Schema 9.3.2.2. \"patternProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.2).", - "type": "object", - "additionalProperties": { - "$ref": "#/components/schemas/Schema" - } - }, - "additionalProperties": { - "description": "The `additionalProperties` keyword.\n\nSee [JSON Schema 9.3.2.3. \"additionalProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.3).", - "allOf": [ - { - "$ref": "#/components/schemas/Schema" - } - ], - "nullable": true - }, - "propertyNames": { - "description": "The `propertyNames` keyword.\n\nSee [JSON Schema 9.3.2.5. \"propertyNames\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.5).", - "allOf": [ - { - "$ref": "#/components/schemas/Schema" - } - ], - "nullable": true - } - }, - "additionalProperties": true, - "definitions": { - "Schema": { - "description": "A JSON Schema.", - "anyOf": [ - { - "description": "A trivial boolean JSON Schema.\n\nThe schema `true` matches everything (always passes validation), whereas the schema `false` matches nothing (always fails validation).", - "type": "boolean" - }, - { - "description": "A JSON Schema object.", - "allOf": [ - { - "$ref": "#/components/schemas/SchemaObject" - } - ] - } - ] - }, - "SchemaObject": { - "description": "A JSON Schema object.", - "type": "object", - "properties": { - "type": { - "description": "The `type` keyword.\n\nSee [JSON Schema Validation 6.1.1. \"type\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.1) and [JSON Schema 4.2.1. Instance Data Model](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-4.2.1).", - "allOf": [ - { - "$ref": "#/components/schemas/SingleOrVec_for_InstanceType" - } - ], - "nullable": true - }, - "format": { - "description": "The `format` keyword.\n\nSee [JSON Schema Validation 7. A Vocabulary for Semantic Content With \"format\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-7).", - "type": "string", - "nullable": true - }, - "enum": { - "description": "The `enum` keyword.\n\nSee [JSON Schema Validation 6.1.2. \"enum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.2)", - "type": "array", - "items": {}, - "nullable": true - }, - "const": { - "description": "The `const` keyword.\n\nSee [JSON Schema Validation 6.1.3. \"const\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.3)", - "nullable": true - }, - "$ref": { - "description": "The `$ref` keyword.\n\nSee [JSON Schema 8.2.4.1. Direct References with \"$ref\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.4.1).", - "type": "string", - "nullable": true - }, - "$id": { - "description": "The `$id` keyword.\n\nSee [JSON Schema 8.2.2. The \"$id\" Keyword](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.2).", - "type": "string", - "nullable": true - }, - "title": { - "description": "The `title` keyword.\n\nSee [JSON Schema Validation 9.1. \"title\" and \"description\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).", - "type": "string", - "nullable": true - }, - "description": { - "description": "The `description` keyword.\n\nSee [JSON Schema Validation 9.1. \"title\" and \"description\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).", - "type": "string", - "nullable": true - }, - "default": { - "description": "The `default` keyword.\n\nSee [JSON Schema Validation 9.2. \"default\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.2).", - "nullable": true - }, - "deprecated": { - "description": "The `deprecated` keyword.\n\nSee [JSON Schema Validation 9.3. \"deprecated\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.3).", - "type": "boolean" - }, - "readOnly": { - "description": "The `readOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", - "type": "boolean" - }, - "writeOnly": { - "description": "The `writeOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", - "type": "boolean" - }, - "examples": { - "description": "The `examples` keyword.\n\nSee [JSON Schema Validation 9.5. \"examples\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.5).", - "type": "array", - "items": {} - }, - "allOf": { - "description": "The `allOf` keyword.\n\nSee [JSON Schema 9.2.1.1. \"allOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.1).", - "type": "array", - "items": { - "$ref": "#/components/schemas/Schema" - }, - "nullable": true - }, - "anyOf": { - "description": "The `anyOf` keyword.\n\nSee [JSON Schema 9.2.1.2. \"anyOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.2).", - "type": "array", - "items": { - "$ref": "#/components/schemas/Schema" - }, - "nullable": true - }, - "oneOf": { - "description": "The `oneOf` keyword.\n\nSee [JSON Schema 9.2.1.3. \"oneOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.3).", - "type": "array", - "items": { - "$ref": "#/components/schemas/Schema" - }, - "nullable": true - }, - "not": { - "description": "The `not` keyword.\n\nSee [JSON Schema 9.2.1.4. \"not\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.4).", - "allOf": [ - { - "$ref": "#/components/schemas/Schema" - } - ], - "nullable": true - }, - "if": { - "description": "The `if` keyword.\n\nSee [JSON Schema 9.2.2.1. \"if\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.1).", - "allOf": [ - { - "$ref": "#/components/schemas/Schema" - } - ], - "nullable": true - }, - "then": { - "description": "The `then` keyword.\n\nSee [JSON Schema 9.2.2.2. \"then\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.2).", - "allOf": [ - { - "$ref": "#/components/schemas/Schema" - } - ], - "nullable": true - }, - "else": { - "description": "The `else` keyword.\n\nSee [JSON Schema 9.2.2.3. \"else\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.3).", - "allOf": [ - { - "$ref": "#/components/schemas/Schema" - } - ], - "nullable": true - }, - "multipleOf": { - "description": "The `multipleOf` keyword.\n\nSee [JSON Schema Validation 6.2.1. \"multipleOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.1).", - "type": "number", - "format": "double", - "nullable": true - }, - "maximum": { - "description": "The `maximum` keyword.\n\nSee [JSON Schema Validation 6.2.2. \"maximum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.2).", - "type": "number", - "format": "double", - "nullable": true - }, - "exclusiveMaximum": { - "description": "The `exclusiveMaximum` keyword.\n\nSee [JSON Schema Validation 6.2.3. \"exclusiveMaximum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.3).", - "type": "number", - "format": "double", - "nullable": true - }, - "minimum": { - "description": "The `minimum` keyword.\n\nSee [JSON Schema Validation 6.2.4. \"minimum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.4).", - "type": "number", - "format": "double", - "nullable": true - }, - "exclusiveMinimum": { - "description": "The `exclusiveMinimum` keyword.\n\nSee [JSON Schema Validation 6.2.5. \"exclusiveMinimum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.5).", - "type": "number", - "format": "double", - "nullable": true - }, - "maxLength": { - "description": "The `maxLength` keyword.\n\nSee [JSON Schema Validation 6.3.1. \"maxLength\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.1).", - "type": "integer", - "format": "uint32", - "minimum": 0.0, - "nullable": true - }, - "minLength": { - "description": "The `minLength` keyword.\n\nSee [JSON Schema Validation 6.3.2. \"minLength\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.2).", - "type": "integer", - "format": "uint32", - "minimum": 0.0, - "nullable": true - }, - "pattern": { - "description": "The `pattern` keyword.\n\nSee [JSON Schema Validation 6.3.3. \"pattern\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.3).", - "type": "string", - "nullable": true - }, - "items": { - "description": "The `items` keyword.\n\nSee [JSON Schema 9.3.1.1. \"items\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.1).", - "allOf": [ - { - "$ref": "#/components/schemas/SingleOrVec_for_Schema" - } - ], - "nullable": true - }, - "additionalItems": { - "description": "The `additionalItems` keyword.\n\nSee [JSON Schema 9.3.1.2. \"additionalItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.2).", - "allOf": [ - { - "$ref": "#/components/schemas/Schema" - } - ], - "nullable": true - }, - "maxItems": { - "description": "The `maxItems` keyword.\n\nSee [JSON Schema Validation 6.4.1. \"maxItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.1).", - "type": "integer", - "format": "uint32", - "minimum": 0.0, - "nullable": true - }, - "minItems": { - "description": "The `minItems` keyword.\n\nSee [JSON Schema Validation 6.4.2. \"minItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.2).", - "type": "integer", - "format": "uint32", - "minimum": 0.0, - "nullable": true - }, - "uniqueItems": { - "description": "The `uniqueItems` keyword.\n\nSee [JSON Schema Validation 6.4.3. \"uniqueItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.3).", - "type": "boolean", - "nullable": true - }, - "contains": { - "description": "The `contains` keyword.\n\nSee [JSON Schema 9.3.1.4. \"contains\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.4).", - "allOf": [ - { - "$ref": "#/components/schemas/Schema" - } - ], - "nullable": true - }, - "maxProperties": { - "description": "The `maxProperties` keyword.\n\nSee [JSON Schema Validation 6.5.1. \"maxProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.1).", - "type": "integer", - "format": "uint32", - "minimum": 0.0, - "nullable": true - }, - "minProperties": { - "description": "The `minProperties` keyword.\n\nSee [JSON Schema Validation 6.5.2. \"minProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.2).", - "type": "integer", - "format": "uint32", - "minimum": 0.0, - "nullable": true - }, - "required": { - "description": "The `required` keyword.\n\nSee [JSON Schema Validation 6.5.3. \"required\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.3).", - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "properties": { - "description": "The `properties` keyword.\n\nSee [JSON Schema 9.3.2.1. \"properties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.1).", - "type": "object", - "additionalProperties": { - "$ref": "#/components/schemas/Schema" - } - }, - "patternProperties": { - "description": "The `patternProperties` keyword.\n\nSee [JSON Schema 9.3.2.2. \"patternProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.2).", - "type": "object", - "additionalProperties": { - "$ref": "#/components/schemas/Schema" - } - }, - "additionalProperties": { - "description": "The `additionalProperties` keyword.\n\nSee [JSON Schema 9.3.2.3. \"additionalProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.3).", - "allOf": [ - { - "$ref": "#/components/schemas/Schema" - } - ], - "nullable": true - }, - "propertyNames": { - "description": "The `propertyNames` keyword.\n\nSee [JSON Schema 9.3.2.5. \"propertyNames\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.5).", - "allOf": [ - { - "$ref": "#/components/schemas/Schema" - } - ], - "nullable": true - } - }, - "additionalProperties": true - }, - "SingleOrVec_for_InstanceType": { - "description": "A type which can be serialized as a single item, or multiple items.\n\nIn some contexts, a `Single` may be semantically distinct from a `Vec` containing only item.", - "anyOf": [ - { - "$ref": "#/components/schemas/InstanceType" - }, - { - "type": "array", - "items": { - "$ref": "#/components/schemas/InstanceType" - } - } - ] - }, - "InstanceType": { - "description": "The possible types of values in JSON Schema documents.\n\nSee [JSON Schema 4.2.1. Instance Data Model](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-4.2.1).", - "type": "string", - "enum": [ - "null", - "boolean", - "object", - "array", - "number", - "string", - "integer" - ] - }, - "SingleOrVec_for_Schema": { - "description": "A type which can be serialized as a single item, or multiple items.\n\nIn some contexts, a `Single` may be semantically distinct from a `Vec` containing only item.", - "anyOf": [ - { - "$ref": "#/components/schemas/Schema" - }, - { - "type": "array", - "items": { - "$ref": "#/components/schemas/Schema" - } - } - ] - } - } -} \ No newline at end of file diff --git a/schemars/tests/expected/schema.json b/schemars/tests/expected/schema.json deleted file mode 100644 index 6cf840b..0000000 --- a/schemars/tests/expected/schema.json +++ /dev/null @@ -1,762 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "RootSchema", - "description": "The root object of a JSON Schema document.", - "type": "object", - "properties": { - "$schema": { - "description": "The `$schema` keyword.\n\nSee [JSON Schema 8.1.1. The \"$schema\" Keyword](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.1.1).", - "type": [ - "string", - "null" - ] - }, - "definitions": { - "description": "The `definitions` keyword.\n\nIn JSON Schema draft 2019-09 this was replaced by $defs, but in Schemars this is still serialized as `definitions` for backward-compatibility.\n\nSee [JSON Schema 8.2.5. Schema Re-Use With \"$defs\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.5), and [JSON Schema (draft 07) 9. Schema Re-Use With \"definitions\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-9).", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Schema" - } - }, - "type": { - "description": "The `type` keyword.\n\nSee [JSON Schema Validation 6.1.1. \"type\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.1) and [JSON Schema 4.2.1. Instance Data Model](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-4.2.1).", - "anyOf": [ - { - "$ref": "#/definitions/SingleOrVec_for_InstanceType" - }, - { - "type": "null" - } - ] - }, - "format": { - "description": "The `format` keyword.\n\nSee [JSON Schema Validation 7. A Vocabulary for Semantic Content With \"format\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-7).", - "type": [ - "string", - "null" - ] - }, - "enum": { - "description": "The `enum` keyword.\n\nSee [JSON Schema Validation 6.1.2. \"enum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.2)", - "type": [ - "array", - "null" - ], - "items": true - }, - "const": { - "description": "The `const` keyword.\n\nSee [JSON Schema Validation 6.1.3. \"const\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.3)" - }, - "$ref": { - "description": "The `$ref` keyword.\n\nSee [JSON Schema 8.2.4.1. Direct References with \"$ref\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.4.1).", - "type": [ - "string", - "null" - ] - }, - "$id": { - "description": "The `$id` keyword.\n\nSee [JSON Schema 8.2.2. The \"$id\" Keyword](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.2).", - "type": [ - "string", - "null" - ] - }, - "title": { - "description": "The `title` keyword.\n\nSee [JSON Schema Validation 9.1. \"title\" and \"description\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).", - "type": [ - "string", - "null" - ] - }, - "description": { - "description": "The `description` keyword.\n\nSee [JSON Schema Validation 9.1. \"title\" and \"description\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).", - "type": [ - "string", - "null" - ] - }, - "default": { - "description": "The `default` keyword.\n\nSee [JSON Schema Validation 9.2. \"default\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.2)." - }, - "deprecated": { - "description": "The `deprecated` keyword.\n\nSee [JSON Schema Validation 9.3. \"deprecated\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.3).", - "type": "boolean" - }, - "readOnly": { - "description": "The `readOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", - "type": "boolean" - }, - "writeOnly": { - "description": "The `writeOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", - "type": "boolean" - }, - "examples": { - "description": "The `examples` keyword.\n\nSee [JSON Schema Validation 9.5. \"examples\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.5).", - "type": "array", - "items": true - }, - "allOf": { - "description": "The `allOf` keyword.\n\nSee [JSON Schema 9.2.1.1. \"allOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.1).", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Schema" - } - }, - "anyOf": { - "description": "The `anyOf` keyword.\n\nSee [JSON Schema 9.2.1.2. \"anyOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.2).", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Schema" - } - }, - "oneOf": { - "description": "The `oneOf` keyword.\n\nSee [JSON Schema 9.2.1.3. \"oneOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.3).", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Schema" - } - }, - "not": { - "description": "The `not` keyword.\n\nSee [JSON Schema 9.2.1.4. \"not\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.4).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "if": { - "description": "The `if` keyword.\n\nSee [JSON Schema 9.2.2.1. \"if\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.1).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "then": { - "description": "The `then` keyword.\n\nSee [JSON Schema 9.2.2.2. \"then\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.2).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "else": { - "description": "The `else` keyword.\n\nSee [JSON Schema 9.2.2.3. \"else\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.3).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "multipleOf": { - "description": "The `multipleOf` keyword.\n\nSee [JSON Schema Validation 6.2.1. \"multipleOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.1).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "maximum": { - "description": "The `maximum` keyword.\n\nSee [JSON Schema Validation 6.2.2. \"maximum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.2).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "exclusiveMaximum": { - "description": "The `exclusiveMaximum` keyword.\n\nSee [JSON Schema Validation 6.2.3. \"exclusiveMaximum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.3).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "minimum": { - "description": "The `minimum` keyword.\n\nSee [JSON Schema Validation 6.2.4. \"minimum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.4).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "exclusiveMinimum": { - "description": "The `exclusiveMinimum` keyword.\n\nSee [JSON Schema Validation 6.2.5. \"exclusiveMinimum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.5).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "maxLength": { - "description": "The `maxLength` keyword.\n\nSee [JSON Schema Validation 6.3.1. \"maxLength\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.1).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "minLength": { - "description": "The `minLength` keyword.\n\nSee [JSON Schema Validation 6.3.2. \"minLength\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.2).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "pattern": { - "description": "The `pattern` keyword.\n\nSee [JSON Schema Validation 6.3.3. \"pattern\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.3).", - "type": [ - "string", - "null" - ] - }, - "items": { - "description": "The `items` keyword.\n\nSee [JSON Schema 9.3.1.1. \"items\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.1).", - "anyOf": [ - { - "$ref": "#/definitions/SingleOrVec_for_Schema" - }, - { - "type": "null" - } - ] - }, - "additionalItems": { - "description": "The `additionalItems` keyword.\n\nSee [JSON Schema 9.3.1.2. \"additionalItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.2).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "maxItems": { - "description": "The `maxItems` keyword.\n\nSee [JSON Schema Validation 6.4.1. \"maxItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.1).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "minItems": { - "description": "The `minItems` keyword.\n\nSee [JSON Schema Validation 6.4.2. \"minItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.2).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "uniqueItems": { - "description": "The `uniqueItems` keyword.\n\nSee [JSON Schema Validation 6.4.3. \"uniqueItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.3).", - "type": [ - "boolean", - "null" - ] - }, - "contains": { - "description": "The `contains` keyword.\n\nSee [JSON Schema 9.3.1.4. \"contains\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.4).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "maxProperties": { - "description": "The `maxProperties` keyword.\n\nSee [JSON Schema Validation 6.5.1. \"maxProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.1).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "minProperties": { - "description": "The `minProperties` keyword.\n\nSee [JSON Schema Validation 6.5.2. \"minProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.2).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "required": { - "description": "The `required` keyword.\n\nSee [JSON Schema Validation 6.5.3. \"required\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.3).", - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "properties": { - "description": "The `properties` keyword.\n\nSee [JSON Schema 9.3.2.1. \"properties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.1).", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Schema" - } - }, - "patternProperties": { - "description": "The `patternProperties` keyword.\n\nSee [JSON Schema 9.3.2.2. \"patternProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.2).", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Schema" - } - }, - "additionalProperties": { - "description": "The `additionalProperties` keyword.\n\nSee [JSON Schema 9.3.2.3. \"additionalProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.3).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "propertyNames": { - "description": "The `propertyNames` keyword.\n\nSee [JSON Schema 9.3.2.5. \"propertyNames\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.5).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": true, - "definitions": { - "Schema": { - "description": "A JSON Schema.", - "anyOf": [ - { - "description": "A trivial boolean JSON Schema.\n\nThe schema `true` matches everything (always passes validation), whereas the schema `false` matches nothing (always fails validation).", - "type": "boolean" - }, - { - "description": "A JSON Schema object.", - "allOf": [ - { - "$ref": "#/definitions/SchemaObject" - } - ] - } - ] - }, - "SchemaObject": { - "description": "A JSON Schema object.", - "type": "object", - "properties": { - "type": { - "description": "The `type` keyword.\n\nSee [JSON Schema Validation 6.1.1. \"type\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.1) and [JSON Schema 4.2.1. Instance Data Model](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-4.2.1).", - "anyOf": [ - { - "$ref": "#/definitions/SingleOrVec_for_InstanceType" - }, - { - "type": "null" - } - ] - }, - "format": { - "description": "The `format` keyword.\n\nSee [JSON Schema Validation 7. A Vocabulary for Semantic Content With \"format\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-7).", - "type": [ - "string", - "null" - ] - }, - "enum": { - "description": "The `enum` keyword.\n\nSee [JSON Schema Validation 6.1.2. \"enum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.2)", - "type": [ - "array", - "null" - ], - "items": true - }, - "const": { - "description": "The `const` keyword.\n\nSee [JSON Schema Validation 6.1.3. \"const\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.3)" - }, - "$ref": { - "description": "The `$ref` keyword.\n\nSee [JSON Schema 8.2.4.1. Direct References with \"$ref\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.4.1).", - "type": [ - "string", - "null" - ] - }, - "$id": { - "description": "The `$id` keyword.\n\nSee [JSON Schema 8.2.2. The \"$id\" Keyword](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.2).", - "type": [ - "string", - "null" - ] - }, - "title": { - "description": "The `title` keyword.\n\nSee [JSON Schema Validation 9.1. \"title\" and \"description\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).", - "type": [ - "string", - "null" - ] - }, - "description": { - "description": "The `description` keyword.\n\nSee [JSON Schema Validation 9.1. \"title\" and \"description\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).", - "type": [ - "string", - "null" - ] - }, - "default": { - "description": "The `default` keyword.\n\nSee [JSON Schema Validation 9.2. \"default\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.2)." - }, - "deprecated": { - "description": "The `deprecated` keyword.\n\nSee [JSON Schema Validation 9.3. \"deprecated\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.3).", - "type": "boolean" - }, - "readOnly": { - "description": "The `readOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", - "type": "boolean" - }, - "writeOnly": { - "description": "The `writeOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", - "type": "boolean" - }, - "examples": { - "description": "The `examples` keyword.\n\nSee [JSON Schema Validation 9.5. \"examples\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.5).", - "type": "array", - "items": true - }, - "allOf": { - "description": "The `allOf` keyword.\n\nSee [JSON Schema 9.2.1.1. \"allOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.1).", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Schema" - } - }, - "anyOf": { - "description": "The `anyOf` keyword.\n\nSee [JSON Schema 9.2.1.2. \"anyOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.2).", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Schema" - } - }, - "oneOf": { - "description": "The `oneOf` keyword.\n\nSee [JSON Schema 9.2.1.3. \"oneOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.3).", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Schema" - } - }, - "not": { - "description": "The `not` keyword.\n\nSee [JSON Schema 9.2.1.4. \"not\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.4).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "if": { - "description": "The `if` keyword.\n\nSee [JSON Schema 9.2.2.1. \"if\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.1).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "then": { - "description": "The `then` keyword.\n\nSee [JSON Schema 9.2.2.2. \"then\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.2).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "else": { - "description": "The `else` keyword.\n\nSee [JSON Schema 9.2.2.3. \"else\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.3).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "multipleOf": { - "description": "The `multipleOf` keyword.\n\nSee [JSON Schema Validation 6.2.1. \"multipleOf\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.1).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "maximum": { - "description": "The `maximum` keyword.\n\nSee [JSON Schema Validation 6.2.2. \"maximum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.2).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "exclusiveMaximum": { - "description": "The `exclusiveMaximum` keyword.\n\nSee [JSON Schema Validation 6.2.3. \"exclusiveMaximum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.3).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "minimum": { - "description": "The `minimum` keyword.\n\nSee [JSON Schema Validation 6.2.4. \"minimum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.4).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "exclusiveMinimum": { - "description": "The `exclusiveMinimum` keyword.\n\nSee [JSON Schema Validation 6.2.5. \"exclusiveMinimum\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.5).", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "maxLength": { - "description": "The `maxLength` keyword.\n\nSee [JSON Schema Validation 6.3.1. \"maxLength\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.1).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "minLength": { - "description": "The `minLength` keyword.\n\nSee [JSON Schema Validation 6.3.2. \"minLength\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.2).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "pattern": { - "description": "The `pattern` keyword.\n\nSee [JSON Schema Validation 6.3.3. \"pattern\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.3).", - "type": [ - "string", - "null" - ] - }, - "items": { - "description": "The `items` keyword.\n\nSee [JSON Schema 9.3.1.1. \"items\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.1).", - "anyOf": [ - { - "$ref": "#/definitions/SingleOrVec_for_Schema" - }, - { - "type": "null" - } - ] - }, - "additionalItems": { - "description": "The `additionalItems` keyword.\n\nSee [JSON Schema 9.3.1.2. \"additionalItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.2).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "maxItems": { - "description": "The `maxItems` keyword.\n\nSee [JSON Schema Validation 6.4.1. \"maxItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.1).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "minItems": { - "description": "The `minItems` keyword.\n\nSee [JSON Schema Validation 6.4.2. \"minItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.2).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "uniqueItems": { - "description": "The `uniqueItems` keyword.\n\nSee [JSON Schema Validation 6.4.3. \"uniqueItems\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.3).", - "type": [ - "boolean", - "null" - ] - }, - "contains": { - "description": "The `contains` keyword.\n\nSee [JSON Schema 9.3.1.4. \"contains\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.4).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "maxProperties": { - "description": "The `maxProperties` keyword.\n\nSee [JSON Schema Validation 6.5.1. \"maxProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.1).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "minProperties": { - "description": "The `minProperties` keyword.\n\nSee [JSON Schema Validation 6.5.2. \"minProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.2).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "required": { - "description": "The `required` keyword.\n\nSee [JSON Schema Validation 6.5.3. \"required\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.3).", - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "properties": { - "description": "The `properties` keyword.\n\nSee [JSON Schema 9.3.2.1. \"properties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.1).", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Schema" - } - }, - "patternProperties": { - "description": "The `patternProperties` keyword.\n\nSee [JSON Schema 9.3.2.2. \"patternProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.2).", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Schema" - } - }, - "additionalProperties": { - "description": "The `additionalProperties` keyword.\n\nSee [JSON Schema 9.3.2.3. \"additionalProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.3).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "propertyNames": { - "description": "The `propertyNames` keyword.\n\nSee [JSON Schema 9.3.2.5. \"propertyNames\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.5).", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": true - }, - "SingleOrVec_for_InstanceType": { - "description": "A type which can be serialized as a single item, or multiple items.\n\nIn some contexts, a `Single` may be semantically distinct from a `Vec` containing only item.", - "anyOf": [ - { - "$ref": "#/definitions/InstanceType" - }, - { - "type": "array", - "items": { - "$ref": "#/definitions/InstanceType" - } - } - ] - }, - "InstanceType": { - "description": "The possible types of values in JSON Schema documents.\n\nSee [JSON Schema 4.2.1. Instance Data Model](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-4.2.1).", - "type": "string", - "enum": [ - "null", - "boolean", - "object", - "array", - "number", - "string", - "integer" - ] - }, - "SingleOrVec_for_Schema": { - "description": "A type which can be serialized as a single item, or multiple items.\n\nIn some contexts, a `Single` may be semantically distinct from a `Vec` containing only item.", - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "array", - "items": { - "$ref": "#/definitions/Schema" - } - } - ] - } - } -} \ No newline at end of file From 4dd92a646ada9cea1dc5347521d446053cf371eb Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Mon, 13 May 2024 18:17:21 +0100 Subject: [PATCH 05/31] Update OpenAPI 3.0 schema URI --- schemars/src/gen.rs | 2 +- .../tests/expected/from_value_openapi3.json | 78 +++++++++---------- .../expected/schema_settings-openapi3.json | 38 ++++----- 3 files changed, 59 insertions(+), 59 deletions(-) diff --git a/schemars/src/gen.rs b/schemars/src/gen.rs index 040f798..17c6e16 100644 --- a/schemars/src/gen.rs +++ b/schemars/src/gen.rs @@ -88,7 +88,7 @@ impl SchemaSettings { option_add_null_type: false, definitions_path: "#/components/schemas/".to_owned(), meta_schema: Some( - "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema" + "https://spec.openapis.org/oas/3.0/schema/2021-09-28#/definitions/Schema" .to_owned(), ), visitors: vec![ diff --git a/schemars/tests/expected/from_value_openapi3.json b/schemars/tests/expected/from_value_openapi3.json index 132a59a..858d286 100644 --- a/schemars/tests/expected/from_value_openapi3.json +++ b/schemars/tests/expected/from_value_openapi3.json @@ -1,32 +1,34 @@ { - "$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema", + "$schema": "https://spec.openapis.org/oas/3.0/schema/2021-09-28#/definitions/Schema", "title": "MyStruct", "type": "object", - "properties": { - "myInt": { - "type": "integer" + "example": { + "myBool": true, + "myInnerStruct": { + "my_empty_map": {}, + "my_empty_vec": [], + "my_map": { + "": 0.0 + }, + "my_tuple": [ + "💩", + 42 + ], + "my_vec": [ + "hello", + "world" + ] }, + "myInt": 123, + "myNullableEnum": null + }, + "properties": { "myBool": { "type": "boolean" }, - "myNullableEnum": { - "nullable": true - }, "myInnerStruct": { "type": "object", "properties": { - "my_map": { - "type": "object", - "additionalProperties": { - "type": "number" - } - }, - "my_vec": { - "type": "array", - "items": { - "type": "string" - } - }, "my_empty_map": { "type": "object", "additionalProperties": true @@ -35,6 +37,12 @@ "type": "array", "items": {} }, + "my_map": { + "type": "object", + "additionalProperties": { + "type": "number" + } + }, "my_tuple": { "type": "array", "items": [ @@ -49,28 +57,20 @@ ], "maxItems": 2, "minItems": 2 + }, + "my_vec": { + "type": "array", + "items": { + "type": "string" + } } } - } - }, - "example": { - "myBool": true, - "myInnerStruct": { - "my_empty_map": {}, - "my_empty_vec": [], - "my_map": { - "": 0.0 - }, - "my_tuple": [ - "💩", - 42 - ], - "my_vec": [ - "hello", - "world" - ] }, - "myInt": 123, - "myNullableEnum": null + "myInt": { + "type": "integer" + }, + "myNullableEnum": { + "nullable": true + } } } \ No newline at end of file diff --git a/schemars/tests/expected/schema_settings-openapi3.json b/schemars/tests/expected/schema_settings-openapi3.json index 17fe805..15b752d 100644 --- a/schemars/tests/expected/schema_settings-openapi3.json +++ b/schemars/tests/expected/schema_settings-openapi3.json @@ -1,32 +1,32 @@ { - "$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema", + "$schema": "https://spec.openapis.org/oas/3.0/schema/2021-09-28#/definitions/Schema", "title": "Outer", "type": "object", - "required": [ - "int", - "value", - "values" - ], "properties": { + "inner": { + "allOf": [ + { + "$ref": "#/components/schemas/Inner" + } + ], + "nullable": true + }, "int": { "type": "integer", "format": "int32", "example": 8 }, + "value": {}, "values": { "type": "object", "additionalProperties": true - }, - "value": {}, - "inner": { - "allOf": [ - { - "$ref": "#/components/schemas/Inner" - } - ], - "nullable": true } }, + "required": [ + "int", + "value", + "values" + ], "definitions": { "Inner": { "oneOf": [ @@ -46,13 +46,13 @@ }, { "type": "object", - "required": [ - "ValueNewType" - ], + "additionalProperties": false, "properties": { "ValueNewType": {} }, - "additionalProperties": false + "required": [ + "ValueNewType" + ] } ] } From 384f232a2e121b896ba1de77423a29e14f03db4f Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Mon, 13 May 2024 18:33:48 +0100 Subject: [PATCH 06/31] Regenerate test schemas --- schemars/src/schema.rs | 13 ++- schemars/tests/expected/bound.json | 10 +- schemars/tests/expected/chrono-types.json | 40 +++---- schemars/tests/expected/crate_alias.json | 16 +-- schemars/tests/expected/default.json | 28 ++--- schemars/tests/expected/deprecated-enum.json | 26 ++--- .../tests/expected/deprecated-struct.json | 20 ++-- .../tests/expected/doc_comments_enum.json | 8 +- .../tests/expected/doc_comments_override.json | 10 +- .../tests/expected/doc_comments_struct.json | 10 +- .../doc_comments_struct_ref_siblings.json | 10 +- .../expected/duration_and_systemtime.json | 44 +++---- .../expected/enum-adjacent-tagged-duf.json | 96 +++++++-------- .../tests/expected/enum-adjacent-tagged.json | 96 +++++++-------- .../tests/expected/enum-external-duf.json | 68 +++++------ schemars/tests/expected/enum-external.json | 68 +++++------ .../tests/expected/enum-internal-duf.json | 62 +++++----- schemars/tests/expected/enum-internal.json | 62 +++++----- .../expected/enum-simple-internal-duf.json | 24 ++-- .../tests/expected/enum-simple-internal.json | 24 ++-- .../tests/expected/enum-untagged-duf.json | 38 +++--- schemars/tests/expected/enum-untagged.json | 38 +++--- schemars/tests/expected/enumset.json | 2 +- schemars/tests/expected/examples.json | 48 ++++---- schemars/tests/expected/flatten.json | 26 ++--- schemars/tests/expected/from_json_value.json | 34 +++--- .../tests/expected/from_value_2019_09.json | 78 ++++++------- .../tests/expected/from_value_draft07.json | 78 ++++++------- .../tests/expected/from_value_openapi3.json | 40 +++---- schemars/tests/expected/indexmap.json | 10 +- .../expected/inline-subschemas-recursive.json | 16 +-- .../tests/expected/inline-subschemas.json | 16 +-- schemars/tests/expected/macro_built_enum.json | 16 +-- .../tests/expected/macro_built_struct.json | 18 +-- schemars/tests/expected/nonzero_ints.json | 30 ++--- schemars/tests/expected/os_strings.json | 28 ++--- .../tests/expected/property-name-struct.json | 12 +- schemars/tests/expected/range.json | 106 ++++++++--------- schemars/tests/expected/remote_derive.json | 50 ++++---- .../tests/expected/remote_derive_generic.json | 42 +++---- schemars/tests/expected/result.json | 64 +++++----- schemars/tests/expected/same_name.json | 24 ++-- .../expected/schema-name-const-generics.json | 8 +- .../tests/expected/schema-name-custom.json | 28 ++--- .../expected/schema-name-mixed-generics.json | 32 ++--- .../expected/schema_settings-2019_09.json | 44 +++---- .../expected/schema_settings-openapi3.json | 2 +- schemars/tests/expected/schema_settings.json | 44 +++---- .../schema_with-enum-adjacent-tagged.json | 48 ++++---- .../expected/schema_with-enum-external.json | 40 +++---- .../expected/schema_with-enum-internal.json | 26 ++--- .../expected/schema_with-enum-untagged.json | 8 +- .../tests/expected/schema_with-struct.json | 18 +-- schemars/tests/expected/semver.json | 8 +- .../tests/expected/skip_enum_variants.json | 8 +- .../tests/expected/skip_struct_fields.json | 24 ++-- .../struct-normal-additional-properties.json | 18 +-- schemars/tests/expected/struct-normal.json | 18 +-- schemars/tests/expected/url.json | 10 +- schemars/tests/expected/validate.json | 110 +++++++++--------- schemars/tests/expected/validate_inner.json | 20 ++-- .../expected/validate_schemars_attrs.json | 110 +++++++++--------- 62 files changed, 1092 insertions(+), 1081 deletions(-) diff --git a/schemars/src/schema.rs b/schemars/src/schema.rs index 6a88158..82be821 100644 --- a/schemars/src/schema.rs +++ b/schemars/src/schema.rs @@ -179,8 +179,17 @@ mod ser { use serde::ser::{Serialize, SerializeMap, SerializeSeq}; use serde_json::Value; - const ORDERED_KEYWORDS_START: [&str; 6] = - ["$id", "$schema", "title", "description", "type", "format"]; + // The order of properties in a JSON Schema object is insignificant, but we explicitly order + // some of them here to make them easier for a human to read. + const ORDERED_KEYWORDS_START: [&str; 7] = [ + "$id", + "$schema", + "title", + "description", + "type", + "format", + "properties", + ]; const ORDERED_KEYWORDS_END: [&str; 2] = ["$defs", "definitions"]; pub(super) struct OrderedKeywordWrapper<'a>(pub &'a Value); diff --git a/schemars/tests/expected/bound.json b/schemars/tests/expected/bound.json index 3c022dc..e4ceb13 100644 --- a/schemars/tests/expected/bound.json +++ b/schemars/tests/expected/bound.json @@ -2,10 +2,6 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyContainer", "type": "object", - "required": [ - "associated", - "generic" - ], "properties": { "associated": { "type": "string" @@ -13,5 +9,9 @@ "generic": { "type": "null" } - } + }, + "required": [ + "associated", + "generic" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/chrono-types.json b/schemars/tests/expected/chrono-types.json index e3e788f..b96b00c 100644 --- a/schemars/tests/expected/chrono-types.json +++ b/schemars/tests/expected/chrono-types.json @@ -2,26 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "ChronoTypes", "type": "object", - "required": [ - "date_time", - "naive_date", - "naive_date_time", - "naive_time", - "weekday" - ], "properties": { - "weekday": { - "type": "string", - "enum": [ - "Mon", - "Tue", - "Wed", - "Thu", - "Fri", - "Sat", - "Sun" - ] - }, "date_time": { "type": "string", "format": "date-time" @@ -37,6 +18,25 @@ "naive_time": { "type": "string", "format": "partial-date-time" + }, + "weekday": { + "type": "string", + "enum": [ + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + "Sun" + ] } - } + }, + "required": [ + "date_time", + "naive_date", + "naive_date_time", + "naive_time", + "weekday" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/crate_alias.json b/schemars/tests/expected/crate_alias.json index 66bf749..d37d482 100644 --- a/schemars/tests/expected/crate_alias.json +++ b/schemars/tests/expected/crate_alias.json @@ -2,18 +2,18 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Struct", "type": "object", - "required": [ - "bar", - "foo" - ], "properties": { + "bar": { + "type": "boolean" + }, "foo": { "description": "This is a document", "type": "integer", "format": "int32" - }, - "bar": { - "type": "boolean" } - } + }, + "required": [ + "bar", + "foo" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/default.json b/schemars/tests/expected/default.json index 4155d53..f8c6b2a 100644 --- a/schemars/tests/expected/default.json +++ b/schemars/tests/expected/default.json @@ -3,14 +3,14 @@ "title": "MyStruct", "type": "object", "properties": { + "my_bool": { + "type": "boolean", + "default": false + }, "my_int": { - "default": 0, "type": "integer", - "format": "int32" - }, - "my_bool": { - "default": false, - "type": "boolean" + "format": "int32", + "default": 0 }, "my_optional_string": { "default": null, @@ -20,12 +20,12 @@ ] }, "my_struct2": { - "default": "i:0 b:false", "allOf": [ { "$ref": "#/definitions/MyStruct2" } - ] + ], + "default": "i:0 b:false" }, "my_struct2_default_skipped": { "$ref": "#/definitions/MyStruct2" @@ -38,14 +38,14 @@ "MyStruct2": { "type": "object", "properties": { + "my_bool": { + "type": "boolean", + "default": true + }, "my_int": { - "default": 6, "type": "integer", - "format": "int32" - }, - "my_bool": { - "default": true, - "type": "boolean" + "format": "int32", + "default": 6 } } }, diff --git a/schemars/tests/expected/deprecated-enum.json b/schemars/tests/expected/deprecated-enum.json index 825ae50..55a98ef 100644 --- a/schemars/tests/expected/deprecated-enum.json +++ b/schemars/tests/expected/deprecated-enum.json @@ -10,38 +10,38 @@ ] }, { - "deprecated": true, "type": "string", + "deprecated": true, "enum": [ "DeprecatedUnitVariant" ] }, { - "deprecated": true, "type": "object", - "required": [ - "DeprecatedStructVariant" - ], "properties": { "DeprecatedStructVariant": { "type": "object", - "required": [ - "deprecated_field", - "foo" - ], "properties": { "deprecated_field": { - "deprecated": true, - "type": "boolean" + "type": "boolean", + "deprecated": true }, "foo": { "type": "integer", "format": "int32" } - } + }, + "required": [ + "deprecated_field", + "foo" + ] } }, - "additionalProperties": false + "additionalProperties": false, + "deprecated": true, + "required": [ + "DeprecatedStructVariant" + ] } ] } \ No newline at end of file diff --git a/schemars/tests/expected/deprecated-struct.json b/schemars/tests/expected/deprecated-struct.json index 8f7ba71..b915eb1 100644 --- a/schemars/tests/expected/deprecated-struct.json +++ b/schemars/tests/expected/deprecated-struct.json @@ -1,20 +1,20 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "DeprecatedStruct", - "deprecated": true, "type": "object", - "required": [ - "deprecated_field", - "foo" - ], "properties": { + "deprecated_field": { + "type": "boolean", + "deprecated": true + }, "foo": { "type": "integer", "format": "int32" - }, - "deprecated_field": { - "deprecated": true, - "type": "boolean" } - } + }, + "deprecated": true, + "required": [ + "deprecated_field", + "foo" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/doc_comments_enum.json b/schemars/tests/expected/doc_comments_enum.json index 53aaa86..c983c1d 100644 --- a/schemars/tests/expected/doc_comments_enum.json +++ b/schemars/tests/expected/doc_comments_enum.json @@ -21,9 +21,6 @@ "title": "Complex variant", "description": "This is a struct-like variant.", "type": "object", - "required": [ - "Complex" - ], "properties": { "Complex": { "type": "object", @@ -39,7 +36,10 @@ } } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "Complex" + ] } ] } \ No newline at end of file diff --git a/schemars/tests/expected/doc_comments_override.json b/schemars/tests/expected/doc_comments_override.json index 83dce79..0f90433 100644 --- a/schemars/tests/expected/doc_comments_override.json +++ b/schemars/tests/expected/doc_comments_override.json @@ -3,10 +3,6 @@ "title": "OverrideDocs struct", "description": "New description", "type": "object", - "required": [ - "my_int", - "my_undocumented_bool" - ], "properties": { "my_int": { "title": "My integer", @@ -17,5 +13,9 @@ "my_undocumented_bool": { "type": "boolean" } - } + }, + "required": [ + "my_int", + "my_undocumented_bool" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/doc_comments_struct.json b/schemars/tests/expected/doc_comments_struct.json index 9f1b4bf..103a71a 100644 --- a/schemars/tests/expected/doc_comments_struct.json +++ b/schemars/tests/expected/doc_comments_struct.json @@ -3,11 +3,6 @@ "title": "This is the struct's title", "description": "This is the struct's description.", "type": "object", - "required": [ - "my_int", - "my_undocumented_bool", - "my_unit" - ], "properties": { "my_int": { "title": "An integer", @@ -26,6 +21,11 @@ ] } }, + "required": [ + "my_int", + "my_undocumented_bool", + "my_unit" + ], "definitions": { "MyUnitStruct": { "title": "A Unit", diff --git a/schemars/tests/expected/doc_comments_struct_ref_siblings.json b/schemars/tests/expected/doc_comments_struct_ref_siblings.json index 35bb648..a3dcbe0 100644 --- a/schemars/tests/expected/doc_comments_struct_ref_siblings.json +++ b/schemars/tests/expected/doc_comments_struct_ref_siblings.json @@ -3,11 +3,6 @@ "title": "This is the struct's title", "description": "This is the struct's description.", "type": "object", - "required": [ - "my_int", - "my_undocumented_bool", - "my_unit" - ], "properties": { "my_int": { "title": "An integer", @@ -22,6 +17,11 @@ "$ref": "#/definitions/MyUnitStruct" } }, + "required": [ + "my_int", + "my_undocumented_bool", + "my_unit" + ], "definitions": { "MyUnitStruct": { "title": "A Unit", diff --git a/schemars/tests/expected/duration_and_systemtime.json b/schemars/tests/expected/duration_and_systemtime.json index d01f7d5..b212599 100644 --- a/schemars/tests/expected/duration_and_systemtime.json +++ b/schemars/tests/expected/duration_and_systemtime.json @@ -2,10 +2,6 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct", "type": "object", - "required": [ - "duration", - "time" - ], "properties": { "duration": { "$ref": "#/definitions/Duration" @@ -14,44 +10,48 @@ "$ref": "#/definitions/SystemTime" } }, + "required": [ + "duration", + "time" + ], "definitions": { "Duration": { "type": "object", - "required": [ - "nanos", - "secs" - ], "properties": { - "secs": { + "nanos": { "type": "integer", - "format": "uint64", + "format": "uint32", "minimum": 0.0 }, - "nanos": { + "secs": { "type": "integer", - "format": "uint32", + "format": "uint64", "minimum": 0.0 } - } + }, + "required": [ + "nanos", + "secs" + ] }, "SystemTime": { "type": "object", - "required": [ - "nanos_since_epoch", - "secs_since_epoch" - ], "properties": { - "secs_since_epoch": { + "nanos_since_epoch": { "type": "integer", - "format": "uint64", + "format": "uint32", "minimum": 0.0 }, - "nanos_since_epoch": { + "secs_since_epoch": { "type": "integer", - "format": "uint32", + "format": "uint64", "minimum": 0.0 } - } + }, + "required": [ + "nanos_since_epoch", + "secs_since_epoch" + ] } } } \ No newline at end of file diff --git a/schemars/tests/expected/enum-adjacent-tagged-duf.json b/schemars/tests/expected/enum-adjacent-tagged-duf.json index c5b54c8..a89ab50 100644 --- a/schemars/tests/expected/enum-adjacent-tagged-duf.json +++ b/schemars/tests/expected/enum-adjacent-tagged-duf.json @@ -4,9 +4,6 @@ "oneOf": [ { "type": "object", - "required": [ - "t" - ], "properties": { "t": { "type": "string", @@ -15,14 +12,13 @@ ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "t" + ] }, { "type": "object", - "required": [ - "c", - "t" - ], "properties": { "c": { "type": "object", @@ -37,14 +33,14 @@ ] } }, - "additionalProperties": false - }, - { - "type": "object", + "additionalProperties": false, "required": [ "c", "t" - ], + ] + }, + { + "type": "object", "properties": { "c": { "$ref": "#/definitions/UnitStruct" @@ -56,14 +52,14 @@ ] } }, - "additionalProperties": false - }, - { - "type": "object", + "additionalProperties": false, "required": [ "c", "t" - ], + ] + }, + { + "type": "object", "properties": { "c": { "$ref": "#/definitions/Struct" @@ -75,21 +71,17 @@ ] } }, - "additionalProperties": false - }, - { - "type": "object", + "additionalProperties": false, "required": [ "c", "t" - ], + ] + }, + { + "type": "object", "properties": { "c": { "type": "object", - "required": [ - "bar", - "foo" - ], "properties": { "bar": { "type": "boolean" @@ -99,7 +91,11 @@ "format": "int32" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "bar", + "foo" + ] }, "t": { "type": "string", @@ -108,14 +104,14 @@ ] } }, - "additionalProperties": false - }, - { - "type": "object", + "additionalProperties": false, "required": [ "c", "t" - ], + ] + }, + { + "type": "object", "properties": { "c": { "type": "array", @@ -138,13 +134,14 @@ ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "c", + "t" + ] }, { "type": "object", - "required": [ - "t" - ], "properties": { "t": { "type": "string", @@ -153,14 +150,13 @@ ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "t" + ] }, { "type": "object", - "required": [ - "c", - "t" - ], "properties": { "c": { "type": "integer", @@ -173,16 +169,16 @@ ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "c", + "t" + ] } ], "definitions": { "Struct": { "type": "object", - "required": [ - "bar", - "foo" - ], "properties": { "bar": { "type": "boolean" @@ -191,7 +187,11 @@ "type": "integer", "format": "int32" } - } + }, + "required": [ + "bar", + "foo" + ] }, "UnitStruct": { "type": "null" diff --git a/schemars/tests/expected/enum-adjacent-tagged.json b/schemars/tests/expected/enum-adjacent-tagged.json index efe19dc..9380c5b 100644 --- a/schemars/tests/expected/enum-adjacent-tagged.json +++ b/schemars/tests/expected/enum-adjacent-tagged.json @@ -4,9 +4,6 @@ "oneOf": [ { "type": "object", - "required": [ - "t" - ], "properties": { "t": { "type": "string", @@ -14,14 +11,13 @@ "UnitOne" ] } - } + }, + "required": [ + "t" + ] }, { "type": "object", - "required": [ - "c", - "t" - ], "properties": { "c": { "type": "object", @@ -35,14 +31,14 @@ "StringMap" ] } - } - }, - { - "type": "object", + }, "required": [ "c", "t" - ], + ] + }, + { + "type": "object", "properties": { "c": { "$ref": "#/definitions/UnitStruct" @@ -53,14 +49,14 @@ "UnitStructNewType" ] } - } - }, - { - "type": "object", + }, "required": [ "c", "t" - ], + ] + }, + { + "type": "object", "properties": { "c": { "$ref": "#/definitions/Struct" @@ -71,21 +67,17 @@ "StructNewType" ] } - } - }, - { - "type": "object", + }, "required": [ "c", "t" - ], + ] + }, + { + "type": "object", "properties": { "c": { "type": "object", - "required": [ - "bar", - "foo" - ], "properties": { "bar": { "type": "boolean" @@ -94,7 +86,11 @@ "type": "integer", "format": "int32" } - } + }, + "required": [ + "bar", + "foo" + ] }, "t": { "type": "string", @@ -102,14 +98,14 @@ "Struct" ] } - } - }, - { - "type": "object", + }, "required": [ "c", "t" - ], + ] + }, + { + "type": "object", "properties": { "c": { "type": "array", @@ -131,13 +127,14 @@ "Tuple" ] } - } + }, + "required": [ + "c", + "t" + ] }, { "type": "object", - "required": [ - "t" - ], "properties": { "t": { "type": "string", @@ -145,14 +142,13 @@ "UnitTwo" ] } - } + }, + "required": [ + "t" + ] }, { "type": "object", - "required": [ - "c", - "t" - ], "properties": { "c": { "type": "integer", @@ -164,16 +160,16 @@ "WithInt" ] } - } + }, + "required": [ + "c", + "t" + ] } ], "definitions": { "Struct": { "type": "object", - "required": [ - "bar", - "foo" - ], "properties": { "bar": { "type": "boolean" @@ -182,7 +178,11 @@ "type": "integer", "format": "int32" } - } + }, + "required": [ + "bar", + "foo" + ] }, "UnitStruct": { "type": "null" diff --git a/schemars/tests/expected/enum-external-duf.json b/schemars/tests/expected/enum-external-duf.json index b6b7b99..d492808 100644 --- a/schemars/tests/expected/enum-external-duf.json +++ b/schemars/tests/expected/enum-external-duf.json @@ -11,9 +11,6 @@ }, { "type": "object", - "required": [ - "stringMap" - ], "properties": { "stringMap": { "type": "object", @@ -22,44 +19,40 @@ } } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "stringMap" + ] }, { "type": "object", - "required": [ - "unitStructNewType" - ], "properties": { "unitStructNewType": { "$ref": "#/definitions/UnitStruct" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "unitStructNewType" + ] }, { "type": "object", - "required": [ - "structNewType" - ], "properties": { "structNewType": { "$ref": "#/definitions/Struct" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "structNewType" + ] }, { "type": "object", - "required": [ - "struct" - ], "properties": { "struct": { "type": "object", - "required": [ - "bar", - "foo" - ], "properties": { "bar": { "type": "boolean" @@ -69,16 +62,20 @@ "format": "int32" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "bar", + "foo" + ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "struct" + ] }, { "type": "object", - "required": [ - "tuple" - ], "properties": { "tuple": { "type": "array", @@ -95,29 +92,28 @@ "minItems": 2 } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "tuple" + ] }, { "type": "object", - "required": [ - "withInt" - ], "properties": { "withInt": { "type": "integer", "format": "int32" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "withInt" + ] } ], "definitions": { "Struct": { "type": "object", - "required": [ - "bar", - "foo" - ], "properties": { "bar": { "type": "boolean" @@ -126,7 +122,11 @@ "type": "integer", "format": "int32" } - } + }, + "required": [ + "bar", + "foo" + ] }, "UnitStruct": { "type": "null" diff --git a/schemars/tests/expected/enum-external.json b/schemars/tests/expected/enum-external.json index cc721df..b09f107 100644 --- a/schemars/tests/expected/enum-external.json +++ b/schemars/tests/expected/enum-external.json @@ -11,9 +11,6 @@ }, { "type": "object", - "required": [ - "stringMap" - ], "properties": { "stringMap": { "type": "object", @@ -22,44 +19,40 @@ } } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "stringMap" + ] }, { "type": "object", - "required": [ - "unitStructNewType" - ], "properties": { "unitStructNewType": { "$ref": "#/definitions/UnitStruct" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "unitStructNewType" + ] }, { "type": "object", - "required": [ - "structNewType" - ], "properties": { "structNewType": { "$ref": "#/definitions/Struct" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "structNewType" + ] }, { "type": "object", - "required": [ - "struct" - ], "properties": { "struct": { "type": "object", - "required": [ - "bar", - "foo" - ], "properties": { "bar": { "type": "boolean" @@ -68,16 +61,20 @@ "type": "integer", "format": "int32" } - } + }, + "required": [ + "bar", + "foo" + ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "struct" + ] }, { "type": "object", - "required": [ - "tuple" - ], "properties": { "tuple": { "type": "array", @@ -94,29 +91,28 @@ "minItems": 2 } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "tuple" + ] }, { "type": "object", - "required": [ - "withInt" - ], "properties": { "withInt": { "type": "integer", "format": "int32" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "withInt" + ] } ], "definitions": { "Struct": { "type": "object", - "required": [ - "bar", - "foo" - ], "properties": { "bar": { "type": "boolean" @@ -125,7 +121,11 @@ "type": "integer", "format": "int32" } - } + }, + "required": [ + "bar", + "foo" + ] }, "UnitStruct": { "type": "null" diff --git a/schemars/tests/expected/enum-internal-duf.json b/schemars/tests/expected/enum-internal-duf.json index 7a9bcd7..6db4d31 100644 --- a/schemars/tests/expected/enum-internal-duf.json +++ b/schemars/tests/expected/enum-internal-duf.json @@ -4,9 +4,6 @@ "oneOf": [ { "type": "object", - "required": [ - "typeProperty" - ], "properties": { "typeProperty": { "type": "string", @@ -15,13 +12,13 @@ ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "typeProperty" + ] }, { "type": "object", - "required": [ - "typeProperty" - ], "properties": { "typeProperty": { "type": "string", @@ -32,13 +29,13 @@ }, "additionalProperties": { "type": "string" - } + }, + "required": [ + "typeProperty" + ] }, { "type": "object", - "required": [ - "typeProperty" - ], "properties": { "typeProperty": { "type": "string", @@ -47,15 +44,13 @@ ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "typeProperty" + ] }, { "type": "object", - "required": [ - "bar", - "foo", - "typeProperty" - ], "properties": { "bar": { "type": "boolean" @@ -70,15 +65,15 @@ "StructNewType" ] } - } - }, - { - "type": "object", + }, "required": [ "bar", "foo", "typeProperty" - ], + ] + }, + { + "type": "object", "properties": { "bar": { "type": "boolean" @@ -94,13 +89,15 @@ ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "bar", + "foo", + "typeProperty" + ] }, { "type": "object", - "required": [ - "typeProperty" - ], "properties": { "typeProperty": { "type": "string", @@ -109,14 +106,14 @@ ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "typeProperty" + ] }, { "type": "object", "format": "int32", - "required": [ - "typeProperty" - ], "properties": { "typeProperty": { "type": "string", @@ -124,7 +121,10 @@ "WithInt" ] } - } + }, + "required": [ + "typeProperty" + ] } ] } \ No newline at end of file diff --git a/schemars/tests/expected/enum-internal.json b/schemars/tests/expected/enum-internal.json index 115dbf3..2382f2d 100644 --- a/schemars/tests/expected/enum-internal.json +++ b/schemars/tests/expected/enum-internal.json @@ -4,9 +4,6 @@ "oneOf": [ { "type": "object", - "required": [ - "typeProperty" - ], "properties": { "typeProperty": { "type": "string", @@ -14,13 +11,13 @@ "UnitOne" ] } - } + }, + "required": [ + "typeProperty" + ] }, { "type": "object", - "required": [ - "typeProperty" - ], "properties": { "typeProperty": { "type": "string", @@ -31,13 +28,13 @@ }, "additionalProperties": { "type": "string" - } + }, + "required": [ + "typeProperty" + ] }, { "type": "object", - "required": [ - "typeProperty" - ], "properties": { "typeProperty": { "type": "string", @@ -45,15 +42,13 @@ "UnitStructNewType" ] } - } + }, + "required": [ + "typeProperty" + ] }, { "type": "object", - "required": [ - "bar", - "foo", - "typeProperty" - ], "properties": { "bar": { "type": "boolean" @@ -68,15 +63,15 @@ "StructNewType" ] } - } - }, - { - "type": "object", + }, "required": [ "bar", "foo", "typeProperty" - ], + ] + }, + { + "type": "object", "properties": { "bar": { "type": "boolean" @@ -91,13 +86,15 @@ "Struct" ] } - } + }, + "required": [ + "bar", + "foo", + "typeProperty" + ] }, { "type": "object", - "required": [ - "typeProperty" - ], "properties": { "typeProperty": { "type": "string", @@ -105,14 +102,14 @@ "UnitTwo" ] } - } + }, + "required": [ + "typeProperty" + ] }, { "type": "object", "format": "int32", - "required": [ - "typeProperty" - ], "properties": { "typeProperty": { "type": "string", @@ -120,7 +117,10 @@ "WithInt" ] } - } + }, + "required": [ + "typeProperty" + ] } ] } \ No newline at end of file diff --git a/schemars/tests/expected/enum-simple-internal-duf.json b/schemars/tests/expected/enum-simple-internal-duf.json index 833f7b7..9e6e5d4 100644 --- a/schemars/tests/expected/enum-simple-internal-duf.json +++ b/schemars/tests/expected/enum-simple-internal-duf.json @@ -4,9 +4,6 @@ "oneOf": [ { "type": "object", - "required": [ - "typeProperty" - ], "properties": { "typeProperty": { "type": "string", @@ -15,13 +12,13 @@ ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "typeProperty" + ] }, { "type": "object", - "required": [ - "typeProperty" - ], "properties": { "typeProperty": { "type": "string", @@ -30,13 +27,13 @@ ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "typeProperty" + ] }, { "type": "object", - "required": [ - "typeProperty" - ], "properties": { "typeProperty": { "type": "string", @@ -45,7 +42,10 @@ ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "typeProperty" + ] } ] } \ No newline at end of file diff --git a/schemars/tests/expected/enum-simple-internal.json b/schemars/tests/expected/enum-simple-internal.json index 50cd62c..a549c8c 100644 --- a/schemars/tests/expected/enum-simple-internal.json +++ b/schemars/tests/expected/enum-simple-internal.json @@ -4,9 +4,6 @@ "oneOf": [ { "type": "object", - "required": [ - "typeProperty" - ], "properties": { "typeProperty": { "type": "string", @@ -14,13 +11,13 @@ "A" ] } - } + }, + "required": [ + "typeProperty" + ] }, { "type": "object", - "required": [ - "typeProperty" - ], "properties": { "typeProperty": { "type": "string", @@ -28,13 +25,13 @@ "B" ] } - } + }, + "required": [ + "typeProperty" + ] }, { "type": "object", - "required": [ - "typeProperty" - ], "properties": { "typeProperty": { "type": "string", @@ -42,7 +39,10 @@ "C" ] } - } + }, + "required": [ + "typeProperty" + ] } ] } \ No newline at end of file diff --git a/schemars/tests/expected/enum-untagged-duf.json b/schemars/tests/expected/enum-untagged-duf.json index 24397bf..7ce7d3d 100644 --- a/schemars/tests/expected/enum-untagged-duf.json +++ b/schemars/tests/expected/enum-untagged-duf.json @@ -19,20 +19,20 @@ }, { "type": "object", - "required": [ - "bar", - "foo" - ], "properties": { + "bar": { + "type": "boolean" + }, "foo": { "type": "integer", "format": "int32" - }, - "bar": { - "type": "boolean" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "bar", + "foo" + ] }, { "type": "array", @@ -54,24 +54,24 @@ } ], "definitions": { - "UnitStruct": { - "type": "null" - }, "Struct": { "type": "object", - "required": [ - "bar", - "foo" - ], "properties": { + "bar": { + "type": "boolean" + }, "foo": { "type": "integer", "format": "int32" - }, - "bar": { - "type": "boolean" } - } + }, + "required": [ + "bar", + "foo" + ] + }, + "UnitStruct": { + "type": "null" } } } \ No newline at end of file diff --git a/schemars/tests/expected/enum-untagged.json b/schemars/tests/expected/enum-untagged.json index 7eccbe2..add81e4 100644 --- a/schemars/tests/expected/enum-untagged.json +++ b/schemars/tests/expected/enum-untagged.json @@ -19,19 +19,19 @@ }, { "type": "object", - "required": [ - "bar", - "foo" - ], "properties": { + "bar": { + "type": "boolean" + }, "foo": { "type": "integer", "format": "int32" - }, - "bar": { - "type": "boolean" } - } + }, + "required": [ + "bar", + "foo" + ] }, { "type": "array", @@ -53,24 +53,24 @@ } ], "definitions": { - "UnitStruct": { - "type": "null" - }, "Struct": { "type": "object", - "required": [ - "bar", - "foo" - ], "properties": { + "bar": { + "type": "boolean" + }, "foo": { "type": "integer", "format": "int32" - }, - "bar": { - "type": "boolean" } - } + }, + "required": [ + "bar", + "foo" + ] + }, + "UnitStruct": { + "type": "null" } } } \ No newline at end of file diff --git a/schemars/tests/expected/enumset.json b/schemars/tests/expected/enumset.json index 4950c93..b4dc26a 100644 --- a/schemars/tests/expected/enumset.json +++ b/schemars/tests/expected/enumset.json @@ -15,4 +15,4 @@ ] } } -} +} \ No newline at end of file diff --git a/schemars/tests/expected/examples.json b/schemars/tests/expected/examples.json index c02a139..9c5c5ab 100644 --- a/schemars/tests/expected/examples.json +++ b/schemars/tests/expected/examples.json @@ -1,39 +1,39 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Struct", - "examples": [ - { - "bar": false, - "baz": null, - "foo": 0 - }, - null - ], "type": "object", - "required": [ - "bar", - "foo" - ], "properties": { - "foo": { - "examples": [ - 8, - null - ], - "type": "integer", - "format": "int32" - }, "bar": { "type": "boolean" }, "baz": { - "examples": [ - null - ], "type": [ "string", "null" + ], + "examples": [ + null + ] + }, + "foo": { + "type": "integer", + "format": "int32", + "examples": [ + 8, + null ] } - } + }, + "examples": [ + { + "bar": false, + "baz": null, + "foo": 0 + }, + null + ], + "required": [ + "bar", + "foo" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/flatten.json b/schemars/tests/expected/flatten.json index fedf571..ec0aff3 100644 --- a/schemars/tests/expected/flatten.json +++ b/schemars/tests/expected/flatten.json @@ -2,27 +2,21 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Flat", "type": "object", - "required": [ - "b", - "f", - "s", - "v" - ], "properties": { + "b": { + "type": "boolean" + }, "f": { "type": "number", "format": "float" }, - "b": { - "type": "boolean" + "os": { + "type": "string", + "default": "" }, "s": { "type": "string" }, - "os": { - "default": "", - "type": "string" - }, "v": { "type": "array", "items": { @@ -30,5 +24,11 @@ "format": "int32" } } - } + }, + "required": [ + "b", + "f", + "s", + "v" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/from_json_value.json b/schemars/tests/expected/from_json_value.json index 0d94b66..cd1c634 100644 --- a/schemars/tests/expected/from_json_value.json +++ b/schemars/tests/expected/from_json_value.json @@ -1,21 +1,5 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "examples": [ - { - "bool": true, - "minusOne": -1, - "null": null, - "object": { - "array": [ - "foo", - "bar" - ] - }, - "one": 1, - "zero": 0, - "zeroPointZero": 0.0 - } - ], "type": "object", "properties": { "bool": { @@ -45,5 +29,21 @@ "zeroPointZero": { "type": "number" } - } + }, + "examples": [ + { + "bool": true, + "minusOne": -1, + "null": null, + "object": { + "array": [ + "foo", + "bar" + ] + }, + "one": 1, + "zero": 0, + "zeroPointZero": 0.0 + } + ] } \ No newline at end of file diff --git a/schemars/tests/expected/from_value_2019_09.json b/schemars/tests/expected/from_value_2019_09.json index 9cea709..4c6adcf 100644 --- a/schemars/tests/expected/from_value_2019_09.json +++ b/schemars/tests/expected/from_value_2019_09.json @@ -1,52 +1,14 @@ { "$schema": "https://json-schema.org/draft/2019-09/schema", "title": "MyStruct", - "examples": [ - { - "myBool": true, - "myInnerStruct": { - "my_empty_map": {}, - "my_empty_vec": [], - "my_map": { - "": 0.0 - }, - "my_tuple": [ - "💩", - 42 - ], - "my_vec": [ - "hello", - "world" - ] - }, - "myInt": 123, - "myNullableEnum": null - } - ], "type": "object", "properties": { - "myInt": { - "type": "integer" - }, "myBool": { "type": "boolean" }, - "myNullableEnum": true, "myInnerStruct": { "type": "object", "properties": { - "my_map": { - "type": "object", - "additionalProperties": { - "type": "number" - } - }, - "my_vec": { - "type": "array", - "items": { - "type": "string" - } - }, "my_empty_map": { "type": "object", "additionalProperties": true @@ -55,6 +17,12 @@ "type": "array", "items": true }, + "my_map": { + "type": "object", + "additionalProperties": { + "type": "number" + } + }, "my_tuple": { "type": "array", "items": [ @@ -69,8 +37,40 @@ ], "maxItems": 2, "minItems": 2 + }, + "my_vec": { + "type": "array", + "items": { + "type": "string" + } } } + }, + "myInt": { + "type": "integer" + }, + "myNullableEnum": true + }, + "examples": [ + { + "myBool": true, + "myInnerStruct": { + "my_empty_map": {}, + "my_empty_vec": [], + "my_map": { + "": 0.0 + }, + "my_tuple": [ + "💩", + 42 + ], + "my_vec": [ + "hello", + "world" + ] + }, + "myInt": 123, + "myNullableEnum": null } - } + ] } \ No newline at end of file diff --git a/schemars/tests/expected/from_value_draft07.json b/schemars/tests/expected/from_value_draft07.json index dec3996..5d87f02 100644 --- a/schemars/tests/expected/from_value_draft07.json +++ b/schemars/tests/expected/from_value_draft07.json @@ -1,52 +1,14 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct", - "examples": [ - { - "myBool": true, - "myInnerStruct": { - "my_empty_map": {}, - "my_empty_vec": [], - "my_map": { - "": 0.0 - }, - "my_tuple": [ - "💩", - 42 - ], - "my_vec": [ - "hello", - "world" - ] - }, - "myInt": 123, - "myNullableEnum": null - } - ], "type": "object", "properties": { - "myInt": { - "type": "integer" - }, "myBool": { "type": "boolean" }, - "myNullableEnum": true, "myInnerStruct": { "type": "object", "properties": { - "my_map": { - "type": "object", - "additionalProperties": { - "type": "number" - } - }, - "my_vec": { - "type": "array", - "items": { - "type": "string" - } - }, "my_empty_map": { "type": "object", "additionalProperties": true @@ -55,6 +17,12 @@ "type": "array", "items": true }, + "my_map": { + "type": "object", + "additionalProperties": { + "type": "number" + } + }, "my_tuple": { "type": "array", "items": [ @@ -69,8 +37,40 @@ ], "maxItems": 2, "minItems": 2 + }, + "my_vec": { + "type": "array", + "items": { + "type": "string" + } } } + }, + "myInt": { + "type": "integer" + }, + "myNullableEnum": true + }, + "examples": [ + { + "myBool": true, + "myInnerStruct": { + "my_empty_map": {}, + "my_empty_vec": [], + "my_map": { + "": 0.0 + }, + "my_tuple": [ + "💩", + 42 + ], + "my_vec": [ + "hello", + "world" + ] + }, + "myInt": 123, + "myNullableEnum": null } - } + ] } \ No newline at end of file diff --git a/schemars/tests/expected/from_value_openapi3.json b/schemars/tests/expected/from_value_openapi3.json index 858d286..0405935 100644 --- a/schemars/tests/expected/from_value_openapi3.json +++ b/schemars/tests/expected/from_value_openapi3.json @@ -2,26 +2,6 @@ "$schema": "https://spec.openapis.org/oas/3.0/schema/2021-09-28#/definitions/Schema", "title": "MyStruct", "type": "object", - "example": { - "myBool": true, - "myInnerStruct": { - "my_empty_map": {}, - "my_empty_vec": [], - "my_map": { - "": 0.0 - }, - "my_tuple": [ - "💩", - 42 - ], - "my_vec": [ - "hello", - "world" - ] - }, - "myInt": 123, - "myNullableEnum": null - }, "properties": { "myBool": { "type": "boolean" @@ -72,5 +52,25 @@ "myNullableEnum": { "nullable": true } + }, + "example": { + "myBool": true, + "myInnerStruct": { + "my_empty_map": {}, + "my_empty_vec": [], + "my_map": { + "": 0.0 + }, + "my_tuple": [ + "💩", + 42 + ], + "my_vec": [ + "hello", + "world" + ] + }, + "myInt": 123, + "myNullableEnum": null } } \ No newline at end of file diff --git a/schemars/tests/expected/indexmap.json b/schemars/tests/expected/indexmap.json index 98065d9..9c209e6 100644 --- a/schemars/tests/expected/indexmap.json +++ b/schemars/tests/expected/indexmap.json @@ -2,10 +2,6 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "IndexMapTypes", "type": "object", - "required": [ - "map", - "set" - ], "properties": { "map": { "type": "object", @@ -21,5 +17,9 @@ }, "uniqueItems": true } - } + }, + "required": [ + "map", + "set" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/inline-subschemas-recursive.json b/schemars/tests/expected/inline-subschemas-recursive.json index e051e5d..0c1eebe 100644 --- a/schemars/tests/expected/inline-subschemas-recursive.json +++ b/schemars/tests/expected/inline-subschemas-recursive.json @@ -18,14 +18,14 @@ "object", "null" ], - "required": [ - "recursive" - ], "properties": { "recursive": { "$ref": "#/definitions/RecursiveOuter" } - } + }, + "required": [ + "recursive" + ] } }, "definitions": { @@ -47,14 +47,14 @@ "object", "null" ], - "required": [ - "recursive" - ], "properties": { "recursive": { "$ref": "#/definitions/RecursiveOuter" } - } + }, + "required": [ + "recursive" + ] } } } diff --git a/schemars/tests/expected/inline-subschemas.json b/schemars/tests/expected/inline-subschemas.json index b731553..fe4e83e 100644 --- a/schemars/tests/expected/inline-subschemas.json +++ b/schemars/tests/expected/inline-subschemas.json @@ -2,22 +2,22 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyJob", "type": "object", - "required": [ - "spec" - ], "properties": { "spec": { "type": "object", - "required": [ - "replicas" - ], "properties": { "replicas": { "type": "integer", "format": "uint32", "minimum": 0.0 } - } + }, + "required": [ + "replicas" + ] } - } + }, + "required": [ + "spec" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/macro_built_enum.json b/schemars/tests/expected/macro_built_enum.json index d030787..51a9bb7 100644 --- a/schemars/tests/expected/macro_built_enum.json +++ b/schemars/tests/expected/macro_built_enum.json @@ -4,29 +4,29 @@ "oneOf": [ { "type": "object", - "required": [ - "InnerStruct" - ], "properties": { "InnerStruct": { "$ref": "#/definitions/InnerStruct" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "InnerStruct" + ] } ], "definitions": { "InnerStruct": { "type": "object", - "required": [ - "x" - ], "properties": { "x": { "type": "integer", "format": "int32" } - } + }, + "required": [ + "x" + ] } } } \ No newline at end of file diff --git a/schemars/tests/expected/macro_built_struct.json b/schemars/tests/expected/macro_built_struct.json index 811eae3..0c5840c 100644 --- a/schemars/tests/expected/macro_built_struct.json +++ b/schemars/tests/expected/macro_built_struct.json @@ -2,19 +2,19 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "A", "type": "object", - "required": [ - "v", - "x" - ], "properties": { + "v": { + "type": "integer", + "format": "int32" + }, "x": { "type": "integer", "format": "uint8", "minimum": 0.0 - }, - "v": { - "type": "integer", - "format": "int32" } - } + }, + "required": [ + "v", + "x" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/nonzero_ints.json b/schemars/tests/expected/nonzero_ints.json index cbfcfd9..3b83929 100644 --- a/schemars/tests/expected/nonzero_ints.json +++ b/schemars/tests/expected/nonzero_ints.json @@ -2,17 +2,13 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct", "type": "object", - "required": [ - "nonzero_signed", - "nonzero_unsigned", - "signed", - "unsigned" - ], "properties": { - "unsigned": { + "nonzero_signed": { "type": "integer", - "format": "uint32", - "minimum": 0.0 + "format": "int32", + "not": { + "const": 0 + } }, "nonzero_unsigned": { "type": "integer", @@ -23,12 +19,16 @@ "type": "integer", "format": "int32" }, - "nonzero_signed": { + "unsigned": { "type": "integer", - "format": "int32", - "not": { - "const": 0 - } + "format": "uint32", + "minimum": 0.0 } - } + }, + "required": [ + "nonzero_signed", + "nonzero_unsigned", + "signed", + "unsigned" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/os_strings.json b/schemars/tests/expected/os_strings.json index d333675..50448ed 100644 --- a/schemars/tests/expected/os_strings.json +++ b/schemars/tests/expected/os_strings.json @@ -2,26 +2,23 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "OsStrings", "type": "object", - "required": [ - "borrowed", - "owned" - ], "properties": { - "owned": { + "borrowed": { "$ref": "#/definitions/OsString" }, - "borrowed": { + "owned": { "$ref": "#/definitions/OsString" } }, + "required": [ + "borrowed", + "owned" + ], "definitions": { "OsString": { "oneOf": [ { "type": "object", - "required": [ - "Unix" - ], "properties": { "Unix": { "type": "array", @@ -31,13 +28,13 @@ "minimum": 0.0 } } - } + }, + "required": [ + "Unix" + ] }, { "type": "object", - "required": [ - "Windows" - ], "properties": { "Windows": { "type": "array", @@ -47,7 +44,10 @@ "minimum": 0.0 } } - } + }, + "required": [ + "Windows" + ] } ] } diff --git a/schemars/tests/expected/property-name-struct.json b/schemars/tests/expected/property-name-struct.json index aa708fd..1aa6e78 100644 --- a/schemars/tests/expected/property-name-struct.json +++ b/schemars/tests/expected/property-name-struct.json @@ -2,11 +2,6 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct", "type": "object", - "required": [ - "camelCase", - "new_name_1", - "new_name_2" - ], "properties": { "camelCase": { "type": "integer", @@ -20,5 +15,10 @@ "type": "integer", "format": "int32" } - } + }, + "required": [ + "camelCase", + "new_name_1", + "new_name_2" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/range.json b/schemars/tests/expected/range.json index 0bb3aeb..bb68e3a 100644 --- a/schemars/tests/expected/range.json +++ b/schemars/tests/expected/range.json @@ -2,88 +2,88 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct", "type": "object", - "required": [ - "bound", - "inclusive", - "range" - ], "properties": { - "range": { - "$ref": "#/definitions/Range_of_uint" + "bound": { + "$ref": "#/definitions/Bound_of_string" }, "inclusive": { "$ref": "#/definitions/Range_of_double" }, - "bound": { - "$ref": "#/definitions/Bound_of_string" + "range": { + "$ref": "#/definitions/Range_of_uint" } }, + "required": [ + "bound", + "inclusive", + "range" + ], "definitions": { - "Range_of_uint": { - "type": "object", - "required": [ - "end", - "start" - ], - "properties": { - "start": { - "type": "integer", - "format": "uint", - "minimum": 0.0 - }, - "end": { - "type": "integer", - "format": "uint", - "minimum": 0.0 - } - } - }, - "Range_of_double": { - "type": "object", - "required": [ - "end", - "start" - ], - "properties": { - "start": { - "type": "number", - "format": "double" - }, - "end": { - "type": "number", - "format": "double" - } - } - }, "Bound_of_string": { "oneOf": [ { "type": "object", - "required": [ - "Included" - ], "properties": { "Included": { "type": "string" } - } + }, + "required": [ + "Included" + ] }, { "type": "object", - "required": [ - "Excluded" - ], "properties": { "Excluded": { "type": "string" } - } + }, + "required": [ + "Excluded" + ] }, { "type": "string", "const": "Unbounded" } ] + }, + "Range_of_double": { + "type": "object", + "properties": { + "end": { + "type": "number", + "format": "double" + }, + "start": { + "type": "number", + "format": "double" + } + }, + "required": [ + "end", + "start" + ] + }, + "Range_of_uint": { + "type": "object", + "properties": { + "end": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + } + }, + "required": [ + "end", + "start" + ] } } } \ No newline at end of file diff --git a/schemars/tests/expected/remote_derive.json b/schemars/tests/expected/remote_derive.json index 6f09eb2..76df9b6 100644 --- a/schemars/tests/expected/remote_derive.json +++ b/schemars/tests/expected/remote_derive.json @@ -2,54 +2,54 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Process", "type": "object", - "required": [ - "command_line", - "wall_time" - ], "properties": { "command_line": { "type": "string" }, - "wall_time": { - "$ref": "#/definitions/Duration" - }, - "user_cpu_time": { - "default": { - "nanos": 0, - "secs": 0 - }, + "system_cpu_time": { "allOf": [ { "$ref": "#/definitions/Duration" } - ] + ], + "default": "0.000000000s" }, - "system_cpu_time": { - "default": "0.000000000s", + "user_cpu_time": { "allOf": [ { "$ref": "#/definitions/Duration" } - ] + ], + "default": { + "nanos": 0, + "secs": 0 + } + }, + "wall_time": { + "$ref": "#/definitions/Duration" } }, + "required": [ + "command_line", + "wall_time" + ], "definitions": { "Duration": { "type": "object", - "required": [ - "nanos", - "secs" - ], "properties": { - "secs": { - "type": "integer", - "format": "int64" - }, "nanos": { "type": "integer", "format": "int32" + }, + "secs": { + "type": "integer", + "format": "int64" } - } + }, + "required": [ + "nanos", + "secs" + ] } } } \ No newline at end of file diff --git a/schemars/tests/expected/remote_derive_generic.json b/schemars/tests/expected/remote_derive_generic.json index 5a65f3b..d06320f 100644 --- a/schemars/tests/expected/remote_derive_generic.json +++ b/schemars/tests/expected/remote_derive_generic.json @@ -2,22 +2,10 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct_for_int32", "type": "object", - "required": [ - "byte_or_bool2", - "fake_map", - "s", - "unit_or_t2" - ], "properties": { "byte_or_bool2": { "$ref": "#/definitions/Or_for_uint8_and_boolean" }, - "unit_or_t2": { - "$ref": "#/definitions/Or_for_null_and_int32" - }, - "s": { - "$ref": "#/definitions/Str" - }, "fake_map": { "type": "object", "additionalProperties": { @@ -27,29 +15,41 @@ }, "uniqueItems": true } + }, + "s": { + "$ref": "#/definitions/Str" + }, + "unit_or_t2": { + "$ref": "#/definitions/Or_for_null_and_int32" } }, + "required": [ + "byte_or_bool2", + "fake_map", + "s", + "unit_or_t2" + ], "definitions": { - "Or_for_uint8_and_boolean": { + "Or_for_null_and_int32": { "anyOf": [ { - "type": "integer", - "format": "uint8", - "minimum": 0.0 + "type": "null" }, { - "type": "boolean" + "type": "integer", + "format": "int32" } ] }, - "Or_for_null_and_int32": { + "Or_for_uint8_and_boolean": { "anyOf": [ { - "type": "null" + "type": "integer", + "format": "uint8", + "minimum": 0.0 }, { - "type": "integer", - "format": "int32" + "type": "boolean" } ] }, diff --git a/schemars/tests/expected/result.json b/schemars/tests/expected/result.json index 2a01573..44c079e 100644 --- a/schemars/tests/expected/result.json +++ b/schemars/tests/expected/result.json @@ -2,10 +2,6 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Container", "type": "object", - "required": [ - "result1", - "result2" - ], "properties": { "result1": { "$ref": "#/definitions/Result_of_MyStruct_or_Array_of_string" @@ -14,25 +10,38 @@ "$ref": "#/definitions/Result_of_boolean_or_null" } }, + "required": [ + "result1", + "result2" + ], "definitions": { + "MyStruct": { + "type": "object", + "properties": { + "foo": { + "type": "integer", + "format": "int32" + } + }, + "required": [ + "foo" + ] + }, "Result_of_MyStruct_or_Array_of_string": { "oneOf": [ { "type": "object", - "required": [ - "Ok" - ], "properties": { "Ok": { "$ref": "#/definitions/MyStruct" } - } + }, + "required": [ + "Ok" + ] }, { "type": "object", - "required": [ - "Err" - ], "properties": { "Err": { "type": "array", @@ -40,45 +49,36 @@ "type": "string" } } - } + }, + "required": [ + "Err" + ] } ] }, - "MyStruct": { - "type": "object", - "required": [ - "foo" - ], - "properties": { - "foo": { - "type": "integer", - "format": "int32" - } - } - }, "Result_of_boolean_or_null": { "oneOf": [ { "type": "object", - "required": [ - "Ok" - ], "properties": { "Ok": { "type": "boolean" } - } + }, + "required": [ + "Ok" + ] }, { "type": "object", - "required": [ - "Err" - ], "properties": { "Err": { "type": "null" } - } + }, + "required": [ + "Err" + ] } ] } diff --git a/schemars/tests/expected/same_name.json b/schemars/tests/expected/same_name.json index 9e4e6b3..ebea3fe 100644 --- a/schemars/tests/expected/same_name.json +++ b/schemars/tests/expected/same_name.json @@ -2,10 +2,6 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Config2", "type": "object", - "required": [ - "a_cfg", - "b_cfg" - ], "properties": { "a_cfg": { "$ref": "#/definitions/Config" @@ -14,28 +10,32 @@ "$ref": "#/definitions/Config2" } }, + "required": [ + "a_cfg", + "b_cfg" + ], "definitions": { "Config": { "type": "object", - "required": [ - "test" - ], "properties": { "test": { "type": "string" } - } + }, + "required": [ + "test" + ] }, "Config2": { "type": "object", - "required": [ - "test2" - ], "properties": { "test2": { "type": "string" } - } + }, + "required": [ + "test2" + ] } } } \ No newline at end of file diff --git a/schemars/tests/expected/schema-name-const-generics.json b/schemars/tests/expected/schema-name-const-generics.json index 7505b28..6bb3ab1 100644 --- a/schemars/tests/expected/schema-name-const-generics.json +++ b/schemars/tests/expected/schema-name-const-generics.json @@ -2,13 +2,13 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "const-generics-z-42", "type": "object", - "required": [ - "foo" - ], "properties": { "foo": { "type": "integer", "format": "int32" } - } + }, + "required": [ + "foo" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/schema-name-custom.json b/schemars/tests/expected/schema-name-custom.json index 844699a..4e6cc36 100644 --- a/schemars/tests/expected/schema-name-custom.json +++ b/schemars/tests/expected/schema-name-custom.json @@ -2,14 +2,10 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "a-new-name-Array_of_string-int32-int32", "type": "object", - "required": [ - "inner", - "t", - "u", - "v", - "w" - ], "properties": { + "inner": { + "$ref": "#/definitions/another-new-name" + }, "t": { "type": "integer", "format": "int32" @@ -25,23 +21,27 @@ "items": { "type": "string" } - }, - "inner": { - "$ref": "#/definitions/another-new-name" } }, + "required": [ + "inner", + "t", + "u", + "v", + "w" + ], "definitions": { "another-new-name": { "type": "object", - "required": [ - "foo" - ], "properties": { "foo": { "type": "integer", "format": "int32" } - } + }, + "required": [ + "foo" + ] } } } \ No newline at end of file diff --git a/schemars/tests/expected/schema-name-mixed-generics.json b/schemars/tests/expected/schema-name-mixed-generics.json index bddd083..0da8044 100644 --- a/schemars/tests/expected/schema-name-mixed-generics.json +++ b/schemars/tests/expected/schema-name-mixed-generics.json @@ -2,10 +2,6 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MixedGenericStruct_for_MyStruct_for_int32_and_null_and_boolean_and_Array_of_string_and_42_and_z", "type": "object", - "required": [ - "foo", - "generic" - ], "properties": { "foo": { "type": "integer", @@ -15,28 +11,25 @@ "$ref": "#/definitions/MyStruct_for_int32_and_null_and_boolean_and_Array_of_string" } }, + "required": [ + "foo", + "generic" + ], "definitions": { "MySimpleStruct": { "type": "object", - "required": [ - "foo" - ], "properties": { "foo": { "type": "integer", "format": "int32" } - } + }, + "required": [ + "foo" + ] }, "MyStruct_for_int32_and_null_and_boolean_and_Array_of_string": { "type": "object", - "required": [ - "inner", - "t", - "u", - "v", - "w" - ], "properties": { "inner": { "$ref": "#/definitions/MySimpleStruct" @@ -57,7 +50,14 @@ "type": "string" } } - } + }, + "required": [ + "inner", + "t", + "u", + "v", + "w" + ] } } } \ No newline at end of file diff --git a/schemars/tests/expected/schema_settings-2019_09.json b/schemars/tests/expected/schema_settings-2019_09.json index fb936a7..4e18426 100644 --- a/schemars/tests/expected/schema_settings-2019_09.json +++ b/schemars/tests/expected/schema_settings-2019_09.json @@ -2,25 +2,7 @@ "$schema": "https://json-schema.org/draft/2019-09/schema", "title": "Outer", "type": "object", - "required": [ - "int", - "value", - "values" - ], "properties": { - "int": { - "examples": [ - 8, - null - ], - "type": "integer", - "format": "int32" - }, - "values": { - "type": "object", - "additionalProperties": true - }, - "value": true, "inner": { "anyOf": [ { @@ -30,8 +12,26 @@ "type": "null" } ] + }, + "int": { + "type": "integer", + "format": "int32", + "examples": [ + 8, + null + ] + }, + "value": true, + "values": { + "type": "object", + "additionalProperties": true } }, + "required": [ + "int", + "value", + "values" + ], "definitions": { "Inner": { "oneOf": [ @@ -51,13 +51,13 @@ }, { "type": "object", - "required": [ - "ValueNewType" - ], "properties": { "ValueNewType": true }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "ValueNewType" + ] } ] } diff --git a/schemars/tests/expected/schema_settings-openapi3.json b/schemars/tests/expected/schema_settings-openapi3.json index 15b752d..5318b01 100644 --- a/schemars/tests/expected/schema_settings-openapi3.json +++ b/schemars/tests/expected/schema_settings-openapi3.json @@ -46,10 +46,10 @@ }, { "type": "object", - "additionalProperties": false, "properties": { "ValueNewType": {} }, + "additionalProperties": false, "required": [ "ValueNewType" ] diff --git a/schemars/tests/expected/schema_settings.json b/schemars/tests/expected/schema_settings.json index c876d6f..8e70c7c 100644 --- a/schemars/tests/expected/schema_settings.json +++ b/schemars/tests/expected/schema_settings.json @@ -2,25 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Outer", "type": "object", - "required": [ - "int", - "value", - "values" - ], "properties": { - "int": { - "examples": [ - 8, - null - ], - "type": "integer", - "format": "int32" - }, - "values": { - "type": "object", - "additionalProperties": true - }, - "value": true, "inner": { "anyOf": [ { @@ -30,8 +12,26 @@ "type": "null" } ] + }, + "int": { + "type": "integer", + "format": "int32", + "examples": [ + 8, + null + ] + }, + "value": true, + "values": { + "type": "object", + "additionalProperties": true } }, + "required": [ + "int", + "value", + "values" + ], "definitions": { "Inner": { "oneOf": [ @@ -51,13 +51,13 @@ }, { "type": "object", - "required": [ - "ValueNewType" - ], "properties": { "ValueNewType": true }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "ValueNewType" + ] } ] } diff --git a/schemars/tests/expected/schema_with-enum-adjacent-tagged.json b/schemars/tests/expected/schema_with-enum-adjacent-tagged.json index 3ecba4c..c29c59e 100644 --- a/schemars/tests/expected/schema_with-enum-adjacent-tagged.json +++ b/schemars/tests/expected/schema_with-enum-adjacent-tagged.json @@ -4,21 +4,17 @@ "oneOf": [ { "type": "object", - "required": [ - "c", - "t" - ], "properties": { "c": { "type": "object", - "required": [ - "foo" - ], "properties": { "foo": { "type": "boolean" } - } + }, + "required": [ + "foo" + ] }, "t": { "type": "string", @@ -26,14 +22,14 @@ "Struct" ] } - } - }, - { - "type": "object", + }, "required": [ "c", "t" - ], + ] + }, + { + "type": "object", "properties": { "c": { "type": "boolean" @@ -44,14 +40,14 @@ "NewType" ] } - } - }, - { - "type": "object", + }, "required": [ "c", "t" - ], + ] + }, + { + "type": "object", "properties": { "c": { "type": "array", @@ -73,14 +69,14 @@ "Tuple" ] } - } - }, - { - "type": "object", + }, "required": [ "c", "t" - ], + ] + }, + { + "type": "object", "properties": { "c": { "type": "boolean" @@ -91,7 +87,11 @@ "Unit" ] } - } + }, + "required": [ + "c", + "t" + ] } ] } \ No newline at end of file diff --git a/schemars/tests/expected/schema_with-enum-external.json b/schemars/tests/expected/schema_with-enum-external.json index dea0219..78b6475 100644 --- a/schemars/tests/expected/schema_with-enum-external.json +++ b/schemars/tests/expected/schema_with-enum-external.json @@ -4,41 +4,38 @@ "oneOf": [ { "type": "object", - "required": [ - "struct" - ], "properties": { "struct": { "type": "object", - "required": [ - "foo" - ], "properties": { "foo": { "type": "boolean" } - } + }, + "required": [ + "foo" + ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "struct" + ] }, { "type": "object", - "required": [ - "newType" - ], "properties": { "newType": { "type": "boolean" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "newType" + ] }, { "type": "object", - "required": [ - "tuple" - ], "properties": { "tuple": { "type": "array", @@ -55,19 +52,22 @@ "minItems": 2 } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "tuple" + ] }, { "type": "object", - "required": [ - "unit" - ], "properties": { "unit": { "type": "boolean" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "unit" + ] } ] } \ No newline at end of file diff --git a/schemars/tests/expected/schema_with-enum-internal.json b/schemars/tests/expected/schema_with-enum-internal.json index c4a0cc1..8b6fbeb 100644 --- a/schemars/tests/expected/schema_with-enum-internal.json +++ b/schemars/tests/expected/schema_with-enum-internal.json @@ -4,10 +4,6 @@ "oneOf": [ { "type": "object", - "required": [ - "foo", - "typeProperty" - ], "properties": { "foo": { "type": "boolean" @@ -18,13 +14,14 @@ "Struct" ] } - } + }, + "required": [ + "foo", + "typeProperty" + ] }, { "type": "object", - "required": [ - "typeProperty" - ], "properties": { "typeProperty": { "type": "string", @@ -32,13 +29,13 @@ "NewType" ] } - } + }, + "required": [ + "typeProperty" + ] }, { "type": "object", - "required": [ - "typeProperty" - ], "properties": { "typeProperty": { "type": "string", @@ -46,7 +43,10 @@ "Unit" ] } - } + }, + "required": [ + "typeProperty" + ] } ] } \ No newline at end of file diff --git a/schemars/tests/expected/schema_with-enum-untagged.json b/schemars/tests/expected/schema_with-enum-untagged.json index 55147de..b912feb 100644 --- a/schemars/tests/expected/schema_with-enum-untagged.json +++ b/schemars/tests/expected/schema_with-enum-untagged.json @@ -4,14 +4,14 @@ "anyOf": [ { "type": "object", - "required": [ - "foo" - ], "properties": { "foo": { "type": "boolean" } - } + }, + "required": [ + "foo" + ] }, { "type": "boolean" diff --git a/schemars/tests/expected/schema_with-struct.json b/schemars/tests/expected/schema_with-struct.json index feb5b51..40b5c56 100644 --- a/schemars/tests/expected/schema_with-struct.json +++ b/schemars/tests/expected/schema_with-struct.json @@ -2,21 +2,21 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Struct", "type": "object", - "required": [ - "bar", - "baz", - "foo" - ], "properties": { - "foo": { - "type": "boolean" - }, "bar": { "type": "integer", "format": "int32" }, "baz": { "type": "boolean" + }, + "foo": { + "type": "boolean" } - } + }, + "required": [ + "bar", + "baz", + "foo" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/semver.json b/schemars/tests/expected/semver.json index d87ad04..e8c59b9 100644 --- a/schemars/tests/expected/semver.json +++ b/schemars/tests/expected/semver.json @@ -2,11 +2,13 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "SemverTypes", "type": "object", - "required": ["version"], "properties": { "version": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" } - } -} + }, + "required": [ + "version" + ] +} \ No newline at end of file diff --git a/schemars/tests/expected/skip_enum_variants.json b/schemars/tests/expected/skip_enum_variants.json index ba1bf23..b955e77 100644 --- a/schemars/tests/expected/skip_enum_variants.json +++ b/schemars/tests/expected/skip_enum_variants.json @@ -10,16 +10,16 @@ }, { "type": "object", - "required": [ - "Included1" - ], "properties": { "Included1": { "type": "number", "format": "float" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "Included1" + ] } ] } \ No newline at end of file diff --git a/schemars/tests/expected/skip_struct_fields.json b/schemars/tests/expected/skip_struct_fields.json index d488561..21e01a6 100644 --- a/schemars/tests/expected/skip_struct_fields.json +++ b/schemars/tests/expected/skip_struct_fields.json @@ -2,23 +2,23 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct", "type": "object", - "required": [ - "included", - "writable" - ], "properties": { + "included": { + "type": "null" + }, "readable": { + "type": "string", "default": "", - "readOnly": true, - "type": "string" + "readOnly": true }, "writable": { - "writeOnly": true, "type": "number", - "format": "float" - }, - "included": { - "type": "null" + "format": "float", + "writeOnly": true } - } + }, + "required": [ + "included", + "writable" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/struct-normal-additional-properties.json b/schemars/tests/expected/struct-normal-additional-properties.json index 1a8d749..ed8602a 100644 --- a/schemars/tests/expected/struct-normal-additional-properties.json +++ b/schemars/tests/expected/struct-normal-additional-properties.json @@ -2,15 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Struct", "type": "object", - "required": [ - "bar", - "foo" - ], "properties": { - "foo": { - "type": "integer", - "format": "int32" - }, "bar": { "type": "boolean" }, @@ -19,7 +11,15 @@ "string", "null" ] + }, + "foo": { + "type": "integer", + "format": "int32" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "bar", + "foo" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/struct-normal.json b/schemars/tests/expected/struct-normal.json index cee25b1..e6fe68b 100644 --- a/schemars/tests/expected/struct-normal.json +++ b/schemars/tests/expected/struct-normal.json @@ -2,15 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Struct", "type": "object", - "required": [ - "bar", - "foo" - ], "properties": { - "foo": { - "type": "integer", - "format": "int32" - }, "bar": { "type": "boolean" }, @@ -19,6 +11,14 @@ "string", "null" ] + }, + "foo": { + "type": "integer", + "format": "int32" } - } + }, + "required": [ + "bar", + "foo" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/url.json b/schemars/tests/expected/url.json index ddb8c5a..5722f9e 100644 --- a/schemars/tests/expected/url.json +++ b/schemars/tests/expected/url.json @@ -2,13 +2,13 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "UrlTypes", "type": "object", - "required": [ - "url" - ], "properties": { "url": { "type": "string", "format": "uri" } - } -} + }, + "required": [ + "url" + ] +} \ No newline at end of file diff --git a/schemars/tests/expected/validate.json b/schemars/tests/expected/validate.json index d4a14e3..38aabcc 100644 --- a/schemars/tests/expected/validate.json +++ b/schemars/tests/expected/validate.json @@ -2,49 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Struct", "type": "object", - "required": [ - "contains_str1", - "contains_str2", - "email_address", - "homepage", - "map_contains", - "min_max", - "min_max2", - "non_empty_str", - "non_empty_str2", - "pair", - "regex_str1", - "regex_str2", - "regex_str3", - "required_option", - "tel", - "x" - ], "properties": { - "min_max": { - "type": "number", - "format": "float", - "maximum": 100.0, - "minimum": 0.01 - }, - "min_max2": { - "type": "number", - "format": "float", - "maximum": 1000.0, - "minimum": 1.0 - }, - "regex_str1": { - "type": "string", - "pattern": "^[Hh]ello\\b" - }, - "regex_str2": { - "type": "string", - "pattern": "^[Hh]ello\\b" - }, - "regex_str3": { - "type": "string", - "pattern": "^\\d+$" - }, "contains_str1": { "type": "string", "pattern": "substring\\.\\.\\." @@ -57,14 +15,31 @@ "type": "string", "format": "email" }, - "tel": { - "type": "string", - "format": "phone" - }, "homepage": { "type": "string", "format": "uri" }, + "map_contains": { + "type": "object", + "additionalProperties": { + "type": "null" + }, + "required": [ + "map_key" + ] + }, + "min_max": { + "type": "number", + "format": "float", + "maximum": 100.0, + "minimum": 0.01 + }, + "min_max2": { + "type": "number", + "format": "float", + "maximum": 1000.0, + "minimum": 1.0 + }, "non_empty_str": { "type": "string", "maxLength": 100, @@ -84,21 +59,46 @@ "maxItems": 2, "minItems": 2 }, - "map_contains": { - "type": "object", - "required": [ - "map_key" - ], - "additionalProperties": { - "type": "null" - } + "regex_str1": { + "type": "string", + "pattern": "^[Hh]ello\\b" + }, + "regex_str2": { + "type": "string", + "pattern": "^[Hh]ello\\b" + }, + "regex_str3": { + "type": "string", + "pattern": "^\\d+$" }, "required_option": { "type": "boolean" }, + "tel": { + "type": "string", + "format": "phone" + }, "x": { "type": "integer", "format": "int32" } - } + }, + "required": [ + "contains_str1", + "contains_str2", + "email_address", + "homepage", + "map_contains", + "min_max", + "min_max2", + "non_empty_str", + "non_empty_str2", + "pair", + "regex_str1", + "regex_str2", + "regex_str3", + "required_option", + "tel", + "x" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/validate_inner.json b/schemars/tests/expected/validate_inner.json index 443a9fa..ffe6792 100644 --- a/schemars/tests/expected/validate_inner.json +++ b/schemars/tests/expected/validate_inner.json @@ -2,15 +2,6 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Struct", "type": "object", - "required": [ - "array_str_length", - "slice_str_contains", - "vec_i32_range", - "vec_str_length", - "vec_str_length2", - "vec_str_regex", - "vec_str_url" - ], "properties": { "array_str_length": { "type": "array", @@ -70,5 +61,14 @@ "format": "uri" } } - } + }, + "required": [ + "array_str_length", + "slice_str_contains", + "vec_i32_range", + "vec_str_length", + "vec_str_length2", + "vec_str_regex", + "vec_str_url" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/validate_schemars_attrs.json b/schemars/tests/expected/validate_schemars_attrs.json index 86658ac..438b4aa 100644 --- a/schemars/tests/expected/validate_schemars_attrs.json +++ b/schemars/tests/expected/validate_schemars_attrs.json @@ -2,49 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Struct2", "type": "object", - "required": [ - "contains_str1", - "contains_str2", - "email_address", - "homepage", - "map_contains", - "min_max", - "min_max2", - "non_empty_str", - "non_empty_str2", - "pair", - "regex_str1", - "regex_str2", - "regex_str3", - "required_option", - "tel", - "x" - ], "properties": { - "min_max": { - "type": "number", - "format": "float", - "maximum": 100.0, - "minimum": 0.01 - }, - "min_max2": { - "type": "number", - "format": "float", - "maximum": 1000.0, - "minimum": 1.0 - }, - "regex_str1": { - "type": "string", - "pattern": "^[Hh]ello\\b" - }, - "regex_str2": { - "type": "string", - "pattern": "^[Hh]ello\\b" - }, - "regex_str3": { - "type": "string", - "pattern": "^\\d+$" - }, "contains_str1": { "type": "string", "pattern": "substring\\.\\.\\." @@ -57,14 +15,31 @@ "type": "string", "format": "email" }, - "tel": { - "type": "string", - "format": "phone" - }, "homepage": { "type": "string", "format": "uri" }, + "map_contains": { + "type": "object", + "additionalProperties": { + "type": "null" + }, + "required": [ + "map_key" + ] + }, + "min_max": { + "type": "number", + "format": "float", + "maximum": 100.0, + "minimum": 0.01 + }, + "min_max2": { + "type": "number", + "format": "float", + "maximum": 1000.0, + "minimum": 1.0 + }, "non_empty_str": { "type": "string", "maxLength": 100, @@ -84,21 +59,46 @@ "maxItems": 2, "minItems": 2 }, - "map_contains": { - "type": "object", - "required": [ - "map_key" - ], - "additionalProperties": { - "type": "null" - } + "regex_str1": { + "type": "string", + "pattern": "^[Hh]ello\\b" + }, + "regex_str2": { + "type": "string", + "pattern": "^[Hh]ello\\b" + }, + "regex_str3": { + "type": "string", + "pattern": "^\\d+$" }, "required_option": { "type": "boolean" }, + "tel": { + "type": "string", + "format": "phone" + }, "x": { "type": "integer", "format": "int32" } - } + }, + "required": [ + "contains_str1", + "contains_str2", + "email_address", + "homepage", + "map_contains", + "min_max", + "min_max2", + "non_empty_str", + "non_empty_str2", + "pair", + "regex_str1", + "regex_str2", + "regex_str3", + "required_option", + "tel", + "x" + ] } \ No newline at end of file From 593c3fcf76c742f89bff3c9572fad636d7f6e1bb Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Mon, 13 May 2024 18:33:55 +0100 Subject: [PATCH 07/31] Regenerate example schemas --- .../examples/custom_serialization.rs | 10 +++-- .../examples/custom_serialization.schema.json | 16 ++++---- .../examples/custom_settings.schema.json | 32 +++++++-------- .../examples/doc_comments.schema.json | 32 +++++++-------- .../_includes/examples/from_value.schema.json | 20 +++++----- docs/_includes/examples/main.schema.json | 32 +++++++-------- .../examples/remote_derive.schema.json | 20 +++++----- .../examples/schemars_attrs.schema.json | 26 ++++++------ .../examples/serde_attrs.schema.json | 20 +++++----- docs/_includes/examples/validate.schema.json | 40 +++++++++---------- .../examples/custom_serialization.schema.json | 16 ++++---- schemars/examples/custom_settings.schema.json | 32 +++++++-------- schemars/examples/doc_comments.schema.json | 32 +++++++-------- schemars/examples/from_value.schema.json | 20 +++++----- schemars/examples/main.schema.json | 32 +++++++-------- schemars/examples/remote_derive.schema.json | 20 +++++----- schemars/examples/schemars_attrs.schema.json | 26 ++++++------ schemars/examples/serde_attrs.schema.json | 20 +++++----- schemars/examples/validate.schema.json | 40 +++++++++---------- 19 files changed, 244 insertions(+), 242 deletions(-) diff --git a/docs/_includes/examples/custom_serialization.rs b/docs/_includes/examples/custom_serialization.rs index 53c78fa..c119ea5 100644 --- a/docs/_includes/examples/custom_serialization.rs +++ b/docs/_includes/examples/custom_serialization.rs @@ -1,4 +1,4 @@ -use schemars::schema::{Schema, SchemaObject}; +use schemars::Schema; use schemars::{gen::SchemaGenerator, schema_for, JsonSchema}; use serde::{Deserialize, Serialize}; @@ -21,9 +21,11 @@ pub struct MyStruct { } fn make_custom_schema(gen: &mut SchemaGenerator) -> Schema { - let mut schema: SchemaObject = ::json_schema(gen).into(); - schema.format = Some("boolean".to_owned()); - schema.into() + let mut schema = String::json_schema(gen); + schema + .ensure_object() + .insert("format".into(), "boolean".into()); + schema } fn eight() -> i32 { diff --git a/docs/_includes/examples/custom_serialization.schema.json b/docs/_includes/examples/custom_serialization.schema.json index 42fda99..1475c25 100644 --- a/docs/_includes/examples/custom_serialization.schema.json +++ b/docs/_includes/examples/custom_serialization.schema.json @@ -4,22 +4,22 @@ "type": "object", "properties": { "bool_as_string": { - "default": "false", "type": "string", - "format": "boolean" + "format": "boolean", + "default": "false" }, "bool_normal": { - "default": false, - "type": "boolean" + "type": "boolean", + "default": false }, "int_as_string": { - "default": "8", - "type": "string" + "type": "string", + "default": "8" }, "int_normal": { - "default": 8, "type": "integer", - "format": "int32" + "format": "int32", + "default": 8 } } } diff --git a/docs/_includes/examples/custom_settings.schema.json b/docs/_includes/examples/custom_settings.schema.json index 12ac7d5..8da4348 100644 --- a/docs/_includes/examples/custom_settings.schema.json +++ b/docs/_includes/examples/custom_settings.schema.json @@ -2,10 +2,6 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct", "type": "object", - "required": [ - "my_bool", - "my_int" - ], "properties": { "my_bool": { "type": "boolean" @@ -23,32 +19,30 @@ "nullable": true } }, + "required": [ + "my_int", + "my_bool" + ], "definitions": { "MyEnum": { "oneOf": [ { "type": "object", - "required": [ - "StringNewType" - ], "properties": { "StringNewType": { "type": "string" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "StringNewType" + ] }, { "type": "object", - "required": [ - "StructVariant" - ], "properties": { "StructVariant": { "type": "object", - "required": [ - "floats" - ], "properties": { "floats": { "type": "array", @@ -57,10 +51,16 @@ "format": "float" } } - } + }, + "required": [ + "floats" + ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "StructVariant" + ] } ] } diff --git a/docs/_includes/examples/doc_comments.schema.json b/docs/_includes/examples/doc_comments.schema.json index 121cdb4..501b79c 100644 --- a/docs/_includes/examples/doc_comments.schema.json +++ b/docs/_includes/examples/doc_comments.schema.json @@ -3,10 +3,6 @@ "title": "My Amazing Struct", "description": "This struct shows off generating a schema with a custom title and description.", "type": "object", - "required": [ - "my_bool", - "my_int" - ], "properties": { "my_bool": { "description": "This bool has a description, but no title.", @@ -30,6 +26,10 @@ ] } }, + "required": [ + "my_int", + "my_bool" + ], "definitions": { "MyEnum": { "title": "My Amazing Enum", @@ -37,28 +37,22 @@ { "description": "A wrapper around a `String`", "type": "object", - "required": [ - "StringNewType" - ], "properties": { "StringNewType": { "type": "string" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "StringNewType" + ] }, { "description": "A struct-like enum variant which contains some floats", "type": "object", - "required": [ - "StructVariant" - ], "properties": { "StructVariant": { "type": "object", - "required": [ - "floats" - ], "properties": { "floats": { "description": "The floats themselves", @@ -68,10 +62,16 @@ "format": "float" } } - } + }, + "required": [ + "floats" + ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "StructVariant" + ] } ] } diff --git a/docs/_includes/examples/from_value.schema.json b/docs/_includes/examples/from_value.schema.json index 4ba7735..237a90e 100644 --- a/docs/_includes/examples/from_value.schema.json +++ b/docs/_includes/examples/from_value.schema.json @@ -1,15 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct", - "examples": [ - { - "my_bool": true, - "my_int": 123, - "my_nullable_enum": { - "StringNewType": "foo" - } - } - ], "type": "object", "properties": { "my_bool": { @@ -19,5 +10,14 @@ "type": "integer" }, "my_nullable_enum": true - } + }, + "examples": [ + { + "my_bool": true, + "my_int": 123, + "my_nullable_enum": { + "StringNewType": "foo" + } + } + ] } diff --git a/docs/_includes/examples/main.schema.json b/docs/_includes/examples/main.schema.json index ddbd9d3..bcfe137 100644 --- a/docs/_includes/examples/main.schema.json +++ b/docs/_includes/examples/main.schema.json @@ -2,10 +2,6 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct", "type": "object", - "required": [ - "my_bool", - "my_int" - ], "properties": { "my_bool": { "type": "boolean" @@ -25,32 +21,30 @@ ] } }, + "required": [ + "my_int", + "my_bool" + ], "definitions": { "MyEnum": { "oneOf": [ { "type": "object", - "required": [ - "StringNewType" - ], "properties": { "StringNewType": { "type": "string" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "StringNewType" + ] }, { "type": "object", - "required": [ - "StructVariant" - ], "properties": { "StructVariant": { "type": "object", - "required": [ - "floats" - ], "properties": { "floats": { "type": "array", @@ -59,10 +53,16 @@ "format": "float" } } - } + }, + "required": [ + "floats" + ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "StructVariant" + ] } ] } diff --git a/docs/_includes/examples/remote_derive.schema.json b/docs/_includes/examples/remote_derive.schema.json index e584177..b6315cb 100644 --- a/docs/_includes/examples/remote_derive.schema.json +++ b/docs/_includes/examples/remote_derive.schema.json @@ -2,11 +2,6 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Process", "type": "object", - "required": [ - "command_line", - "durations", - "wall_time" - ], "properties": { "command_line": { "type": "string" @@ -21,13 +16,14 @@ "$ref": "#/definitions/Duration" } }, + "required": [ + "command_line", + "wall_time", + "durations" + ], "definitions": { "Duration": { "type": "object", - "required": [ - "nanos", - "secs" - ], "properties": { "nanos": { "type": "integer", @@ -37,7 +33,11 @@ "type": "integer", "format": "int64" } - } + }, + "required": [ + "secs", + "nanos" + ] } } } diff --git a/docs/_includes/examples/schemars_attrs.schema.json b/docs/_includes/examples/schemars_attrs.schema.json index 9a6a22a..9637e1b 100644 --- a/docs/_includes/examples/schemars_attrs.schema.json +++ b/docs/_includes/examples/schemars_attrs.schema.json @@ -2,17 +2,11 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct", "type": "object", - "required": [ - "myBool", - "myNumber", - "myVecStr" - ], "properties": { "myBool": { "type": "boolean" }, "myNullableEnum": { - "default": null, "anyOf": [ { "$ref": "#/definitions/MyEnum" @@ -20,13 +14,14 @@ { "type": "null" } - ] + ], + "default": null }, "myNumber": { "type": "integer", "format": "int32", - "maximum": 10.0, - "minimum": 1.0 + "maximum": 10, + "minimum": 1 }, "myVecStr": { "type": "array", @@ -37,6 +32,11 @@ } }, "additionalProperties": false, + "required": [ + "myNumber", + "myBool", + "myVecStr" + ], "definitions": { "MyEnum": { "anyOf": [ @@ -46,9 +46,6 @@ }, { "type": "object", - "required": [ - "floats" - ], "properties": { "floats": { "type": "array", @@ -59,7 +56,10 @@ "maxItems": 100, "minItems": 1 } - } + }, + "required": [ + "floats" + ] } ] } diff --git a/docs/_includes/examples/serde_attrs.schema.json b/docs/_includes/examples/serde_attrs.schema.json index d044193..9bb2f82 100644 --- a/docs/_includes/examples/serde_attrs.schema.json +++ b/docs/_includes/examples/serde_attrs.schema.json @@ -2,16 +2,11 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct", "type": "object", - "required": [ - "myBool", - "myNumber" - ], "properties": { "myBool": { "type": "boolean" }, "myNullableEnum": { - "default": null, "anyOf": [ { "$ref": "#/definitions/MyEnum" @@ -19,7 +14,8 @@ { "type": "null" } - ] + ], + "default": null }, "myNumber": { "type": "integer", @@ -27,6 +23,10 @@ } }, "additionalProperties": false, + "required": [ + "myNumber", + "myBool" + ], "definitions": { "MyEnum": { "anyOf": [ @@ -35,9 +35,6 @@ }, { "type": "object", - "required": [ - "floats" - ], "properties": { "floats": { "type": "array", @@ -46,7 +43,10 @@ "format": "float" } } - } + }, + "required": [ + "floats" + ] } ] } diff --git a/docs/_includes/examples/validate.schema.json b/docs/_includes/examples/validate.schema.json index 1e45a96..308f631 100644 --- a/docs/_includes/examples/validate.schema.json +++ b/docs/_includes/examples/validate.schema.json @@ -2,11 +2,6 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct", "type": "object", - "required": [ - "my_bool", - "my_int", - "my_nullable_enum" - ], "properties": { "my_bool": { "type": "boolean" @@ -14,35 +9,29 @@ "my_int": { "type": "integer", "format": "int32", - "maximum": 10.0, - "minimum": 1.0 + "maximum": 10, + "minimum": 1 }, "my_nullable_enum": { "oneOf": [ { "type": "object", - "required": [ - "StringNewType" - ], "properties": { "StringNewType": { "type": "string", "format": "phone" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "StringNewType" + ] }, { "type": "object", - "required": [ - "StructVariant" - ], "properties": { "StructVariant": { "type": "object", - "required": [ - "floats" - ], "properties": { "floats": { "type": "array", @@ -53,12 +42,23 @@ "maxItems": 100, "minItems": 1 } - } + }, + "required": [ + "floats" + ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "StructVariant" + ] } ] } - } + }, + "required": [ + "my_int", + "my_bool", + "my_nullable_enum" + ] } diff --git a/schemars/examples/custom_serialization.schema.json b/schemars/examples/custom_serialization.schema.json index 42fda99..1475c25 100644 --- a/schemars/examples/custom_serialization.schema.json +++ b/schemars/examples/custom_serialization.schema.json @@ -4,22 +4,22 @@ "type": "object", "properties": { "bool_as_string": { - "default": "false", "type": "string", - "format": "boolean" + "format": "boolean", + "default": "false" }, "bool_normal": { - "default": false, - "type": "boolean" + "type": "boolean", + "default": false }, "int_as_string": { - "default": "8", - "type": "string" + "type": "string", + "default": "8" }, "int_normal": { - "default": 8, "type": "integer", - "format": "int32" + "format": "int32", + "default": 8 } } } diff --git a/schemars/examples/custom_settings.schema.json b/schemars/examples/custom_settings.schema.json index 12ac7d5..8da4348 100644 --- a/schemars/examples/custom_settings.schema.json +++ b/schemars/examples/custom_settings.schema.json @@ -2,10 +2,6 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct", "type": "object", - "required": [ - "my_bool", - "my_int" - ], "properties": { "my_bool": { "type": "boolean" @@ -23,32 +19,30 @@ "nullable": true } }, + "required": [ + "my_int", + "my_bool" + ], "definitions": { "MyEnum": { "oneOf": [ { "type": "object", - "required": [ - "StringNewType" - ], "properties": { "StringNewType": { "type": "string" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "StringNewType" + ] }, { "type": "object", - "required": [ - "StructVariant" - ], "properties": { "StructVariant": { "type": "object", - "required": [ - "floats" - ], "properties": { "floats": { "type": "array", @@ -57,10 +51,16 @@ "format": "float" } } - } + }, + "required": [ + "floats" + ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "StructVariant" + ] } ] } diff --git a/schemars/examples/doc_comments.schema.json b/schemars/examples/doc_comments.schema.json index 121cdb4..501b79c 100644 --- a/schemars/examples/doc_comments.schema.json +++ b/schemars/examples/doc_comments.schema.json @@ -3,10 +3,6 @@ "title": "My Amazing Struct", "description": "This struct shows off generating a schema with a custom title and description.", "type": "object", - "required": [ - "my_bool", - "my_int" - ], "properties": { "my_bool": { "description": "This bool has a description, but no title.", @@ -30,6 +26,10 @@ ] } }, + "required": [ + "my_int", + "my_bool" + ], "definitions": { "MyEnum": { "title": "My Amazing Enum", @@ -37,28 +37,22 @@ { "description": "A wrapper around a `String`", "type": "object", - "required": [ - "StringNewType" - ], "properties": { "StringNewType": { "type": "string" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "StringNewType" + ] }, { "description": "A struct-like enum variant which contains some floats", "type": "object", - "required": [ - "StructVariant" - ], "properties": { "StructVariant": { "type": "object", - "required": [ - "floats" - ], "properties": { "floats": { "description": "The floats themselves", @@ -68,10 +62,16 @@ "format": "float" } } - } + }, + "required": [ + "floats" + ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "StructVariant" + ] } ] } diff --git a/schemars/examples/from_value.schema.json b/schemars/examples/from_value.schema.json index 4ba7735..237a90e 100644 --- a/schemars/examples/from_value.schema.json +++ b/schemars/examples/from_value.schema.json @@ -1,15 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct", - "examples": [ - { - "my_bool": true, - "my_int": 123, - "my_nullable_enum": { - "StringNewType": "foo" - } - } - ], "type": "object", "properties": { "my_bool": { @@ -19,5 +10,14 @@ "type": "integer" }, "my_nullable_enum": true - } + }, + "examples": [ + { + "my_bool": true, + "my_int": 123, + "my_nullable_enum": { + "StringNewType": "foo" + } + } + ] } diff --git a/schemars/examples/main.schema.json b/schemars/examples/main.schema.json index ddbd9d3..bcfe137 100644 --- a/schemars/examples/main.schema.json +++ b/schemars/examples/main.schema.json @@ -2,10 +2,6 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct", "type": "object", - "required": [ - "my_bool", - "my_int" - ], "properties": { "my_bool": { "type": "boolean" @@ -25,32 +21,30 @@ ] } }, + "required": [ + "my_int", + "my_bool" + ], "definitions": { "MyEnum": { "oneOf": [ { "type": "object", - "required": [ - "StringNewType" - ], "properties": { "StringNewType": { "type": "string" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "StringNewType" + ] }, { "type": "object", - "required": [ - "StructVariant" - ], "properties": { "StructVariant": { "type": "object", - "required": [ - "floats" - ], "properties": { "floats": { "type": "array", @@ -59,10 +53,16 @@ "format": "float" } } - } + }, + "required": [ + "floats" + ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "StructVariant" + ] } ] } diff --git a/schemars/examples/remote_derive.schema.json b/schemars/examples/remote_derive.schema.json index e584177..b6315cb 100644 --- a/schemars/examples/remote_derive.schema.json +++ b/schemars/examples/remote_derive.schema.json @@ -2,11 +2,6 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Process", "type": "object", - "required": [ - "command_line", - "durations", - "wall_time" - ], "properties": { "command_line": { "type": "string" @@ -21,13 +16,14 @@ "$ref": "#/definitions/Duration" } }, + "required": [ + "command_line", + "wall_time", + "durations" + ], "definitions": { "Duration": { "type": "object", - "required": [ - "nanos", - "secs" - ], "properties": { "nanos": { "type": "integer", @@ -37,7 +33,11 @@ "type": "integer", "format": "int64" } - } + }, + "required": [ + "secs", + "nanos" + ] } } } diff --git a/schemars/examples/schemars_attrs.schema.json b/schemars/examples/schemars_attrs.schema.json index 9a6a22a..9637e1b 100644 --- a/schemars/examples/schemars_attrs.schema.json +++ b/schemars/examples/schemars_attrs.schema.json @@ -2,17 +2,11 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct", "type": "object", - "required": [ - "myBool", - "myNumber", - "myVecStr" - ], "properties": { "myBool": { "type": "boolean" }, "myNullableEnum": { - "default": null, "anyOf": [ { "$ref": "#/definitions/MyEnum" @@ -20,13 +14,14 @@ { "type": "null" } - ] + ], + "default": null }, "myNumber": { "type": "integer", "format": "int32", - "maximum": 10.0, - "minimum": 1.0 + "maximum": 10, + "minimum": 1 }, "myVecStr": { "type": "array", @@ -37,6 +32,11 @@ } }, "additionalProperties": false, + "required": [ + "myNumber", + "myBool", + "myVecStr" + ], "definitions": { "MyEnum": { "anyOf": [ @@ -46,9 +46,6 @@ }, { "type": "object", - "required": [ - "floats" - ], "properties": { "floats": { "type": "array", @@ -59,7 +56,10 @@ "maxItems": 100, "minItems": 1 } - } + }, + "required": [ + "floats" + ] } ] } diff --git a/schemars/examples/serde_attrs.schema.json b/schemars/examples/serde_attrs.schema.json index d044193..9bb2f82 100644 --- a/schemars/examples/serde_attrs.schema.json +++ b/schemars/examples/serde_attrs.schema.json @@ -2,16 +2,11 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct", "type": "object", - "required": [ - "myBool", - "myNumber" - ], "properties": { "myBool": { "type": "boolean" }, "myNullableEnum": { - "default": null, "anyOf": [ { "$ref": "#/definitions/MyEnum" @@ -19,7 +14,8 @@ { "type": "null" } - ] + ], + "default": null }, "myNumber": { "type": "integer", @@ -27,6 +23,10 @@ } }, "additionalProperties": false, + "required": [ + "myNumber", + "myBool" + ], "definitions": { "MyEnum": { "anyOf": [ @@ -35,9 +35,6 @@ }, { "type": "object", - "required": [ - "floats" - ], "properties": { "floats": { "type": "array", @@ -46,7 +43,10 @@ "format": "float" } } - } + }, + "required": [ + "floats" + ] } ] } diff --git a/schemars/examples/validate.schema.json b/schemars/examples/validate.schema.json index 1e45a96..308f631 100644 --- a/schemars/examples/validate.schema.json +++ b/schemars/examples/validate.schema.json @@ -2,11 +2,6 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MyStruct", "type": "object", - "required": [ - "my_bool", - "my_int", - "my_nullable_enum" - ], "properties": { "my_bool": { "type": "boolean" @@ -14,35 +9,29 @@ "my_int": { "type": "integer", "format": "int32", - "maximum": 10.0, - "minimum": 1.0 + "maximum": 10, + "minimum": 1 }, "my_nullable_enum": { "oneOf": [ { "type": "object", - "required": [ - "StringNewType" - ], "properties": { "StringNewType": { "type": "string", "format": "phone" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "StringNewType" + ] }, { "type": "object", - "required": [ - "StructVariant" - ], "properties": { "StructVariant": { "type": "object", - "required": [ - "floats" - ], "properties": { "floats": { "type": "array", @@ -53,12 +42,23 @@ "maxItems": 100, "minItems": 1 } - } + }, + "required": [ + "floats" + ] } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "StructVariant" + ] } ] } - } + }, + "required": [ + "my_int", + "my_bool", + "my_nullable_enum" + ] } From 78022529480902383a5934f714d957556d2ac6a8 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Mon, 13 May 2024 21:30:51 +0100 Subject: [PATCH 08/31] Use `const` instead of single-valued `enum` (#291) --- schemars/src/_private.rs | 45 ++++--------------- schemars/src/gen.rs | 1 + schemars/src/visit.rs | 18 ++++++++ schemars/tests/expected/deprecated-enum.json | 6 +-- .../tests/expected/doc_comments_enum.json | 4 +- .../tests/expected/enum-internal-duf.json | 28 +++--------- schemars/tests/expected/enum-internal.json | 28 +++--------- .../expected/enum-simple-internal-duf.json | 12 ++--- .../tests/expected/enum-simple-internal.json | 12 ++--- schemars/tests/expected/enum-unit-doc.json | 12 ++--- .../expected/schema_settings-2019_09.json | 4 +- schemars/tests/expected/schema_settings.json | 4 +- .../expected/schema_with-enum-internal.json | 12 ++--- schemars_derive/src/schema_exprs.rs | 6 +-- 14 files changed, 61 insertions(+), 131 deletions(-) diff --git a/schemars/src/_private.rs b/schemars/src/_private.rs index f3160e9..cdd5ae8 100644 --- a/schemars/src/_private.rs +++ b/schemars/src/_private.rs @@ -53,17 +53,16 @@ impl MaybeSerializeWrapper { } } -/// Create a schema for a unit enum -pub fn new_unit_enum(variant: &str) -> Schema { - // TODO switch from single-valued "enum" to "const" +/// Create a schema for a unit enum variant +pub fn new_unit_enum_variant(variant: &str) -> Schema { json_schema!({ "type": "string", - "enum": [variant], + "const": variant, }) } -/// Create a schema for an externally tagged enum -pub fn new_externally_tagged_enum(variant: &str, sub_schema: Schema) -> Schema { +/// Create a schema for an externally tagged enum variant +pub fn new_externally_tagged_enum_variant(variant: &str, sub_schema: Schema) -> Schema { json_schema!({ "type": "object", "properties": { @@ -74,7 +73,8 @@ pub fn new_externally_tagged_enum(variant: &str, sub_schema: Schema) -> Schema { }) } -pub fn apply_internal_enum_tag( +/// Update a schema for an internally tagged enum variant +pub fn apply_internal_enum_variant_tag( schema: &mut Schema, tag_name: &str, variant: &str, @@ -94,8 +94,7 @@ pub fn apply_internal_enum_tag( tag_name.to_string(), json!({ "type": "string", - // TODO switch from single-valued "enum" to "const" - "enum": [variant] + "const": variant }), ); } @@ -113,34 +112,6 @@ pub fn apply_internal_enum_tag( } } -/// Create a schema for an internally tagged enum -pub fn new_internally_tagged_enum( - tag_name: &str, - variant: &str, - deny_unknown_fields: bool, -) -> Schema { - // TODO switch from single-valued "enum" to "const" - let mut schema = json_schema!({ - "type": "object", - "properties": { - tag_name: { - "type": "string", - "enum": [variant], - } - }, - "required": [tag_name], - }); - - if deny_unknown_fields { - schema - .as_object_mut() - .unwrap() - .insert("additionalProperties".into(), false.into()); - } - - schema -} - pub fn insert_object_property( schema: &mut Schema, key: &str, diff --git a/schemars/src/gen.rs b/schemars/src/gen.rs index 17c6e16..2d7fafd 100644 --- a/schemars/src/gen.rs +++ b/schemars/src/gen.rs @@ -97,6 +97,7 @@ impl SchemaSettings { skip_additional_properties: true, }), Box::new(SetSingleExample), + Box::new(ReplaceConstValue), ], inline_subschemas: false, } diff --git a/schemars/src/visit.rs b/schemars/src/visit.rs index d228f2e..285aae2 100644 --- a/schemars/src/visit.rs +++ b/schemars/src/visit.rs @@ -170,3 +170,21 @@ impl Visitor for SetSingleExample { } } } + +/// This visitor will replace the `const` schema property with a single-valued `enum` property. +/// +/// This is useful for dialects of JSON Schema (e.g. OpenAPI 3.0) that do not support the `const` property. +#[derive(Debug, Clone)] +pub struct ReplaceConstValue; + +impl Visitor for ReplaceConstValue { + fn visit_schema(&mut self, schema: &mut Schema) { + visit_schema(self, schema); + + if let Some(obj) = schema.as_object_mut() { + if let Some(value) = obj.remove("const") { + obj.insert("enum".into(), Value::Array(vec![value])); + } + } + } +} diff --git a/schemars/tests/expected/deprecated-enum.json b/schemars/tests/expected/deprecated-enum.json index 55a98ef..dab90ec 100644 --- a/schemars/tests/expected/deprecated-enum.json +++ b/schemars/tests/expected/deprecated-enum.json @@ -11,10 +11,8 @@ }, { "type": "string", - "deprecated": true, - "enum": [ - "DeprecatedUnitVariant" - ] + "const": "DeprecatedUnitVariant", + "deprecated": true }, { "type": "object", diff --git a/schemars/tests/expected/doc_comments_enum.json b/schemars/tests/expected/doc_comments_enum.json index c983c1d..fc38db1 100644 --- a/schemars/tests/expected/doc_comments_enum.json +++ b/schemars/tests/expected/doc_comments_enum.json @@ -13,9 +13,7 @@ { "description": "This comment is included in the generated schema :)", "type": "string", - "enum": [ - "DocumentedUnit" - ] + "const": "DocumentedUnit" }, { "title": "Complex variant", diff --git a/schemars/tests/expected/enum-internal-duf.json b/schemars/tests/expected/enum-internal-duf.json index 6db4d31..758bf9b 100644 --- a/schemars/tests/expected/enum-internal-duf.json +++ b/schemars/tests/expected/enum-internal-duf.json @@ -7,9 +7,7 @@ "properties": { "typeProperty": { "type": "string", - "enum": [ - "UnitOne" - ] + "const": "UnitOne" } }, "additionalProperties": false, @@ -22,9 +20,7 @@ "properties": { "typeProperty": { "type": "string", - "enum": [ - "StringMap" - ] + "const": "StringMap" } }, "additionalProperties": { @@ -39,9 +35,7 @@ "properties": { "typeProperty": { "type": "string", - "enum": [ - "UnitStructNewType" - ] + "const": "UnitStructNewType" } }, "additionalProperties": false, @@ -61,9 +55,7 @@ }, "typeProperty": { "type": "string", - "enum": [ - "StructNewType" - ] + "const": "StructNewType" } }, "required": [ @@ -84,9 +76,7 @@ }, "typeProperty": { "type": "string", - "enum": [ - "Struct" - ] + "const": "Struct" } }, "additionalProperties": false, @@ -101,9 +91,7 @@ "properties": { "typeProperty": { "type": "string", - "enum": [ - "UnitTwo" - ] + "const": "UnitTwo" } }, "additionalProperties": false, @@ -117,9 +105,7 @@ "properties": { "typeProperty": { "type": "string", - "enum": [ - "WithInt" - ] + "const": "WithInt" } }, "required": [ diff --git a/schemars/tests/expected/enum-internal.json b/schemars/tests/expected/enum-internal.json index 2382f2d..765248f 100644 --- a/schemars/tests/expected/enum-internal.json +++ b/schemars/tests/expected/enum-internal.json @@ -7,9 +7,7 @@ "properties": { "typeProperty": { "type": "string", - "enum": [ - "UnitOne" - ] + "const": "UnitOne" } }, "required": [ @@ -21,9 +19,7 @@ "properties": { "typeProperty": { "type": "string", - "enum": [ - "StringMap" - ] + "const": "StringMap" } }, "additionalProperties": { @@ -38,9 +34,7 @@ "properties": { "typeProperty": { "type": "string", - "enum": [ - "UnitStructNewType" - ] + "const": "UnitStructNewType" } }, "required": [ @@ -59,9 +53,7 @@ }, "typeProperty": { "type": "string", - "enum": [ - "StructNewType" - ] + "const": "StructNewType" } }, "required": [ @@ -82,9 +74,7 @@ }, "typeProperty": { "type": "string", - "enum": [ - "Struct" - ] + "const": "Struct" } }, "required": [ @@ -98,9 +88,7 @@ "properties": { "typeProperty": { "type": "string", - "enum": [ - "UnitTwo" - ] + "const": "UnitTwo" } }, "required": [ @@ -113,9 +101,7 @@ "properties": { "typeProperty": { "type": "string", - "enum": [ - "WithInt" - ] + "const": "WithInt" } }, "required": [ diff --git a/schemars/tests/expected/enum-simple-internal-duf.json b/schemars/tests/expected/enum-simple-internal-duf.json index 9e6e5d4..4fdf04e 100644 --- a/schemars/tests/expected/enum-simple-internal-duf.json +++ b/schemars/tests/expected/enum-simple-internal-duf.json @@ -7,9 +7,7 @@ "properties": { "typeProperty": { "type": "string", - "enum": [ - "A" - ] + "const": "A" } }, "additionalProperties": false, @@ -22,9 +20,7 @@ "properties": { "typeProperty": { "type": "string", - "enum": [ - "B" - ] + "const": "B" } }, "additionalProperties": false, @@ -37,9 +33,7 @@ "properties": { "typeProperty": { "type": "string", - "enum": [ - "C" - ] + "const": "C" } }, "additionalProperties": false, diff --git a/schemars/tests/expected/enum-simple-internal.json b/schemars/tests/expected/enum-simple-internal.json index a549c8c..050089c 100644 --- a/schemars/tests/expected/enum-simple-internal.json +++ b/schemars/tests/expected/enum-simple-internal.json @@ -7,9 +7,7 @@ "properties": { "typeProperty": { "type": "string", - "enum": [ - "A" - ] + "const": "A" } }, "required": [ @@ -21,9 +19,7 @@ "properties": { "typeProperty": { "type": "string", - "enum": [ - "B" - ] + "const": "B" } }, "required": [ @@ -35,9 +31,7 @@ "properties": { "typeProperty": { "type": "string", - "enum": [ - "C" - ] + "const": "C" } }, "required": [ diff --git a/schemars/tests/expected/enum-unit-doc.json b/schemars/tests/expected/enum-unit-doc.json index 11a5c2a..86160e8 100644 --- a/schemars/tests/expected/enum-unit-doc.json +++ b/schemars/tests/expected/enum-unit-doc.json @@ -6,23 +6,17 @@ "title": "A deer", "description": "A female deer", "type": "string", - "enum": [ - "Do" - ] + "const": "Do" }, { "description": "A drop of golden sun", "type": "string", - "enum": [ - "Re" - ] + "const": "Re" }, { "description": "A name I call myself", "type": "string", - "enum": [ - "Mi" - ] + "const": "Mi" } ] } \ No newline at end of file diff --git a/schemars/tests/expected/schema_settings-2019_09.json b/schemars/tests/expected/schema_settings-2019_09.json index 4e18426..e51f397 100644 --- a/schemars/tests/expected/schema_settings-2019_09.json +++ b/schemars/tests/expected/schema_settings-2019_09.json @@ -45,9 +45,7 @@ { "description": "This is a documented unit variant", "type": "string", - "enum": [ - "DocumentedUnit" - ] + "const": "DocumentedUnit" }, { "type": "object", diff --git a/schemars/tests/expected/schema_settings.json b/schemars/tests/expected/schema_settings.json index 8e70c7c..07cdb0e 100644 --- a/schemars/tests/expected/schema_settings.json +++ b/schemars/tests/expected/schema_settings.json @@ -45,9 +45,7 @@ { "description": "This is a documented unit variant", "type": "string", - "enum": [ - "DocumentedUnit" - ] + "const": "DocumentedUnit" }, { "type": "object", diff --git a/schemars/tests/expected/schema_with-enum-internal.json b/schemars/tests/expected/schema_with-enum-internal.json index 8b6fbeb..45bc8e9 100644 --- a/schemars/tests/expected/schema_with-enum-internal.json +++ b/schemars/tests/expected/schema_with-enum-internal.json @@ -10,9 +10,7 @@ }, "typeProperty": { "type": "string", - "enum": [ - "Struct" - ] + "const": "Struct" } }, "required": [ @@ -25,9 +23,7 @@ "properties": { "typeProperty": { "type": "string", - "enum": [ - "NewType" - ] + "const": "NewType" } }, "required": [ @@ -39,9 +35,7 @@ "properties": { "typeProperty": { "type": "string", - "enum": [ - "Unit" - ] + "const": "Unit" } }, "required": [ diff --git a/schemars_derive/src/schema_exprs.rs b/schemars_derive/src/schema_exprs.rs index f22831e..7a1de81 100644 --- a/schemars_derive/src/schema_exprs.rs +++ b/schemars_derive/src/schema_exprs.rs @@ -197,12 +197,12 @@ fn expr_for_external_tagged_enum<'a>( let mut schema_expr = if variant.is_unit() && variant.attrs.with.is_none() { quote! { - schemars::_private::new_unit_enum(#name) + schemars::_private::new_unit_enum_variant(#name) } } else { let sub_schema = expr_for_untagged_enum_variant(variant, deny_unknown_fields); quote! { - schemars::_private::new_externally_tagged_enum(#name, #sub_schema) + schemars::_private::new_externally_tagged_enum_variant(#name, #sub_schema) } }; @@ -236,7 +236,7 @@ fn expr_for_internal_tagged_enum<'a>( quote!({ let mut schema = #schema_expr; - schemars::_private::apply_internal_enum_tag(&mut schema, #tag_name, #name, #deny_unknown_fields); + schemars::_private::apply_internal_enum_variant_tag(&mut schema, #tag_name, #name, #deny_unknown_fields); schema }) }) From 2e2328f4695c2b5c1f74746a3ed186aa98364adf Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Mon, 13 May 2024 22:02:30 +0100 Subject: [PATCH 09/31] Refactor `flatten` and move it to `_private`, remove `TempFixupForTests`, regenerate test schemas --- schemars/src/_private.rs | 60 ++++++++++ schemars/src/flatten.rs | 71 ----------- schemars/src/lib.rs | 1 - schemars/tests/expected/bytes.json | 4 +- schemars/tests/expected/chrono-types.json | 4 +- schemars/tests/expected/crate_alias.json | 4 +- schemars/tests/expected/deprecated-enum.json | 4 +- .../tests/expected/deprecated-struct.json | 4 +- .../expected/duration_and_systemtime.json | 16 +-- .../expected/enum-adjacent-tagged-duf.json | 32 ++--- .../tests/expected/enum-adjacent-tagged.json | 32 ++--- .../tests/expected/enum-external-duf.json | 8 +- schemars/tests/expected/enum-external.json | 8 +- .../tests/expected/enum-internal-duf.json | 8 +- schemars/tests/expected/enum-internal.json | 8 +- .../tests/expected/enum-untagged-duf.json | 8 +- schemars/tests/expected/enum-untagged.json | 8 +- schemars/tests/expected/examples.json | 4 +- schemars/tests/expected/flatten.json | 2 +- .../tests/expected/inline-subschemas.json | 2 +- .../tests/expected/macro_built_struct.json | 6 +- schemars/tests/expected/nonzero_ints.json | 8 +- schemars/tests/expected/os_strings.json | 8 +- schemars/tests/expected/range.json | 16 +-- schemars/tests/expected/remote_derive.json | 4 +- .../tests/expected/remote_derive_generic.json | 6 +- .../tests/expected/schema-name-custom.json | 4 +- .../tests/expected/schema-name-default.json | 4 +- .../expected/schema-name-mixed-generics.json | 8 +- .../expected/schema_settings-2019_09.json | 4 +- .../expected/schema_settings-openapi3.json | 4 +- schemars/tests/expected/schema_settings.json | 4 +- .../schema_with-enum-adjacent-tagged.json | 16 +-- .../expected/schema_with-enum-internal.json | 4 +- .../tests/expected/schema_with-struct.json | 4 +- .../tests/expected/skip_struct_fields.json | 4 +- .../struct-normal-additional-properties.json | 4 +- schemars/tests/expected/struct-normal.json | 4 +- schemars/tests/expected/validate.json | 20 ++-- schemars/tests/expected/validate_inner.json | 10 +- schemars/tests/expected/validate_newtype.json | 4 +- .../expected/validate_schemars_attrs.json | 20 ++-- schemars/tests/expected/validate_tuple.json | 4 +- schemars/tests/util/mod.rs | 32 ----- schemars_derive/src/schema_exprs.rs | 112 +++++++++--------- 45 files changed, 276 insertions(+), 326 deletions(-) delete mode 100644 schemars/src/flatten.rs diff --git a/schemars/src/_private.rs b/schemars/src/_private.rs index cdd5ae8..4be6c27 100644 --- a/schemars/src/_private.rs +++ b/schemars/src/_private.rs @@ -3,6 +3,7 @@ use crate::JsonSchema; use crate::Schema; use serde::Serialize; use serde_json::json; +use serde_json::map::Entry; use serde_json::Map; use serde_json::Value; @@ -174,3 +175,62 @@ pub fn apply_inner_validation(schema: &mut Schema, f: fn(&mut Schema) -> ()) { f(inner_schema); } } + +pub fn flatten(schema: &mut Schema, other: Schema) { + if let Value::Object(obj2) = other.to_value() { + let obj1 = schema.ensure_object(); + + for (key, value2) in obj2 { + match obj1.entry(key) { + Entry::Vacant(vacant) => { + vacant.insert(value2); + } + Entry::Occupied(mut occupied) => { + match occupied.key().as_str() { + // This special "type" handling can probably be removed once the enum variant `with`/`schema_with` behaviour is fixed + "type" => match (occupied.get_mut(), value2) { + (Value::Array(a1), Value::Array(mut a2)) => { + a2.retain(|v2| !a1.contains(v2)); + a1.extend(a2); + } + (v1, Value::Array(mut a2)) => { + if !a2.contains(v1) { + a2.push(std::mem::take(v1)); + *occupied.get_mut() = Value::Array(a2); + } + } + (Value::Array(a1), v2) => { + if !a1.contains(&v2) { + a1.push(v2); + } + } + (v1, v2) => { + if v1 != &v2 { + *occupied.get_mut() = + Value::Array(vec![std::mem::take(v1), v2]); + } + } + }, + "required" => { + if let Value::Array(a1) = occupied.into_mut() { + if let Value::Array(a2) = value2 { + a1.extend(a2); + } + } + } + "properties" | "patternProperties" => { + if let Value::Object(o1) = occupied.into_mut() { + if let Value::Object(o2) = value2 { + o1.extend(o2); + } + } + } + _ => { + // leave the original value as it is (don't modify `schema`) + } + }; + } + } + } + } +} diff --git a/schemars/src/flatten.rs b/schemars/src/flatten.rs deleted file mode 100644 index 69b0361..0000000 --- a/schemars/src/flatten.rs +++ /dev/null @@ -1,71 +0,0 @@ -use serde_json::map::Entry; -use serde_json::Value; - -use crate::Schema; - -impl Schema { - /// This function is only public for use by schemars_derive. - /// - /// It should not be considered part of the public API. - #[doc(hidden)] - pub fn flatten(mut self, other: Self) -> Schema { - if let Value::Object(obj2) = other.to_value() { - let obj1 = self.ensure_object(); - - for (key, value2) in obj2 { - match obj1.entry(key) { - Entry::Vacant(vacant) => { - vacant.insert(value2); - } - Entry::Occupied(mut occupied) => { - match occupied.key().as_str() { - // This special "type" handling can probably be removed once the enum variant `with`/`schema_with` behaviour is fixed - "type" => match (occupied.get_mut(), value2) { - (Value::Array(a1), Value::Array(mut a2)) => { - a2.retain(|v2| !a1.contains(v2)); - a1.extend(a2); - } - (v1, Value::Array(mut a2)) => { - if !a2.contains(v1) { - a2.push(std::mem::take(v1)); - *occupied.get_mut() = Value::Array(a2); - } - } - (Value::Array(a1), v2) => { - if !a1.contains(&v2) { - a1.push(v2); - } - } - (v1, v2) => { - if v1 != &v2 { - *occupied.get_mut() = - Value::Array(vec![std::mem::take(v1), v2]); - } - } - }, - "required" => { - if let Value::Array(a1) = occupied.into_mut() { - if let Value::Array(a2) = value2 { - a1.extend(a2); - } - } - } - "properties" | "patternProperties" => { - if let Value::Object(o1) = occupied.into_mut() { - if let Value::Object(o2) = value2 { - o1.extend(o2); - } - } - } - _ => { - // leave the original value as it is (don't modify `self`) - } - }; - } - } - } - } - - self - } -} diff --git a/schemars/src/lib.rs b/schemars/src/lib.rs index 55d36c8..2059a24 100644 --- a/schemars/src/lib.rs +++ b/schemars/src/lib.rs @@ -1,7 +1,6 @@ #![deny(unsafe_code)] #![doc = include_str!("../README.md")] -mod flatten; mod json_schema_impls; mod schema; mod ser; diff --git a/schemars/tests/expected/bytes.json b/schemars/tests/expected/bytes.json index 5e1f9a5..06458c6 100644 --- a/schemars/tests/expected/bytes.json +++ b/schemars/tests/expected/bytes.json @@ -8,7 +8,7 @@ "items": { "type": "integer", "format": "uint8", - "minimum": 0.0 + "minimum": 0 } }, { @@ -16,7 +16,7 @@ "items": { "type": "integer", "format": "uint8", - "minimum": 0.0 + "minimum": 0 } } ], diff --git a/schemars/tests/expected/chrono-types.json b/schemars/tests/expected/chrono-types.json index b96b00c..b9e333d 100644 --- a/schemars/tests/expected/chrono-types.json +++ b/schemars/tests/expected/chrono-types.json @@ -33,10 +33,10 @@ } }, "required": [ + "weekday", "date_time", "naive_date", "naive_date_time", - "naive_time", - "weekday" + "naive_time" ] } \ No newline at end of file diff --git a/schemars/tests/expected/crate_alias.json b/schemars/tests/expected/crate_alias.json index d37d482..b01fc0d 100644 --- a/schemars/tests/expected/crate_alias.json +++ b/schemars/tests/expected/crate_alias.json @@ -13,7 +13,7 @@ } }, "required": [ - "bar", - "foo" + "foo", + "bar" ] } \ No newline at end of file diff --git a/schemars/tests/expected/deprecated-enum.json b/schemars/tests/expected/deprecated-enum.json index dab90ec..ee3de61 100644 --- a/schemars/tests/expected/deprecated-enum.json +++ b/schemars/tests/expected/deprecated-enum.json @@ -30,8 +30,8 @@ } }, "required": [ - "deprecated_field", - "foo" + "foo", + "deprecated_field" ] } }, diff --git a/schemars/tests/expected/deprecated-struct.json b/schemars/tests/expected/deprecated-struct.json index b915eb1..d2af941 100644 --- a/schemars/tests/expected/deprecated-struct.json +++ b/schemars/tests/expected/deprecated-struct.json @@ -14,7 +14,7 @@ }, "deprecated": true, "required": [ - "deprecated_field", - "foo" + "foo", + "deprecated_field" ] } \ No newline at end of file diff --git a/schemars/tests/expected/duration_and_systemtime.json b/schemars/tests/expected/duration_and_systemtime.json index b212599..71c17b7 100644 --- a/schemars/tests/expected/duration_and_systemtime.json +++ b/schemars/tests/expected/duration_and_systemtime.json @@ -21,17 +21,17 @@ "nanos": { "type": "integer", "format": "uint32", - "minimum": 0.0 + "minimum": 0 }, "secs": { "type": "integer", "format": "uint64", - "minimum": 0.0 + "minimum": 0 } }, "required": [ - "nanos", - "secs" + "secs", + "nanos" ] }, "SystemTime": { @@ -40,17 +40,17 @@ "nanos_since_epoch": { "type": "integer", "format": "uint32", - "minimum": 0.0 + "minimum": 0 }, "secs_since_epoch": { "type": "integer", "format": "uint64", - "minimum": 0.0 + "minimum": 0 } }, "required": [ - "nanos_since_epoch", - "secs_since_epoch" + "secs_since_epoch", + "nanos_since_epoch" ] } } diff --git a/schemars/tests/expected/enum-adjacent-tagged-duf.json b/schemars/tests/expected/enum-adjacent-tagged-duf.json index a89ab50..f746216 100644 --- a/schemars/tests/expected/enum-adjacent-tagged-duf.json +++ b/schemars/tests/expected/enum-adjacent-tagged-duf.json @@ -35,8 +35,8 @@ }, "additionalProperties": false, "required": [ - "c", - "t" + "t", + "c" ] }, { @@ -54,8 +54,8 @@ }, "additionalProperties": false, "required": [ - "c", - "t" + "t", + "c" ] }, { @@ -73,8 +73,8 @@ }, "additionalProperties": false, "required": [ - "c", - "t" + "t", + "c" ] }, { @@ -93,8 +93,8 @@ }, "additionalProperties": false, "required": [ - "bar", - "foo" + "foo", + "bar" ] }, "t": { @@ -106,8 +106,8 @@ }, "additionalProperties": false, "required": [ - "c", - "t" + "t", + "c" ] }, { @@ -136,8 +136,8 @@ }, "additionalProperties": false, "required": [ - "c", - "t" + "t", + "c" ] }, { @@ -171,8 +171,8 @@ }, "additionalProperties": false, "required": [ - "c", - "t" + "t", + "c" ] } ], @@ -189,8 +189,8 @@ } }, "required": [ - "bar", - "foo" + "foo", + "bar" ] }, "UnitStruct": { diff --git a/schemars/tests/expected/enum-adjacent-tagged.json b/schemars/tests/expected/enum-adjacent-tagged.json index 9380c5b..b977be0 100644 --- a/schemars/tests/expected/enum-adjacent-tagged.json +++ b/schemars/tests/expected/enum-adjacent-tagged.json @@ -33,8 +33,8 @@ } }, "required": [ - "c", - "t" + "t", + "c" ] }, { @@ -51,8 +51,8 @@ } }, "required": [ - "c", - "t" + "t", + "c" ] }, { @@ -69,8 +69,8 @@ } }, "required": [ - "c", - "t" + "t", + "c" ] }, { @@ -88,8 +88,8 @@ } }, "required": [ - "bar", - "foo" + "foo", + "bar" ] }, "t": { @@ -100,8 +100,8 @@ } }, "required": [ - "c", - "t" + "t", + "c" ] }, { @@ -129,8 +129,8 @@ } }, "required": [ - "c", - "t" + "t", + "c" ] }, { @@ -162,8 +162,8 @@ } }, "required": [ - "c", - "t" + "t", + "c" ] } ], @@ -180,8 +180,8 @@ } }, "required": [ - "bar", - "foo" + "foo", + "bar" ] }, "UnitStruct": { diff --git a/schemars/tests/expected/enum-external-duf.json b/schemars/tests/expected/enum-external-duf.json index d492808..0351fa2 100644 --- a/schemars/tests/expected/enum-external-duf.json +++ b/schemars/tests/expected/enum-external-duf.json @@ -64,8 +64,8 @@ }, "additionalProperties": false, "required": [ - "bar", - "foo" + "foo", + "bar" ] } }, @@ -124,8 +124,8 @@ } }, "required": [ - "bar", - "foo" + "foo", + "bar" ] }, "UnitStruct": { diff --git a/schemars/tests/expected/enum-external.json b/schemars/tests/expected/enum-external.json index b09f107..4c1d5a6 100644 --- a/schemars/tests/expected/enum-external.json +++ b/schemars/tests/expected/enum-external.json @@ -63,8 +63,8 @@ } }, "required": [ - "bar", - "foo" + "foo", + "bar" ] } }, @@ -123,8 +123,8 @@ } }, "required": [ - "bar", - "foo" + "foo", + "bar" ] }, "UnitStruct": { diff --git a/schemars/tests/expected/enum-internal-duf.json b/schemars/tests/expected/enum-internal-duf.json index 758bf9b..9c2aa9c 100644 --- a/schemars/tests/expected/enum-internal-duf.json +++ b/schemars/tests/expected/enum-internal-duf.json @@ -59,9 +59,9 @@ } }, "required": [ - "bar", + "typeProperty", "foo", - "typeProperty" + "bar" ] }, { @@ -81,9 +81,9 @@ }, "additionalProperties": false, "required": [ - "bar", + "typeProperty", "foo", - "typeProperty" + "bar" ] }, { diff --git a/schemars/tests/expected/enum-internal.json b/schemars/tests/expected/enum-internal.json index 765248f..7fd931c 100644 --- a/schemars/tests/expected/enum-internal.json +++ b/schemars/tests/expected/enum-internal.json @@ -57,9 +57,9 @@ } }, "required": [ - "bar", + "typeProperty", "foo", - "typeProperty" + "bar" ] }, { @@ -78,9 +78,9 @@ } }, "required": [ - "bar", + "typeProperty", "foo", - "typeProperty" + "bar" ] }, { diff --git a/schemars/tests/expected/enum-untagged-duf.json b/schemars/tests/expected/enum-untagged-duf.json index 7ce7d3d..e3f7c66 100644 --- a/schemars/tests/expected/enum-untagged-duf.json +++ b/schemars/tests/expected/enum-untagged-duf.json @@ -30,8 +30,8 @@ }, "additionalProperties": false, "required": [ - "bar", - "foo" + "foo", + "bar" ] }, { @@ -66,8 +66,8 @@ } }, "required": [ - "bar", - "foo" + "foo", + "bar" ] }, "UnitStruct": { diff --git a/schemars/tests/expected/enum-untagged.json b/schemars/tests/expected/enum-untagged.json index add81e4..2b23ffb 100644 --- a/schemars/tests/expected/enum-untagged.json +++ b/schemars/tests/expected/enum-untagged.json @@ -29,8 +29,8 @@ } }, "required": [ - "bar", - "foo" + "foo", + "bar" ] }, { @@ -65,8 +65,8 @@ } }, "required": [ - "bar", - "foo" + "foo", + "bar" ] }, "UnitStruct": { diff --git a/schemars/tests/expected/examples.json b/schemars/tests/expected/examples.json index 9c5c5ab..3203c4a 100644 --- a/schemars/tests/expected/examples.json +++ b/schemars/tests/expected/examples.json @@ -33,7 +33,7 @@ null ], "required": [ - "bar", - "foo" + "foo", + "bar" ] } \ No newline at end of file diff --git a/schemars/tests/expected/flatten.json b/schemars/tests/expected/flatten.json index ec0aff3..8f77dd2 100644 --- a/schemars/tests/expected/flatten.json +++ b/schemars/tests/expected/flatten.json @@ -26,8 +26,8 @@ } }, "required": [ - "b", "f", + "b", "s", "v" ] diff --git a/schemars/tests/expected/inline-subschemas.json b/schemars/tests/expected/inline-subschemas.json index fe4e83e..7ba76d5 100644 --- a/schemars/tests/expected/inline-subschemas.json +++ b/schemars/tests/expected/inline-subschemas.json @@ -9,7 +9,7 @@ "replicas": { "type": "integer", "format": "uint32", - "minimum": 0.0 + "minimum": 0 } }, "required": [ diff --git a/schemars/tests/expected/macro_built_struct.json b/schemars/tests/expected/macro_built_struct.json index 0c5840c..e958c8e 100644 --- a/schemars/tests/expected/macro_built_struct.json +++ b/schemars/tests/expected/macro_built_struct.json @@ -10,11 +10,11 @@ "x": { "type": "integer", "format": "uint8", - "minimum": 0.0 + "minimum": 0 } }, "required": [ - "v", - "x" + "x", + "v" ] } \ No newline at end of file diff --git a/schemars/tests/expected/nonzero_ints.json b/schemars/tests/expected/nonzero_ints.json index 3b83929..6ee5105 100644 --- a/schemars/tests/expected/nonzero_ints.json +++ b/schemars/tests/expected/nonzero_ints.json @@ -13,7 +13,7 @@ "nonzero_unsigned": { "type": "integer", "format": "uint32", - "minimum": 1.0 + "minimum": 1 }, "signed": { "type": "integer", @@ -22,13 +22,13 @@ "unsigned": { "type": "integer", "format": "uint32", - "minimum": 0.0 + "minimum": 0 } }, "required": [ - "nonzero_signed", + "unsigned", "nonzero_unsigned", "signed", - "unsigned" + "nonzero_signed" ] } \ No newline at end of file diff --git a/schemars/tests/expected/os_strings.json b/schemars/tests/expected/os_strings.json index 50448ed..927f3f9 100644 --- a/schemars/tests/expected/os_strings.json +++ b/schemars/tests/expected/os_strings.json @@ -11,8 +11,8 @@ } }, "required": [ - "borrowed", - "owned" + "owned", + "borrowed" ], "definitions": { "OsString": { @@ -25,7 +25,7 @@ "items": { "type": "integer", "format": "uint8", - "minimum": 0.0 + "minimum": 0 } } }, @@ -41,7 +41,7 @@ "items": { "type": "integer", "format": "uint16", - "minimum": 0.0 + "minimum": 0 } } }, diff --git a/schemars/tests/expected/range.json b/schemars/tests/expected/range.json index bb68e3a..19e4780 100644 --- a/schemars/tests/expected/range.json +++ b/schemars/tests/expected/range.json @@ -14,9 +14,9 @@ } }, "required": [ - "bound", + "range", "inclusive", - "range" + "bound" ], "definitions": { "Bound_of_string": { @@ -62,8 +62,8 @@ } }, "required": [ - "end", - "start" + "start", + "end" ] }, "Range_of_uint": { @@ -72,17 +72,17 @@ "end": { "type": "integer", "format": "uint", - "minimum": 0.0 + "minimum": 0 }, "start": { "type": "integer", "format": "uint", - "minimum": 0.0 + "minimum": 0 } }, "required": [ - "end", - "start" + "start", + "end" ] } } diff --git a/schemars/tests/expected/remote_derive.json b/schemars/tests/expected/remote_derive.json index 76df9b6..9477e0f 100644 --- a/schemars/tests/expected/remote_derive.json +++ b/schemars/tests/expected/remote_derive.json @@ -47,8 +47,8 @@ } }, "required": [ - "nanos", - "secs" + "secs", + "nanos" ] } } diff --git a/schemars/tests/expected/remote_derive_generic.json b/schemars/tests/expected/remote_derive_generic.json index d06320f..b533320 100644 --- a/schemars/tests/expected/remote_derive_generic.json +++ b/schemars/tests/expected/remote_derive_generic.json @@ -25,9 +25,9 @@ }, "required": [ "byte_or_bool2", - "fake_map", + "unit_or_t2", "s", - "unit_or_t2" + "fake_map" ], "definitions": { "Or_for_null_and_int32": { @@ -46,7 +46,7 @@ { "type": "integer", "format": "uint8", - "minimum": 0.0 + "minimum": 0 }, { "type": "boolean" diff --git a/schemars/tests/expected/schema-name-custom.json b/schemars/tests/expected/schema-name-custom.json index 4e6cc36..dc5336b 100644 --- a/schemars/tests/expected/schema-name-custom.json +++ b/schemars/tests/expected/schema-name-custom.json @@ -24,11 +24,11 @@ } }, "required": [ - "inner", "t", "u", "v", - "w" + "w", + "inner" ], "definitions": { "another-new-name": { diff --git a/schemars/tests/expected/schema-name-default.json b/schemars/tests/expected/schema-name-default.json index 39c39a9..7524c63 100644 --- a/schemars/tests/expected/schema-name-default.json +++ b/schemars/tests/expected/schema-name-default.json @@ -24,11 +24,11 @@ } }, "required": [ - "inner", "t", "u", "v", - "w" + "w", + "inner" ], "definitions": { "MySimpleStruct": { diff --git a/schemars/tests/expected/schema-name-mixed-generics.json b/schemars/tests/expected/schema-name-mixed-generics.json index 0da8044..fdadbab 100644 --- a/schemars/tests/expected/schema-name-mixed-generics.json +++ b/schemars/tests/expected/schema-name-mixed-generics.json @@ -12,8 +12,8 @@ } }, "required": [ - "foo", - "generic" + "generic", + "foo" ], "definitions": { "MySimpleStruct": { @@ -52,11 +52,11 @@ } }, "required": [ - "inner", "t", "u", "v", - "w" + "w", + "inner" ] } } diff --git a/schemars/tests/expected/schema_settings-2019_09.json b/schemars/tests/expected/schema_settings-2019_09.json index e51f397..947aa2e 100644 --- a/schemars/tests/expected/schema_settings-2019_09.json +++ b/schemars/tests/expected/schema_settings-2019_09.json @@ -29,8 +29,8 @@ }, "required": [ "int", - "value", - "values" + "values", + "value" ], "definitions": { "Inner": { diff --git a/schemars/tests/expected/schema_settings-openapi3.json b/schemars/tests/expected/schema_settings-openapi3.json index 5318b01..b6de302 100644 --- a/schemars/tests/expected/schema_settings-openapi3.json +++ b/schemars/tests/expected/schema_settings-openapi3.json @@ -24,8 +24,8 @@ }, "required": [ "int", - "value", - "values" + "values", + "value" ], "definitions": { "Inner": { diff --git a/schemars/tests/expected/schema_settings.json b/schemars/tests/expected/schema_settings.json index 07cdb0e..6a66ca0 100644 --- a/schemars/tests/expected/schema_settings.json +++ b/schemars/tests/expected/schema_settings.json @@ -29,8 +29,8 @@ }, "required": [ "int", - "value", - "values" + "values", + "value" ], "definitions": { "Inner": { diff --git a/schemars/tests/expected/schema_with-enum-adjacent-tagged.json b/schemars/tests/expected/schema_with-enum-adjacent-tagged.json index c29c59e..5b95671 100644 --- a/schemars/tests/expected/schema_with-enum-adjacent-tagged.json +++ b/schemars/tests/expected/schema_with-enum-adjacent-tagged.json @@ -24,8 +24,8 @@ } }, "required": [ - "c", - "t" + "t", + "c" ] }, { @@ -42,8 +42,8 @@ } }, "required": [ - "c", - "t" + "t", + "c" ] }, { @@ -71,8 +71,8 @@ } }, "required": [ - "c", - "t" + "t", + "c" ] }, { @@ -89,8 +89,8 @@ } }, "required": [ - "c", - "t" + "t", + "c" ] } ] diff --git a/schemars/tests/expected/schema_with-enum-internal.json b/schemars/tests/expected/schema_with-enum-internal.json index 45bc8e9..90871ad 100644 --- a/schemars/tests/expected/schema_with-enum-internal.json +++ b/schemars/tests/expected/schema_with-enum-internal.json @@ -14,8 +14,8 @@ } }, "required": [ - "foo", - "typeProperty" + "typeProperty", + "foo" ] }, { diff --git a/schemars/tests/expected/schema_with-struct.json b/schemars/tests/expected/schema_with-struct.json index 40b5c56..7cc55bf 100644 --- a/schemars/tests/expected/schema_with-struct.json +++ b/schemars/tests/expected/schema_with-struct.json @@ -15,8 +15,8 @@ } }, "required": [ + "foo", "bar", - "baz", - "foo" + "baz" ] } \ No newline at end of file diff --git a/schemars/tests/expected/skip_struct_fields.json b/schemars/tests/expected/skip_struct_fields.json index 21e01a6..7890298 100644 --- a/schemars/tests/expected/skip_struct_fields.json +++ b/schemars/tests/expected/skip_struct_fields.json @@ -18,7 +18,7 @@ } }, "required": [ - "included", - "writable" + "writable", + "included" ] } \ No newline at end of file diff --git a/schemars/tests/expected/struct-normal-additional-properties.json b/schemars/tests/expected/struct-normal-additional-properties.json index ed8602a..5ff105c 100644 --- a/schemars/tests/expected/struct-normal-additional-properties.json +++ b/schemars/tests/expected/struct-normal-additional-properties.json @@ -19,7 +19,7 @@ }, "additionalProperties": false, "required": [ - "bar", - "foo" + "foo", + "bar" ] } \ No newline at end of file diff --git a/schemars/tests/expected/struct-normal.json b/schemars/tests/expected/struct-normal.json index e6fe68b..0a5a0af 100644 --- a/schemars/tests/expected/struct-normal.json +++ b/schemars/tests/expected/struct-normal.json @@ -18,7 +18,7 @@ } }, "required": [ - "bar", - "foo" + "foo", + "bar" ] } \ No newline at end of file diff --git a/schemars/tests/expected/validate.json b/schemars/tests/expected/validate.json index 38aabcc..2d6e43c 100644 --- a/schemars/tests/expected/validate.json +++ b/schemars/tests/expected/validate.json @@ -31,14 +31,14 @@ "min_max": { "type": "number", "format": "float", - "maximum": 100.0, + "maximum": 100, "minimum": 0.01 }, "min_max2": { "type": "number", "format": "float", - "maximum": 1000.0, - "minimum": 1.0 + "maximum": 1000, + "minimum": 1 }, "non_empty_str": { "type": "string", @@ -84,21 +84,21 @@ } }, "required": [ + "min_max", + "min_max2", + "regex_str1", + "regex_str2", + "regex_str3", "contains_str1", "contains_str2", "email_address", + "tel", "homepage", - "map_contains", - "min_max", - "min_max2", "non_empty_str", "non_empty_str2", "pair", - "regex_str1", - "regex_str2", - "regex_str3", + "map_contains", "required_option", - "tel", "x" ] } \ No newline at end of file diff --git a/schemars/tests/expected/validate_inner.json b/schemars/tests/expected/validate_inner.json index ffe6792..f8a7eca 100644 --- a/schemars/tests/expected/validate_inner.json +++ b/schemars/tests/expected/validate_inner.json @@ -25,8 +25,8 @@ "items": { "type": "integer", "format": "int32", - "maximum": 10.0, - "minimum": -10.0 + "maximum": 10, + "minimum": -10 } }, "vec_str_length": { @@ -65,10 +65,10 @@ "required": [ "array_str_length", "slice_str_contains", - "vec_i32_range", + "vec_str_regex", "vec_str_length", "vec_str_length2", - "vec_str_regex", - "vec_str_url" + "vec_str_url", + "vec_i32_range" ] } \ No newline at end of file diff --git a/schemars/tests/expected/validate_newtype.json b/schemars/tests/expected/validate_newtype.json index 796aecd..4c1146e 100644 --- a/schemars/tests/expected/validate_newtype.json +++ b/schemars/tests/expected/validate_newtype.json @@ -3,6 +3,6 @@ "title": "NewType", "type": "integer", "format": "uint8", - "maximum": 10.0, - "minimum": 0.0 + "maximum": 10, + "minimum": 0 } \ No newline at end of file diff --git a/schemars/tests/expected/validate_schemars_attrs.json b/schemars/tests/expected/validate_schemars_attrs.json index 438b4aa..fa2dbcd 100644 --- a/schemars/tests/expected/validate_schemars_attrs.json +++ b/schemars/tests/expected/validate_schemars_attrs.json @@ -31,14 +31,14 @@ "min_max": { "type": "number", "format": "float", - "maximum": 100.0, + "maximum": 100, "minimum": 0.01 }, "min_max2": { "type": "number", "format": "float", - "maximum": 1000.0, - "minimum": 1.0 + "maximum": 1000, + "minimum": 1 }, "non_empty_str": { "type": "string", @@ -84,21 +84,21 @@ } }, "required": [ + "min_max", + "min_max2", + "regex_str1", + "regex_str2", + "regex_str3", "contains_str1", "contains_str2", "email_address", + "tel", "homepage", - "map_contains", - "min_max", - "min_max2", "non_empty_str", "non_empty_str2", "pair", - "regex_str1", - "regex_str2", - "regex_str3", + "map_contains", "required_option", - "tel", "x" ] } \ No newline at end of file diff --git a/schemars/tests/expected/validate_tuple.json b/schemars/tests/expected/validate_tuple.json index 8ab6eaa..619b8c4 100644 --- a/schemars/tests/expected/validate_tuple.json +++ b/schemars/tests/expected/validate_tuple.json @@ -6,8 +6,8 @@ { "type": "integer", "format": "uint8", - "maximum": 10.0, - "minimum": 0.0 + "maximum": 10, + "minimum": 0 }, { "type": "boolean" diff --git a/schemars/tests/util/mod.rs b/schemars/tests/util/mod.rs index 3a977fd..bab563e 100644 --- a/schemars/tests/util/mod.rs +++ b/schemars/tests/util/mod.rs @@ -1,6 +1,5 @@ use pretty_assertions::assert_eq; extern crate apistos_schemars as schemars; -use schemars::visit::Visitor; use schemars::{gen::SchemaSettings, schema_for, JsonSchema, Schema}; use std::error::Error; use std::fs; @@ -20,15 +19,6 @@ pub fn test_default_generated_schema(file: &str) -> TestResult { } pub fn test_schema(actual: &Schema, file: &str) -> TestResult { - // TEMP for easier comparison of schemas handling changes that don't actually affect a schema: - // - `required` ordering has changed - // - previously `f64` properties may now be integers - let actual = &{ - let mut actual = actual.clone(); - TempFixupForTests.visit_schema(&mut actual); - actual - }; - let expected_json = match fs::read_to_string(format!("tests/expected/{}.json", file)) { Ok(j) => j, Err(e) => { @@ -51,25 +41,3 @@ fn write_actual_to_file(schema: &Schema, file: &str) -> TestResult { fs::write(format!("tests/actual/{}.json", file), actual_json)?; Ok(()) } - -struct TempFixupForTests; - -impl schemars::visit::Visitor for TempFixupForTests { - fn visit_schema(&mut self, schema: &mut Schema) { - schemars::visit::visit_schema(self, schema); - - if let Some(object) = schema.as_object_mut() { - if let Some(serde_json::Value::Array(required)) = object.get_mut("required") { - required.sort_unstable_by(|a, b| a.as_str().cmp(&b.as_str())); - } - - for (key, value) in object { - if key == "multipleOf" || key.ends_with("aximum") || key.ends_with("inimum") { - if let Some(f) = value.as_f64() { - *value = f.into(); - } - } - } - } - } -} diff --git a/schemars_derive/src/schema_exprs.rs b/schemars_derive/src/schema_exprs.rs index 7a1de81..6357172 100644 --- a/schemars_derive/src/schema_exprs.rs +++ b/schemars_derive/src/schema_exprs.rs @@ -434,73 +434,68 @@ fn expr_for_struct( default: &SerdeDefault, deny_unknown_fields: bool, ) -> TokenStream { - let (flattened_fields, property_fields): (Vec<_>, Vec<_>) = fields - .iter() - .filter(|f| !f.serde_attrs.skip_deserializing() || !f.serde_attrs.skip_serializing()) - .partition(|f| f.serde_attrs.flatten()); - let set_container_default = match default { SerdeDefault::None => None, SerdeDefault::Default => Some(quote!(let container_default = Self::default();)), SerdeDefault::Path(path) => Some(quote!(let container_default = #path();)), }; - let properties: Vec<_> = property_fields - .into_iter() + let properties: Vec<_> = fields + .iter() + .filter(|f| !f.serde_attrs.skip_deserializing() || !f.serde_attrs.skip_serializing()) .map(|field| { - let name = field.name(); - let default = field_default_expr(field, set_container_default.is_some()); + if field.serde_attrs.flatten() { + let (ty, type_def) = type_for_field_schema(field); - let (ty, type_def) = type_for_field_schema(field); + let required = field.validation_attrs.required(); - let has_default = default.is_some(); - let required = field.validation_attrs.required(); + let args = quote!(gen, #required); + let mut schema_expr = quote_spanned! {ty.span()=> + schemars::_private::json_schema_for_flatten::<#ty>(#args) + }; - let metadata = SchemaMetadata { - read_only: field.serde_attrs.skip_deserializing(), - write_only: field.serde_attrs.skip_serializing(), - default, - ..field.attrs.as_metadata() - }; + prepend_type_def(type_def, &mut schema_expr); - let gen = quote!(gen); - let mut schema_expr = if field.validation_attrs.required() { - quote_spanned! {ty.span()=> - <#ty as schemars::JsonSchema>::_schemars_private_non_optional_json_schema(#gen) + quote! { + schemars::_private::flatten(&mut schema, #schema_expr); } } else { - quote_spanned! {ty.span()=> - #gen.subschema_for::<#ty>() - } - }; + let name = field.name(); + let default = field_default_expr(field, set_container_default.is_some()); - metadata.apply_to_schema(&mut schema_expr); - field.validation_attrs.apply_to_schema(&mut schema_expr); + let (ty, type_def) = type_for_field_schema(field); - quote! { - { - #type_def - schemars::_private::insert_object_property::<#ty>(&mut schema, #name, #has_default, #required, #schema_expr); - } - } - }) - .collect(); + let has_default = default.is_some(); + let required = field.validation_attrs.required(); - let flattens: Vec<_> = flattened_fields - .into_iter() - .map(|field| { - let (ty, type_def) = type_for_field_schema(field); + let metadata = SchemaMetadata { + read_only: field.serde_attrs.skip_deserializing(), + write_only: field.serde_attrs.skip_serializing(), + default, + ..field.attrs.as_metadata() + }; - let required = field.validation_attrs.required(); + let gen = quote!(gen); + let mut schema_expr = if field.validation_attrs.required() { + quote_spanned! {ty.span()=> + <#ty as schemars::JsonSchema>::_schemars_private_non_optional_json_schema(#gen) + } + } else { + quote_spanned! {ty.span()=> + #gen.subschema_for::<#ty>() + } + }; - let args = quote!(gen, #required); - let mut schema_expr = quote_spanned! {ty.span()=> - schemars::_private::json_schema_for_flatten::<#ty>(#args) - }; + metadata.apply_to_schema(&mut schema_expr); + field.validation_attrs.apply_to_schema(&mut schema_expr); - prepend_type_def(type_def, &mut schema_expr); - schema_expr - }) + quote! { + { + #type_def + schemars::_private::insert_object_property::<#ty>(&mut schema, #name, #has_default, #required, #schema_expr); + } + }} + }) .collect(); let set_additional_properties = if deny_unknown_fields { @@ -510,17 +505,16 @@ fn expr_for_struct( } else { TokenStream::new() }; - quote! { - { - #set_container_default - let mut schema = schemars::json_schema!({ - "type": "object", - #set_additional_properties - }); - #(#properties)* - schema #(.flatten(#flattens))* - } - } + + quote! ({ + #set_container_default + let mut schema = schemars::json_schema!({ + "type": "object", + #set_additional_properties + }); + #(#properties)* + schema + }) } fn field_default_expr(field: &Field, container_has_default: bool) -> Option { From 9443424deb8c531b71c7ffa93f60b4dd090f9891 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Sat, 18 May 2024 21:55:05 +0100 Subject: [PATCH 10/31] Re-add `preserve_order` feature, to preserve order of struct fields in a schema's `properties` --- Cargo.lock | 1 + schemars/Cargo.toml | 1 + schemars/src/gen.rs | 47 +++----- schemars/src/schema.rs | 3 +- schemars/tests/expected/bytes.json | 4 +- schemars/tests/expected/chrono-types.json | 24 ++-- schemars/tests/expected/crate_alias.json | 6 +- schemars/tests/expected/default.json | 16 +-- schemars/tests/expected/deprecated-enum.json | 18 +-- .../tests/expected/deprecated-struct.json | 12 +- .../tests/expected/doc_comments_enum.json | 4 +- .../expected/duration_and_systemtime.json | 16 +-- .../expected/enum-adjacent-tagged-duf.json | 110 +++++++++--------- .../tests/expected/enum-adjacent-tagged.json | 78 ++++++------- .../tests/expected/enum-external-duf.json | 46 ++++---- schemars/tests/expected/enum-external.json | 46 ++++---- .../tests/expected/enum-internal-duf.json | 24 ++-- schemars/tests/expected/enum-internal.json | 12 +- .../expected/enum-simple-internal-duf.json | 12 +- .../tests/expected/enum-untagged-duf.json | 22 ++-- schemars/tests/expected/enum-untagged.json | 22 ++-- schemars/tests/expected/enumset.json | 2 +- schemars/tests/expected/examples.json | 28 ++--- schemars/tests/expected/flatten.json | 12 +- schemars/tests/expected/from_json_value.json | 32 ++--- .../tests/expected/from_value_2019_09.json | 56 ++++----- .../tests/expected/from_value_draft07.json | 56 ++++----- .../tests/expected/from_value_openapi3.json | 58 ++++----- schemars/tests/expected/indexmap.json | 4 +- schemars/tests/expected/macro_built_enum.json | 4 +- .../tests/expected/macro_built_struct.json | 8 +- schemars/tests/expected/nonzero_ints.json | 16 +-- schemars/tests/expected/os_strings.json | 4 +- schemars/tests/expected/range.json | 80 ++++++------- schemars/tests/expected/remote_derive.json | 32 ++--- .../tests/expected/remote_derive_generic.json | 34 +++--- schemars/tests/expected/result.json | 24 ++-- .../tests/expected/schema-name-custom.json | 6 +- .../tests/expected/schema-name-default.json | 6 +- .../expected/schema-name-mixed-generics.json | 36 +++--- .../expected/schema_settings-2019_09.json | 26 ++--- .../expected/schema_settings-openapi3.json | 22 ++-- schemars/tests/expected/schema_settings.json | 26 ++--- .../schema_with-enum-adjacent-tagged.json | 40 +++---- .../expected/schema_with-enum-external.json | 20 ++-- .../expected/schema_with-enum-untagged.json | 4 +- .../tests/expected/schema_with-struct.json | 6 +- .../tests/expected/schema_with-tuple.json | 4 +- .../tests/expected/skip_enum_variants.json | 4 +- .../tests/expected/skip_struct_fields.json | 10 +- .../tests/expected/skip_tuple_fields.json | 4 +- .../struct-normal-additional-properties.json | 8 +- schemars/tests/expected/struct-normal.json | 8 +- schemars/tests/expected/struct-tuple.json | 4 +- .../tests/expected/transparent-struct.json | 4 +- schemars/tests/expected/validate.json | 84 ++++++------- schemars/tests/expected/validate_inner.json | 40 +++---- schemars/tests/expected/validate_newtype.json | 4 +- .../expected/validate_schemars_attrs.json | 84 ++++++------- schemars/tests/expected/validate_tuple.json | 8 +- 60 files changed, 706 insertions(+), 726 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6a57a49..cff5b5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -381,6 +381,7 @@ version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ + "indexmap", "itoa", "ryu", "serde", diff --git a/schemars/Cargo.toml b/schemars/Cargo.toml index ea12342..40b0a60 100644 --- a/schemars/Cargo.toml +++ b/schemars/Cargo.toml @@ -42,6 +42,7 @@ serde = { version = "1.0", features = ["derive"] } default = ["derive"] derive = ["schemars_derive"] +preserve_order = ["serde_json/preserve_order"] raw_value = ["serde_json/raw_value"] diff --git a/schemars/src/gen.rs b/schemars/src/gen.rs index 2d7fafd..20d2603 100644 --- a/schemars/src/gen.rs +++ b/schemars/src/gen.rs @@ -11,8 +11,9 @@ use crate::Schema; use crate::{visit::*, JsonSchema}; use dyn_clone::DynClone; use serde::Serialize; +use serde_json::{Map, Value}; use std::borrow::Cow; -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use std::{any::Any, collections::HashSet, fmt::Debug}; /// Settings to customize how Schemas are generated. @@ -151,7 +152,7 @@ impl SchemaSettings { #[derive(Debug, Default)] pub struct SchemaGenerator { settings: SchemaSettings, - definitions: BTreeMap, + definitions: Map, pending_schema_ids: HashSet>, schema_id_to_name: HashMap, String>, used_schema_names: HashSet, @@ -257,31 +258,31 @@ impl SchemaGenerator { let schema = self.json_schema_internal::(id); - self.definitions.insert(name, schema); + self.definitions.insert(name, schema.to_value()); } /// Borrows the collection of all [referenceable](JsonSchema::is_referenceable) schemas that have been generated. /// - /// The keys of the returned `BTreeMap` are the [schema names](JsonSchema::schema_name), and the values are the schemas + /// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the values are the schemas /// themselves. - pub fn definitions(&self) -> &BTreeMap { + pub fn definitions(&self) -> &Map { &self.definitions } /// Mutably borrows the collection of all [referenceable](JsonSchema::is_referenceable) schemas that have been generated. /// - /// The keys of the returned `BTreeMap` are the [schema names](JsonSchema::schema_name), and the values are the schemas + /// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the values are the schemas /// themselves. - pub fn definitions_mut(&mut self) -> &mut BTreeMap { + pub fn definitions_mut(&mut self) -> &mut Map { &mut self.definitions } /// Returns the collection of all [referenceable](JsonSchema::is_referenceable) schemas that have been generated, /// leaving an empty map in its place. /// - /// The keys of the returned `BTreeMap` are the [schema names](JsonSchema::schema_name), and the values are the schemas + /// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the values are the schemas /// themselves. - pub fn take_definitions(&mut self) -> BTreeMap { + pub fn take_definitions(&mut self) -> Map { std::mem::take(&mut self.definitions) } @@ -311,12 +312,7 @@ impl SchemaGenerator { if !self.definitions.is_empty() { object.insert( "definitions".into(), - serde_json::Value::Object( - self.definitions - .iter() - .map(|(k, v)| (k.clone(), v.clone().into())) - .collect(), - ), + serde_json::Value::Object(self.definitions.clone()), ); } @@ -347,12 +343,7 @@ impl SchemaGenerator { if !self.definitions.is_empty() { object.insert( "definitions".into(), - serde_json::Value::Object( - self.definitions - .into_iter() - .map(|(k, v)| (k, v.into())) - .collect(), - ), + serde_json::Value::Object(self.definitions), ); } @@ -389,12 +380,7 @@ impl SchemaGenerator { if !self.definitions.is_empty() { object.insert( "definitions".into(), - serde_json::Value::Object( - self.definitions - .iter() - .map(|(k, v)| (k.clone(), v.clone().into())) - .collect(), - ), + serde_json::Value::Object(self.definitions.clone()), ); } @@ -431,12 +417,7 @@ impl SchemaGenerator { if !self.definitions.is_empty() { object.insert( "definitions".into(), - serde_json::Value::Object( - self.definitions - .into_iter() - .map(|(k, v)| (k, v.into())) - .collect(), - ), + serde_json::Value::Object(self.definitions), ); } diff --git a/schemars/src/schema.rs b/schemars/src/schema.rs index 82be821..27cb048 100644 --- a/schemars/src/schema.rs +++ b/schemars/src/schema.rs @@ -180,7 +180,8 @@ mod ser { use serde_json::Value; // The order of properties in a JSON Schema object is insignificant, but we explicitly order - // some of them here to make them easier for a human to read. + // some of them here to make them easier for a human to read. All other properties are ordered + // either lexicographically (by default) or by insertion order (if `preserve_order` is enabled) const ORDERED_KEYWORDS_START: [&str; 7] = [ "$id", "$schema", diff --git a/schemars/tests/expected/bytes.json b/schemars/tests/expected/bytes.json index 06458c6..8618a7f 100644 --- a/schemars/tests/expected/bytes.json +++ b/schemars/tests/expected/bytes.json @@ -20,6 +20,6 @@ } } ], - "maxItems": 2, - "minItems": 2 + "minItems": 2, + "maxItems": 2 } \ No newline at end of file diff --git a/schemars/tests/expected/chrono-types.json b/schemars/tests/expected/chrono-types.json index b9e333d..ce03621 100644 --- a/schemars/tests/expected/chrono-types.json +++ b/schemars/tests/expected/chrono-types.json @@ -3,6 +3,18 @@ "title": "ChronoTypes", "type": "object", "properties": { + "weekday": { + "type": "string", + "enum": [ + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + "Sun" + ] + }, "date_time": { "type": "string", "format": "date-time" @@ -18,18 +30,6 @@ "naive_time": { "type": "string", "format": "partial-date-time" - }, - "weekday": { - "type": "string", - "enum": [ - "Mon", - "Tue", - "Wed", - "Thu", - "Fri", - "Sat", - "Sun" - ] } }, "required": [ diff --git a/schemars/tests/expected/crate_alias.json b/schemars/tests/expected/crate_alias.json index b01fc0d..e88a449 100644 --- a/schemars/tests/expected/crate_alias.json +++ b/schemars/tests/expected/crate_alias.json @@ -3,13 +3,13 @@ "title": "Struct", "type": "object", "properties": { - "bar": { - "type": "boolean" - }, "foo": { "description": "This is a document", "type": "integer", "format": "int32" + }, + "bar": { + "type": "boolean" } }, "required": [ diff --git a/schemars/tests/expected/default.json b/schemars/tests/expected/default.json index f8c6b2a..57e346d 100644 --- a/schemars/tests/expected/default.json +++ b/schemars/tests/expected/default.json @@ -3,10 +3,6 @@ "title": "MyStruct", "type": "object", "properties": { - "my_bool": { - "type": "boolean", - "default": false - }, "my_int": { "type": "integer", "format": "int32", @@ -20,12 +16,12 @@ ] }, "my_struct2": { + "default": "i:0 b:false", "allOf": [ { "$ref": "#/definitions/MyStruct2" } - ], - "default": "i:0 b:false" + ] }, "my_struct2_default_skipped": { "$ref": "#/definitions/MyStruct2" @@ -38,14 +34,14 @@ "MyStruct2": { "type": "object", "properties": { - "my_bool": { - "type": "boolean", - "default": true - }, "my_int": { "type": "integer", "format": "int32", "default": 6 + }, + "my_bool": { + "type": "boolean", + "default": true } } }, diff --git a/schemars/tests/expected/deprecated-enum.json b/schemars/tests/expected/deprecated-enum.json index ee3de61..e871160 100644 --- a/schemars/tests/expected/deprecated-enum.json +++ b/schemars/tests/expected/deprecated-enum.json @@ -1,7 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "DeprecatedEnum", - "deprecated": true, "oneOf": [ { "type": "string", @@ -20,13 +19,13 @@ "DeprecatedStructVariant": { "type": "object", "properties": { - "deprecated_field": { - "type": "boolean", - "deprecated": true - }, "foo": { "type": "integer", "format": "int32" + }, + "deprecated_field": { + "type": "boolean", + "deprecated": true } }, "required": [ @@ -35,11 +34,12 @@ ] } }, - "additionalProperties": false, - "deprecated": true, "required": [ "DeprecatedStructVariant" - ] + ], + "additionalProperties": false, + "deprecated": true } - ] + ], + "deprecated": true } \ No newline at end of file diff --git a/schemars/tests/expected/deprecated-struct.json b/schemars/tests/expected/deprecated-struct.json index d2af941..2b25c34 100644 --- a/schemars/tests/expected/deprecated-struct.json +++ b/schemars/tests/expected/deprecated-struct.json @@ -3,18 +3,18 @@ "title": "DeprecatedStruct", "type": "object", "properties": { - "deprecated_field": { - "type": "boolean", - "deprecated": true - }, "foo": { "type": "integer", "format": "int32" + }, + "deprecated_field": { + "type": "boolean", + "deprecated": true } }, - "deprecated": true, "required": [ "foo", "deprecated_field" - ] + ], + "deprecated": true } \ No newline at end of file diff --git a/schemars/tests/expected/doc_comments_enum.json b/schemars/tests/expected/doc_comments_enum.json index fc38db1..7d9321f 100644 --- a/schemars/tests/expected/doc_comments_enum.json +++ b/schemars/tests/expected/doc_comments_enum.json @@ -34,10 +34,10 @@ } } }, - "additionalProperties": false, "required": [ "Complex" - ] + ], + "additionalProperties": false } ] } \ No newline at end of file diff --git a/schemars/tests/expected/duration_and_systemtime.json b/schemars/tests/expected/duration_and_systemtime.json index 71c17b7..bb25dcd 100644 --- a/schemars/tests/expected/duration_and_systemtime.json +++ b/schemars/tests/expected/duration_and_systemtime.json @@ -18,14 +18,14 @@ "Duration": { "type": "object", "properties": { - "nanos": { + "secs": { "type": "integer", - "format": "uint32", + "format": "uint64", "minimum": 0 }, - "secs": { + "nanos": { "type": "integer", - "format": "uint64", + "format": "uint32", "minimum": 0 } }, @@ -37,14 +37,14 @@ "SystemTime": { "type": "object", "properties": { - "nanos_since_epoch": { + "secs_since_epoch": { "type": "integer", - "format": "uint32", + "format": "uint64", "minimum": 0 }, - "secs_since_epoch": { + "nanos_since_epoch": { "type": "integer", - "format": "uint64", + "format": "uint32", "minimum": 0 } }, diff --git a/schemars/tests/expected/enum-adjacent-tagged-duf.json b/schemars/tests/expected/enum-adjacent-tagged-duf.json index f746216..b9cb5fa 100644 --- a/schemars/tests/expected/enum-adjacent-tagged-duf.json +++ b/schemars/tests/expected/enum-adjacent-tagged-duf.json @@ -12,83 +12,89 @@ ] } }, - "additionalProperties": false, "required": [ "t" - ] + ], + "additionalProperties": false }, { "type": "object", "properties": { - "c": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, "t": { "type": "string", "enum": [ "StringMap" ] + }, + "c": { + "type": "object", + "additionalProperties": { + "type": "string" + } } }, - "additionalProperties": false, "required": [ "t", "c" - ] + ], + "additionalProperties": false }, { "type": "object", "properties": { - "c": { - "$ref": "#/definitions/UnitStruct" - }, "t": { "type": "string", "enum": [ "UnitStructNewType" ] + }, + "c": { + "$ref": "#/definitions/UnitStruct" } }, - "additionalProperties": false, "required": [ "t", "c" - ] + ], + "additionalProperties": false }, { "type": "object", "properties": { - "c": { - "$ref": "#/definitions/Struct" - }, "t": { "type": "string", "enum": [ "StructNewType" ] + }, + "c": { + "$ref": "#/definitions/Struct" } }, - "additionalProperties": false, "required": [ "t", "c" - ] + ], + "additionalProperties": false }, { "type": "object", "properties": { + "t": { + "type": "string", + "enum": [ + "Struct" + ] + }, "c": { "type": "object", "properties": { - "bar": { - "type": "boolean" - }, "foo": { "type": "integer", "format": "int32" + }, + "bar": { + "type": "boolean" } }, "additionalProperties": false, @@ -96,23 +102,23 @@ "foo", "bar" ] - }, - "t": { - "type": "string", - "enum": [ - "Struct" - ] } }, - "additionalProperties": false, "required": [ "t", "c" - ] + ], + "additionalProperties": false }, { "type": "object", "properties": { + "t": { + "type": "string", + "enum": [ + "Tuple" + ] + }, "c": { "type": "array", "items": [ @@ -124,21 +130,15 @@ "type": "boolean" } ], - "maxItems": 2, - "minItems": 2 - }, - "t": { - "type": "string", - "enum": [ - "Tuple" - ] + "minItems": 2, + "maxItems": 2 } }, - "additionalProperties": false, "required": [ "t", "c" - ] + ], + "additionalProperties": false }, { "type": "object", @@ -150,51 +150,51 @@ ] } }, - "additionalProperties": false, "required": [ "t" - ] + ], + "additionalProperties": false }, { "type": "object", "properties": { - "c": { - "type": "integer", - "format": "int32" - }, "t": { "type": "string", "enum": [ "WithInt" ] + }, + "c": { + "type": "integer", + "format": "int32" } }, - "additionalProperties": false, "required": [ "t", "c" - ] + ], + "additionalProperties": false } ], "definitions": { + "UnitStruct": { + "type": "null" + }, "Struct": { "type": "object", "properties": { - "bar": { - "type": "boolean" - }, "foo": { "type": "integer", "format": "int32" + }, + "bar": { + "type": "boolean" } }, "required": [ "foo", "bar" ] - }, - "UnitStruct": { - "type": "null" } } } \ No newline at end of file diff --git a/schemars/tests/expected/enum-adjacent-tagged.json b/schemars/tests/expected/enum-adjacent-tagged.json index b977be0..de77713 100644 --- a/schemars/tests/expected/enum-adjacent-tagged.json +++ b/schemars/tests/expected/enum-adjacent-tagged.json @@ -19,17 +19,17 @@ { "type": "object", "properties": { - "c": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, "t": { "type": "string", "enum": [ "StringMap" ] + }, + "c": { + "type": "object", + "additionalProperties": { + "type": "string" + } } }, "required": [ @@ -40,14 +40,14 @@ { "type": "object", "properties": { - "c": { - "$ref": "#/definitions/UnitStruct" - }, "t": { "type": "string", "enum": [ "UnitStructNewType" ] + }, + "c": { + "$ref": "#/definitions/UnitStruct" } }, "required": [ @@ -58,14 +58,14 @@ { "type": "object", "properties": { - "c": { - "$ref": "#/definitions/Struct" - }, "t": { "type": "string", "enum": [ "StructNewType" ] + }, + "c": { + "$ref": "#/definitions/Struct" } }, "required": [ @@ -76,27 +76,27 @@ { "type": "object", "properties": { + "t": { + "type": "string", + "enum": [ + "Struct" + ] + }, "c": { "type": "object", "properties": { - "bar": { - "type": "boolean" - }, "foo": { "type": "integer", "format": "int32" + }, + "bar": { + "type": "boolean" } }, "required": [ "foo", "bar" ] - }, - "t": { - "type": "string", - "enum": [ - "Struct" - ] } }, "required": [ @@ -107,6 +107,12 @@ { "type": "object", "properties": { + "t": { + "type": "string", + "enum": [ + "Tuple" + ] + }, "c": { "type": "array", "items": [ @@ -118,14 +124,8 @@ "type": "boolean" } ], - "maxItems": 2, - "minItems": 2 - }, - "t": { - "type": "string", - "enum": [ - "Tuple" - ] + "minItems": 2, + "maxItems": 2 } }, "required": [ @@ -150,15 +150,15 @@ { "type": "object", "properties": { - "c": { - "type": "integer", - "format": "int32" - }, "t": { "type": "string", "enum": [ "WithInt" ] + }, + "c": { + "type": "integer", + "format": "int32" } }, "required": [ @@ -168,24 +168,24 @@ } ], "definitions": { + "UnitStruct": { + "type": "null" + }, "Struct": { "type": "object", "properties": { - "bar": { - "type": "boolean" - }, "foo": { "type": "integer", "format": "int32" + }, + "bar": { + "type": "boolean" } }, "required": [ "foo", "bar" ] - }, - "UnitStruct": { - "type": "null" } } } \ No newline at end of file diff --git a/schemars/tests/expected/enum-external-duf.json b/schemars/tests/expected/enum-external-duf.json index 0351fa2..a483535 100644 --- a/schemars/tests/expected/enum-external-duf.json +++ b/schemars/tests/expected/enum-external-duf.json @@ -19,10 +19,10 @@ } } }, - "additionalProperties": false, "required": [ "stringMap" - ] + ], + "additionalProperties": false }, { "type": "object", @@ -31,10 +31,10 @@ "$ref": "#/definitions/UnitStruct" } }, - "additionalProperties": false, "required": [ "unitStructNewType" - ] + ], + "additionalProperties": false }, { "type": "object", @@ -43,10 +43,10 @@ "$ref": "#/definitions/Struct" } }, - "additionalProperties": false, "required": [ "structNewType" - ] + ], + "additionalProperties": false }, { "type": "object", @@ -54,12 +54,12 @@ "struct": { "type": "object", "properties": { - "bar": { - "type": "boolean" - }, "foo": { "type": "integer", "format": "int32" + }, + "bar": { + "type": "boolean" } }, "additionalProperties": false, @@ -69,10 +69,10 @@ ] } }, - "additionalProperties": false, "required": [ "struct" - ] + ], + "additionalProperties": false }, { "type": "object", @@ -88,14 +88,14 @@ "type": "boolean" } ], - "maxItems": 2, - "minItems": 2 + "minItems": 2, + "maxItems": 2 } }, - "additionalProperties": false, "required": [ "tuple" - ] + ], + "additionalProperties": false }, { "type": "object", @@ -105,31 +105,31 @@ "format": "int32" } }, - "additionalProperties": false, "required": [ "withInt" - ] + ], + "additionalProperties": false } ], "definitions": { + "UnitStruct": { + "type": "null" + }, "Struct": { "type": "object", "properties": { - "bar": { - "type": "boolean" - }, "foo": { "type": "integer", "format": "int32" + }, + "bar": { + "type": "boolean" } }, "required": [ "foo", "bar" ] - }, - "UnitStruct": { - "type": "null" } } } \ No newline at end of file diff --git a/schemars/tests/expected/enum-external.json b/schemars/tests/expected/enum-external.json index 4c1d5a6..8a1de2b 100644 --- a/schemars/tests/expected/enum-external.json +++ b/schemars/tests/expected/enum-external.json @@ -19,10 +19,10 @@ } } }, - "additionalProperties": false, "required": [ "stringMap" - ] + ], + "additionalProperties": false }, { "type": "object", @@ -31,10 +31,10 @@ "$ref": "#/definitions/UnitStruct" } }, - "additionalProperties": false, "required": [ "unitStructNewType" - ] + ], + "additionalProperties": false }, { "type": "object", @@ -43,10 +43,10 @@ "$ref": "#/definitions/Struct" } }, - "additionalProperties": false, "required": [ "structNewType" - ] + ], + "additionalProperties": false }, { "type": "object", @@ -54,12 +54,12 @@ "struct": { "type": "object", "properties": { - "bar": { - "type": "boolean" - }, "foo": { "type": "integer", "format": "int32" + }, + "bar": { + "type": "boolean" } }, "required": [ @@ -68,10 +68,10 @@ ] } }, - "additionalProperties": false, "required": [ "struct" - ] + ], + "additionalProperties": false }, { "type": "object", @@ -87,14 +87,14 @@ "type": "boolean" } ], - "maxItems": 2, - "minItems": 2 + "minItems": 2, + "maxItems": 2 } }, - "additionalProperties": false, "required": [ "tuple" - ] + ], + "additionalProperties": false }, { "type": "object", @@ -104,31 +104,31 @@ "format": "int32" } }, - "additionalProperties": false, "required": [ "withInt" - ] + ], + "additionalProperties": false } ], "definitions": { + "UnitStruct": { + "type": "null" + }, "Struct": { "type": "object", "properties": { - "bar": { - "type": "boolean" - }, "foo": { "type": "integer", "format": "int32" + }, + "bar": { + "type": "boolean" } }, "required": [ "foo", "bar" ] - }, - "UnitStruct": { - "type": "null" } } } \ No newline at end of file diff --git a/schemars/tests/expected/enum-internal-duf.json b/schemars/tests/expected/enum-internal-duf.json index 9c2aa9c..de51ba1 100644 --- a/schemars/tests/expected/enum-internal-duf.json +++ b/schemars/tests/expected/enum-internal-duf.json @@ -10,10 +10,10 @@ "const": "UnitOne" } }, - "additionalProperties": false, "required": [ "typeProperty" - ] + ], + "additionalProperties": false }, { "type": "object", @@ -38,21 +38,21 @@ "const": "UnitStructNewType" } }, - "additionalProperties": false, "required": [ "typeProperty" - ] + ], + "additionalProperties": false }, { "type": "object", "properties": { - "bar": { - "type": "boolean" - }, "foo": { "type": "integer", "format": "int32" }, + "bar": { + "type": "boolean" + }, "typeProperty": { "type": "string", "const": "StructNewType" @@ -67,13 +67,13 @@ { "type": "object", "properties": { - "bar": { - "type": "boolean" - }, "foo": { "type": "integer", "format": "int32" }, + "bar": { + "type": "boolean" + }, "typeProperty": { "type": "string", "const": "Struct" @@ -94,10 +94,10 @@ "const": "UnitTwo" } }, - "additionalProperties": false, "required": [ "typeProperty" - ] + ], + "additionalProperties": false }, { "type": "object", diff --git a/schemars/tests/expected/enum-internal.json b/schemars/tests/expected/enum-internal.json index 7fd931c..a5dffe3 100644 --- a/schemars/tests/expected/enum-internal.json +++ b/schemars/tests/expected/enum-internal.json @@ -44,13 +44,13 @@ { "type": "object", "properties": { - "bar": { - "type": "boolean" - }, "foo": { "type": "integer", "format": "int32" }, + "bar": { + "type": "boolean" + }, "typeProperty": { "type": "string", "const": "StructNewType" @@ -65,13 +65,13 @@ { "type": "object", "properties": { - "bar": { - "type": "boolean" - }, "foo": { "type": "integer", "format": "int32" }, + "bar": { + "type": "boolean" + }, "typeProperty": { "type": "string", "const": "Struct" diff --git a/schemars/tests/expected/enum-simple-internal-duf.json b/schemars/tests/expected/enum-simple-internal-duf.json index 4fdf04e..7fc20c1 100644 --- a/schemars/tests/expected/enum-simple-internal-duf.json +++ b/schemars/tests/expected/enum-simple-internal-duf.json @@ -10,10 +10,10 @@ "const": "A" } }, - "additionalProperties": false, "required": [ "typeProperty" - ] + ], + "additionalProperties": false }, { "type": "object", @@ -23,10 +23,10 @@ "const": "B" } }, - "additionalProperties": false, "required": [ "typeProperty" - ] + ], + "additionalProperties": false }, { "type": "object", @@ -36,10 +36,10 @@ "const": "C" } }, - "additionalProperties": false, "required": [ "typeProperty" - ] + ], + "additionalProperties": false } ] } \ No newline at end of file diff --git a/schemars/tests/expected/enum-untagged-duf.json b/schemars/tests/expected/enum-untagged-duf.json index e3f7c66..13a7d0a 100644 --- a/schemars/tests/expected/enum-untagged-duf.json +++ b/schemars/tests/expected/enum-untagged-duf.json @@ -20,12 +20,12 @@ { "type": "object", "properties": { - "bar": { - "type": "boolean" - }, "foo": { "type": "integer", "format": "int32" + }, + "bar": { + "type": "boolean" } }, "additionalProperties": false, @@ -45,8 +45,8 @@ "type": "boolean" } ], - "maxItems": 2, - "minItems": 2 + "minItems": 2, + "maxItems": 2 }, { "type": "integer", @@ -54,24 +54,24 @@ } ], "definitions": { + "UnitStruct": { + "type": "null" + }, "Struct": { "type": "object", "properties": { - "bar": { - "type": "boolean" - }, "foo": { "type": "integer", "format": "int32" + }, + "bar": { + "type": "boolean" } }, "required": [ "foo", "bar" ] - }, - "UnitStruct": { - "type": "null" } } } \ No newline at end of file diff --git a/schemars/tests/expected/enum-untagged.json b/schemars/tests/expected/enum-untagged.json index 2b23ffb..ed72b43 100644 --- a/schemars/tests/expected/enum-untagged.json +++ b/schemars/tests/expected/enum-untagged.json @@ -20,12 +20,12 @@ { "type": "object", "properties": { - "bar": { - "type": "boolean" - }, "foo": { "type": "integer", "format": "int32" + }, + "bar": { + "type": "boolean" } }, "required": [ @@ -44,8 +44,8 @@ "type": "boolean" } ], - "maxItems": 2, - "minItems": 2 + "minItems": 2, + "maxItems": 2 }, { "type": "integer", @@ -53,24 +53,24 @@ } ], "definitions": { + "UnitStruct": { + "type": "null" + }, "Struct": { "type": "object", "properties": { - "bar": { - "type": "boolean" - }, "foo": { "type": "integer", "format": "int32" + }, + "bar": { + "type": "boolean" } }, "required": [ "foo", "bar" ] - }, - "UnitStruct": { - "type": "null" } } } \ No newline at end of file diff --git a/schemars/tests/expected/enumset.json b/schemars/tests/expected/enumset.json index b4dc26a..0f9baa2 100644 --- a/schemars/tests/expected/enumset.json +++ b/schemars/tests/expected/enumset.json @@ -2,10 +2,10 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Set_of_Foo", "type": "array", + "uniqueItems": true, "items": { "$ref": "#/definitions/Foo" }, - "uniqueItems": true, "definitions": { "Foo": { "type": "string", diff --git a/schemars/tests/expected/examples.json b/schemars/tests/expected/examples.json index 3203c4a..9f0a505 100644 --- a/schemars/tests/expected/examples.json +++ b/schemars/tests/expected/examples.json @@ -3,6 +3,14 @@ "title": "Struct", "type": "object", "properties": { + "foo": { + "type": "integer", + "format": "int32", + "examples": [ + 8, + null + ] + }, "bar": { "type": "boolean" }, @@ -14,26 +22,18 @@ "examples": [ null ] - }, - "foo": { - "type": "integer", - "format": "int32", - "examples": [ - 8, - null - ] } }, + "required": [ + "foo", + "bar" + ], "examples": [ { + "foo": 0, "bar": false, - "baz": null, - "foo": 0 + "baz": null }, null - ], - "required": [ - "foo", - "bar" ] } \ No newline at end of file diff --git a/schemars/tests/expected/flatten.json b/schemars/tests/expected/flatten.json index 8f77dd2..4ea7094 100644 --- a/schemars/tests/expected/flatten.json +++ b/schemars/tests/expected/flatten.json @@ -3,20 +3,20 @@ "title": "Flat", "type": "object", "properties": { - "b": { - "type": "boolean" - }, "f": { "type": "number", "format": "float" }, - "os": { - "type": "string", - "default": "" + "b": { + "type": "boolean" }, "s": { "type": "string" }, + "os": { + "type": "string", + "default": "" + }, "v": { "type": "array", "items": { diff --git a/schemars/tests/expected/from_json_value.json b/schemars/tests/expected/from_json_value.json index cd1c634..6cc1ecc 100644 --- a/schemars/tests/expected/from_json_value.json +++ b/schemars/tests/expected/from_json_value.json @@ -2,12 +2,21 @@ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { - "bool": { - "type": "boolean" + "zero": { + "type": "integer" + }, + "one": { + "type": "integer" }, "minusOne": { "type": "integer" }, + "zeroPointZero": { + "type": "number" + }, + "bool": { + "type": "boolean" + }, "null": true, "object": { "type": "object", @@ -19,31 +28,22 @@ } } } - }, - "one": { - "type": "integer" - }, - "zero": { - "type": "integer" - }, - "zeroPointZero": { - "type": "number" } }, "examples": [ { - "bool": true, + "zero": 0, + "one": 1, "minusOne": -1, + "zeroPointZero": 0.0, + "bool": true, "null": null, "object": { "array": [ "foo", "bar" ] - }, - "one": 1, - "zero": 0, - "zeroPointZero": 0.0 + } } ] } \ No newline at end of file diff --git a/schemars/tests/expected/from_value_2019_09.json b/schemars/tests/expected/from_value_2019_09.json index 4c6adcf..52c0524 100644 --- a/schemars/tests/expected/from_value_2019_09.json +++ b/schemars/tests/expected/from_value_2019_09.json @@ -3,12 +3,28 @@ "title": "MyStruct", "type": "object", "properties": { + "myInt": { + "type": "integer" + }, "myBool": { "type": "boolean" }, + "myNullableEnum": true, "myInnerStruct": { "type": "object", "properties": { + "my_map": { + "type": "object", + "additionalProperties": { + "type": "number" + } + }, + "my_vec": { + "type": "array", + "items": { + "type": "string" + } + }, "my_empty_map": { "type": "object", "additionalProperties": true @@ -17,19 +33,13 @@ "type": "array", "items": true }, - "my_map": { - "type": "object", - "additionalProperties": { - "type": "number" - } - }, "my_tuple": { "type": "array", "items": [ { "type": "string", - "maxLength": 1, - "minLength": 1 + "minLength": 1, + "maxLength": 1 }, { "type": "integer" @@ -37,40 +47,30 @@ ], "maxItems": 2, "minItems": 2 - }, - "my_vec": { - "type": "array", - "items": { - "type": "string" - } } } - }, - "myInt": { - "type": "integer" - }, - "myNullableEnum": true + } }, "examples": [ { + "myInt": 123, "myBool": true, + "myNullableEnum": null, "myInnerStruct": { - "my_empty_map": {}, - "my_empty_vec": [], "my_map": { "": 0.0 }, - "my_tuple": [ - "💩", - 42 - ], "my_vec": [ "hello", "world" + ], + "my_empty_map": {}, + "my_empty_vec": [], + "my_tuple": [ + "💩", + 42 ] - }, - "myInt": 123, - "myNullableEnum": null + } } ] } \ No newline at end of file diff --git a/schemars/tests/expected/from_value_draft07.json b/schemars/tests/expected/from_value_draft07.json index 5d87f02..de89fad 100644 --- a/schemars/tests/expected/from_value_draft07.json +++ b/schemars/tests/expected/from_value_draft07.json @@ -3,12 +3,28 @@ "title": "MyStruct", "type": "object", "properties": { + "myInt": { + "type": "integer" + }, "myBool": { "type": "boolean" }, + "myNullableEnum": true, "myInnerStruct": { "type": "object", "properties": { + "my_map": { + "type": "object", + "additionalProperties": { + "type": "number" + } + }, + "my_vec": { + "type": "array", + "items": { + "type": "string" + } + }, "my_empty_map": { "type": "object", "additionalProperties": true @@ -17,19 +33,13 @@ "type": "array", "items": true }, - "my_map": { - "type": "object", - "additionalProperties": { - "type": "number" - } - }, "my_tuple": { "type": "array", "items": [ { "type": "string", - "maxLength": 1, - "minLength": 1 + "minLength": 1, + "maxLength": 1 }, { "type": "integer" @@ -37,40 +47,30 @@ ], "maxItems": 2, "minItems": 2 - }, - "my_vec": { - "type": "array", - "items": { - "type": "string" - } } } - }, - "myInt": { - "type": "integer" - }, - "myNullableEnum": true + } }, "examples": [ { + "myInt": 123, "myBool": true, + "myNullableEnum": null, "myInnerStruct": { - "my_empty_map": {}, - "my_empty_vec": [], "my_map": { "": 0.0 }, - "my_tuple": [ - "💩", - 42 - ], "my_vec": [ "hello", "world" + ], + "my_empty_map": {}, + "my_empty_vec": [], + "my_tuple": [ + "💩", + 42 ] - }, - "myInt": 123, - "myNullableEnum": null + } } ] } \ No newline at end of file diff --git a/schemars/tests/expected/from_value_openapi3.json b/schemars/tests/expected/from_value_openapi3.json index 0405935..4e9dd2c 100644 --- a/schemars/tests/expected/from_value_openapi3.json +++ b/schemars/tests/expected/from_value_openapi3.json @@ -3,12 +3,30 @@ "title": "MyStruct", "type": "object", "properties": { + "myInt": { + "type": "integer" + }, "myBool": { "type": "boolean" }, + "myNullableEnum": { + "nullable": true + }, "myInnerStruct": { "type": "object", "properties": { + "my_map": { + "type": "object", + "additionalProperties": { + "type": "number" + } + }, + "my_vec": { + "type": "array", + "items": { + "type": "string" + } + }, "my_empty_map": { "type": "object", "additionalProperties": true @@ -17,19 +35,13 @@ "type": "array", "items": {} }, - "my_map": { - "type": "object", - "additionalProperties": { - "type": "number" - } - }, "my_tuple": { "type": "array", "items": [ { "type": "string", - "maxLength": 1, - "minLength": 1 + "minLength": 1, + "maxLength": 1 }, { "type": "integer" @@ -37,40 +49,28 @@ ], "maxItems": 2, "minItems": 2 - }, - "my_vec": { - "type": "array", - "items": { - "type": "string" - } } } - }, - "myInt": { - "type": "integer" - }, - "myNullableEnum": { - "nullable": true } }, "example": { + "myInt": 123, "myBool": true, + "myNullableEnum": null, "myInnerStruct": { - "my_empty_map": {}, - "my_empty_vec": [], "my_map": { "": 0.0 }, - "my_tuple": [ - "💩", - 42 - ], "my_vec": [ "hello", "world" + ], + "my_empty_map": {}, + "my_empty_vec": [], + "my_tuple": [ + "💩", + 42 ] - }, - "myInt": 123, - "myNullableEnum": null + } } } \ No newline at end of file diff --git a/schemars/tests/expected/indexmap.json b/schemars/tests/expected/indexmap.json index 9c209e6..318bf9b 100644 --- a/schemars/tests/expected/indexmap.json +++ b/schemars/tests/expected/indexmap.json @@ -11,11 +11,11 @@ }, "set": { "type": "array", + "uniqueItems": true, "items": { "type": "integer", "format": "int" - }, - "uniqueItems": true + } } }, "required": [ diff --git a/schemars/tests/expected/macro_built_enum.json b/schemars/tests/expected/macro_built_enum.json index 51a9bb7..4fedbb2 100644 --- a/schemars/tests/expected/macro_built_enum.json +++ b/schemars/tests/expected/macro_built_enum.json @@ -9,10 +9,10 @@ "$ref": "#/definitions/InnerStruct" } }, - "additionalProperties": false, "required": [ "InnerStruct" - ] + ], + "additionalProperties": false } ], "definitions": { diff --git a/schemars/tests/expected/macro_built_struct.json b/schemars/tests/expected/macro_built_struct.json index e958c8e..74a279c 100644 --- a/schemars/tests/expected/macro_built_struct.json +++ b/schemars/tests/expected/macro_built_struct.json @@ -3,14 +3,14 @@ "title": "A", "type": "object", "properties": { - "v": { - "type": "integer", - "format": "int32" - }, "x": { "type": "integer", "format": "uint8", "minimum": 0 + }, + "v": { + "type": "integer", + "format": "int32" } }, "required": [ diff --git a/schemars/tests/expected/nonzero_ints.json b/schemars/tests/expected/nonzero_ints.json index 6ee5105..97dee99 100644 --- a/schemars/tests/expected/nonzero_ints.json +++ b/schemars/tests/expected/nonzero_ints.json @@ -3,12 +3,10 @@ "title": "MyStruct", "type": "object", "properties": { - "nonzero_signed": { + "unsigned": { "type": "integer", - "format": "int32", - "not": { - "const": 0 - } + "format": "uint32", + "minimum": 0 }, "nonzero_unsigned": { "type": "integer", @@ -19,10 +17,12 @@ "type": "integer", "format": "int32" }, - "unsigned": { + "nonzero_signed": { "type": "integer", - "format": "uint32", - "minimum": 0 + "format": "int32", + "not": { + "const": 0 + } } }, "required": [ diff --git a/schemars/tests/expected/os_strings.json b/schemars/tests/expected/os_strings.json index 927f3f9..b422026 100644 --- a/schemars/tests/expected/os_strings.json +++ b/schemars/tests/expected/os_strings.json @@ -3,10 +3,10 @@ "title": "OsStrings", "type": "object", "properties": { - "borrowed": { + "owned": { "$ref": "#/definitions/OsString" }, - "owned": { + "borrowed": { "$ref": "#/definitions/OsString" } }, diff --git a/schemars/tests/expected/range.json b/schemars/tests/expected/range.json index 19e4780..25f7389 100644 --- a/schemars/tests/expected/range.json +++ b/schemars/tests/expected/range.json @@ -3,14 +3,14 @@ "title": "MyStruct", "type": "object", "properties": { - "bound": { - "$ref": "#/definitions/Bound_of_string" + "range": { + "$ref": "#/definitions/Range_of_uint" }, "inclusive": { "$ref": "#/definitions/Range_of_double" }, - "range": { - "$ref": "#/definitions/Range_of_uint" + "bound": { + "$ref": "#/definitions/Bound_of_string" } }, "required": [ @@ -19,6 +19,42 @@ "bound" ], "definitions": { + "Range_of_uint": { + "type": "object", + "properties": { + "start": { + "type": "integer", + "format": "uint", + "minimum": 0 + }, + "end": { + "type": "integer", + "format": "uint", + "minimum": 0 + } + }, + "required": [ + "start", + "end" + ] + }, + "Range_of_double": { + "type": "object", + "properties": { + "start": { + "type": "number", + "format": "double" + }, + "end": { + "type": "number", + "format": "double" + } + }, + "required": [ + "start", + "end" + ] + }, "Bound_of_string": { "oneOf": [ { @@ -48,42 +84,6 @@ "const": "Unbounded" } ] - }, - "Range_of_double": { - "type": "object", - "properties": { - "end": { - "type": "number", - "format": "double" - }, - "start": { - "type": "number", - "format": "double" - } - }, - "required": [ - "start", - "end" - ] - }, - "Range_of_uint": { - "type": "object", - "properties": { - "end": { - "type": "integer", - "format": "uint", - "minimum": 0 - }, - "start": { - "type": "integer", - "format": "uint", - "minimum": 0 - } - }, - "required": [ - "start", - "end" - ] } } } \ No newline at end of file diff --git a/schemars/tests/expected/remote_derive.json b/schemars/tests/expected/remote_derive.json index 9477e0f..0f27ad3 100644 --- a/schemars/tests/expected/remote_derive.json +++ b/schemars/tests/expected/remote_derive.json @@ -6,27 +6,27 @@ "command_line": { "type": "string" }, - "system_cpu_time": { + "wall_time": { + "$ref": "#/definitions/Duration" + }, + "user_cpu_time": { + "default": { + "secs": 0, + "nanos": 0 + }, "allOf": [ { "$ref": "#/definitions/Duration" } - ], - "default": "0.000000000s" + ] }, - "user_cpu_time": { + "system_cpu_time": { + "default": "0.000000000s", "allOf": [ { "$ref": "#/definitions/Duration" } - ], - "default": { - "nanos": 0, - "secs": 0 - } - }, - "wall_time": { - "$ref": "#/definitions/Duration" + ] } }, "required": [ @@ -37,13 +37,13 @@ "Duration": { "type": "object", "properties": { - "nanos": { - "type": "integer", - "format": "int32" - }, "secs": { "type": "integer", "format": "int64" + }, + "nanos": { + "type": "integer", + "format": "int32" } }, "required": [ diff --git a/schemars/tests/expected/remote_derive_generic.json b/schemars/tests/expected/remote_derive_generic.json index b533320..f6ba3ef 100644 --- a/schemars/tests/expected/remote_derive_generic.json +++ b/schemars/tests/expected/remote_derive_generic.json @@ -6,21 +6,21 @@ "byte_or_bool2": { "$ref": "#/definitions/Or_for_uint8_and_boolean" }, + "unit_or_t2": { + "$ref": "#/definitions/Or_for_null_and_int32" + }, + "s": { + "$ref": "#/definitions/Str" + }, "fake_map": { "type": "object", "additionalProperties": { "type": "array", + "uniqueItems": true, "items": { "type": "string" - }, - "uniqueItems": true + } } - }, - "s": { - "$ref": "#/definitions/Str" - }, - "unit_or_t2": { - "$ref": "#/definitions/Or_for_null_and_int32" } }, "required": [ @@ -30,26 +30,26 @@ "fake_map" ], "definitions": { - "Or_for_null_and_int32": { + "Or_for_uint8_and_boolean": { "anyOf": [ { - "type": "null" + "type": "integer", + "format": "uint8", + "minimum": 0 }, { - "type": "integer", - "format": "int32" + "type": "boolean" } ] }, - "Or_for_uint8_and_boolean": { + "Or_for_null_and_int32": { "anyOf": [ { - "type": "integer", - "format": "uint8", - "minimum": 0 + "type": "null" }, { - "type": "boolean" + "type": "integer", + "format": "int32" } ] }, diff --git a/schemars/tests/expected/result.json b/schemars/tests/expected/result.json index 44c079e..8a25a0e 100644 --- a/schemars/tests/expected/result.json +++ b/schemars/tests/expected/result.json @@ -15,18 +15,6 @@ "result2" ], "definitions": { - "MyStruct": { - "type": "object", - "properties": { - "foo": { - "type": "integer", - "format": "int32" - } - }, - "required": [ - "foo" - ] - }, "Result_of_MyStruct_or_Array_of_string": { "oneOf": [ { @@ -56,6 +44,18 @@ } ] }, + "MyStruct": { + "type": "object", + "properties": { + "foo": { + "type": "integer", + "format": "int32" + } + }, + "required": [ + "foo" + ] + }, "Result_of_boolean_or_null": { "oneOf": [ { diff --git a/schemars/tests/expected/schema-name-custom.json b/schemars/tests/expected/schema-name-custom.json index dc5336b..447edbd 100644 --- a/schemars/tests/expected/schema-name-custom.json +++ b/schemars/tests/expected/schema-name-custom.json @@ -3,9 +3,6 @@ "title": "a-new-name-Array_of_string-int32-int32", "type": "object", "properties": { - "inner": { - "$ref": "#/definitions/another-new-name" - }, "t": { "type": "integer", "format": "int32" @@ -21,6 +18,9 @@ "items": { "type": "string" } + }, + "inner": { + "$ref": "#/definitions/another-new-name" } }, "required": [ diff --git a/schemars/tests/expected/schema-name-default.json b/schemars/tests/expected/schema-name-default.json index 7524c63..bd2ab9a 100644 --- a/schemars/tests/expected/schema-name-default.json +++ b/schemars/tests/expected/schema-name-default.json @@ -3,9 +3,6 @@ "title": "MyStruct_for_int32_and_null_and_boolean_and_Array_of_string", "type": "object", "properties": { - "inner": { - "$ref": "#/definitions/MySimpleStruct" - }, "t": { "type": "integer", "format": "int32" @@ -21,6 +18,9 @@ "items": { "type": "string" } + }, + "inner": { + "$ref": "#/definitions/MySimpleStruct" } }, "required": [ diff --git a/schemars/tests/expected/schema-name-mixed-generics.json b/schemars/tests/expected/schema-name-mixed-generics.json index fdadbab..4342410 100644 --- a/schemars/tests/expected/schema-name-mixed-generics.json +++ b/schemars/tests/expected/schema-name-mixed-generics.json @@ -3,12 +3,12 @@ "title": "MixedGenericStruct_for_MyStruct_for_int32_and_null_and_boolean_and_Array_of_string_and_42_and_z", "type": "object", "properties": { + "generic": { + "$ref": "#/definitions/MyStruct_for_int32_and_null_and_boolean_and_Array_of_string" + }, "foo": { "type": "integer", "format": "int32" - }, - "generic": { - "$ref": "#/definitions/MyStruct_for_int32_and_null_and_boolean_and_Array_of_string" } }, "required": [ @@ -16,24 +16,9 @@ "foo" ], "definitions": { - "MySimpleStruct": { - "type": "object", - "properties": { - "foo": { - "type": "integer", - "format": "int32" - } - }, - "required": [ - "foo" - ] - }, "MyStruct_for_int32_and_null_and_boolean_and_Array_of_string": { "type": "object", "properties": { - "inner": { - "$ref": "#/definitions/MySimpleStruct" - }, "t": { "type": "integer", "format": "int32" @@ -49,6 +34,9 @@ "items": { "type": "string" } + }, + "inner": { + "$ref": "#/definitions/MySimpleStruct" } }, "required": [ @@ -58,6 +46,18 @@ "w", "inner" ] + }, + "MySimpleStruct": { + "type": "object", + "properties": { + "foo": { + "type": "integer", + "format": "int32" + } + }, + "required": [ + "foo" + ] } } } \ No newline at end of file diff --git a/schemars/tests/expected/schema_settings-2019_09.json b/schemars/tests/expected/schema_settings-2019_09.json index 947aa2e..22f962f 100644 --- a/schemars/tests/expected/schema_settings-2019_09.json +++ b/schemars/tests/expected/schema_settings-2019_09.json @@ -3,16 +3,6 @@ "title": "Outer", "type": "object", "properties": { - "inner": { - "anyOf": [ - { - "$ref": "#/definitions/Inner" - }, - { - "type": "null" - } - ] - }, "int": { "type": "integer", "format": "int32", @@ -21,10 +11,20 @@ null ] }, - "value": true, "values": { "type": "object", "additionalProperties": true + }, + "value": true, + "inner": { + "anyOf": [ + { + "$ref": "#/definitions/Inner" + }, + { + "type": "null" + } + ] } }, "required": [ @@ -52,10 +52,10 @@ "properties": { "ValueNewType": true }, - "additionalProperties": false, "required": [ "ValueNewType" - ] + ], + "additionalProperties": false } ] } diff --git a/schemars/tests/expected/schema_settings-openapi3.json b/schemars/tests/expected/schema_settings-openapi3.json index b6de302..c4e5199 100644 --- a/schemars/tests/expected/schema_settings-openapi3.json +++ b/schemars/tests/expected/schema_settings-openapi3.json @@ -3,23 +3,23 @@ "title": "Outer", "type": "object", "properties": { - "inner": { - "allOf": [ - { - "$ref": "#/components/schemas/Inner" - } - ], - "nullable": true - }, "int": { "type": "integer", "format": "int32", "example": 8 }, - "value": {}, "values": { "type": "object", "additionalProperties": true + }, + "value": {}, + "inner": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/Inner" + } + ] } }, "required": [ @@ -49,10 +49,10 @@ "properties": { "ValueNewType": {} }, - "additionalProperties": false, "required": [ "ValueNewType" - ] + ], + "additionalProperties": false } ] } diff --git a/schemars/tests/expected/schema_settings.json b/schemars/tests/expected/schema_settings.json index 6a66ca0..4a50262 100644 --- a/schemars/tests/expected/schema_settings.json +++ b/schemars/tests/expected/schema_settings.json @@ -3,16 +3,6 @@ "title": "Outer", "type": "object", "properties": { - "inner": { - "anyOf": [ - { - "$ref": "#/definitions/Inner" - }, - { - "type": "null" - } - ] - }, "int": { "type": "integer", "format": "int32", @@ -21,10 +11,20 @@ null ] }, - "value": true, "values": { "type": "object", "additionalProperties": true + }, + "value": true, + "inner": { + "anyOf": [ + { + "$ref": "#/definitions/Inner" + }, + { + "type": "null" + } + ] } }, "required": [ @@ -52,10 +52,10 @@ "properties": { "ValueNewType": true }, - "additionalProperties": false, "required": [ "ValueNewType" - ] + ], + "additionalProperties": false } ] } diff --git a/schemars/tests/expected/schema_with-enum-adjacent-tagged.json b/schemars/tests/expected/schema_with-enum-adjacent-tagged.json index 5b95671..ab848e3 100644 --- a/schemars/tests/expected/schema_with-enum-adjacent-tagged.json +++ b/schemars/tests/expected/schema_with-enum-adjacent-tagged.json @@ -5,6 +5,12 @@ { "type": "object", "properties": { + "t": { + "type": "string", + "enum": [ + "Struct" + ] + }, "c": { "type": "object", "properties": { @@ -15,12 +21,6 @@ "required": [ "foo" ] - }, - "t": { - "type": "string", - "enum": [ - "Struct" - ] } }, "required": [ @@ -31,14 +31,14 @@ { "type": "object", "properties": { - "c": { - "type": "boolean" - }, "t": { "type": "string", "enum": [ "NewType" ] + }, + "c": { + "type": "boolean" } }, "required": [ @@ -49,6 +49,12 @@ { "type": "object", "properties": { + "t": { + "type": "string", + "enum": [ + "Tuple" + ] + }, "c": { "type": "array", "items": [ @@ -60,14 +66,8 @@ "format": "int32" } ], - "maxItems": 2, - "minItems": 2 - }, - "t": { - "type": "string", - "enum": [ - "Tuple" - ] + "minItems": 2, + "maxItems": 2 } }, "required": [ @@ -78,14 +78,14 @@ { "type": "object", "properties": { - "c": { - "type": "boolean" - }, "t": { "type": "string", "enum": [ "Unit" ] + }, + "c": { + "type": "boolean" } }, "required": [ diff --git a/schemars/tests/expected/schema_with-enum-external.json b/schemars/tests/expected/schema_with-enum-external.json index 78b6475..5f28d2f 100644 --- a/schemars/tests/expected/schema_with-enum-external.json +++ b/schemars/tests/expected/schema_with-enum-external.json @@ -17,10 +17,10 @@ ] } }, - "additionalProperties": false, "required": [ "struct" - ] + ], + "additionalProperties": false }, { "type": "object", @@ -29,10 +29,10 @@ "type": "boolean" } }, - "additionalProperties": false, "required": [ "newType" - ] + ], + "additionalProperties": false }, { "type": "object", @@ -48,14 +48,14 @@ "format": "int32" } ], - "maxItems": 2, - "minItems": 2 + "minItems": 2, + "maxItems": 2 } }, - "additionalProperties": false, "required": [ "tuple" - ] + ], + "additionalProperties": false }, { "type": "object", @@ -64,10 +64,10 @@ "type": "boolean" } }, - "additionalProperties": false, "required": [ "unit" - ] + ], + "additionalProperties": false } ] } \ No newline at end of file diff --git a/schemars/tests/expected/schema_with-enum-untagged.json b/schemars/tests/expected/schema_with-enum-untagged.json index b912feb..834eb41 100644 --- a/schemars/tests/expected/schema_with-enum-untagged.json +++ b/schemars/tests/expected/schema_with-enum-untagged.json @@ -27,8 +27,8 @@ "format": "int32" } ], - "maxItems": 2, - "minItems": 2 + "minItems": 2, + "maxItems": 2 }, { "type": "boolean" diff --git a/schemars/tests/expected/schema_with-struct.json b/schemars/tests/expected/schema_with-struct.json index 7cc55bf..31caebe 100644 --- a/schemars/tests/expected/schema_with-struct.json +++ b/schemars/tests/expected/schema_with-struct.json @@ -3,15 +3,15 @@ "title": "Struct", "type": "object", "properties": { + "foo": { + "type": "boolean" + }, "bar": { "type": "integer", "format": "int32" }, "baz": { "type": "boolean" - }, - "foo": { - "type": "boolean" } }, "required": [ diff --git a/schemars/tests/expected/schema_with-tuple.json b/schemars/tests/expected/schema_with-tuple.json index 1a8f8ca..4e2c095 100644 --- a/schemars/tests/expected/schema_with-tuple.json +++ b/schemars/tests/expected/schema_with-tuple.json @@ -14,6 +14,6 @@ "type": "boolean" } ], - "maxItems": 3, - "minItems": 3 + "minItems": 3, + "maxItems": 3 } \ No newline at end of file diff --git a/schemars/tests/expected/skip_enum_variants.json b/schemars/tests/expected/skip_enum_variants.json index b955e77..d245734 100644 --- a/schemars/tests/expected/skip_enum_variants.json +++ b/schemars/tests/expected/skip_enum_variants.json @@ -16,10 +16,10 @@ "format": "float" } }, - "additionalProperties": false, "required": [ "Included1" - ] + ], + "additionalProperties": false } ] } \ No newline at end of file diff --git a/schemars/tests/expected/skip_struct_fields.json b/schemars/tests/expected/skip_struct_fields.json index 7890298..48549ce 100644 --- a/schemars/tests/expected/skip_struct_fields.json +++ b/schemars/tests/expected/skip_struct_fields.json @@ -3,18 +3,18 @@ "title": "MyStruct", "type": "object", "properties": { - "included": { - "type": "null" - }, "readable": { "type": "string", - "default": "", - "readOnly": true + "readOnly": true, + "default": "" }, "writable": { "type": "number", "format": "float", "writeOnly": true + }, + "included": { + "type": "null" } }, "required": [ diff --git a/schemars/tests/expected/skip_tuple_fields.json b/schemars/tests/expected/skip_tuple_fields.json index 9e5745b..3cdce84 100644 --- a/schemars/tests/expected/skip_tuple_fields.json +++ b/schemars/tests/expected/skip_tuple_fields.json @@ -11,6 +11,6 @@ "type": "null" } ], - "maxItems": 2, - "minItems": 2 + "minItems": 2, + "maxItems": 2 } \ No newline at end of file diff --git a/schemars/tests/expected/struct-normal-additional-properties.json b/schemars/tests/expected/struct-normal-additional-properties.json index 5ff105c..529e2c4 100644 --- a/schemars/tests/expected/struct-normal-additional-properties.json +++ b/schemars/tests/expected/struct-normal-additional-properties.json @@ -3,6 +3,10 @@ "title": "Struct", "type": "object", "properties": { + "foo": { + "type": "integer", + "format": "int32" + }, "bar": { "type": "boolean" }, @@ -11,10 +15,6 @@ "string", "null" ] - }, - "foo": { - "type": "integer", - "format": "int32" } }, "additionalProperties": false, diff --git a/schemars/tests/expected/struct-normal.json b/schemars/tests/expected/struct-normal.json index 0a5a0af..3e73393 100644 --- a/schemars/tests/expected/struct-normal.json +++ b/schemars/tests/expected/struct-normal.json @@ -3,6 +3,10 @@ "title": "Struct", "type": "object", "properties": { + "foo": { + "type": "integer", + "format": "int32" + }, "bar": { "type": "boolean" }, @@ -11,10 +15,6 @@ "string", "null" ] - }, - "foo": { - "type": "integer", - "format": "int32" } }, "required": [ diff --git a/schemars/tests/expected/struct-tuple.json b/schemars/tests/expected/struct-tuple.json index ced169e..c89e416 100644 --- a/schemars/tests/expected/struct-tuple.json +++ b/schemars/tests/expected/struct-tuple.json @@ -17,6 +17,6 @@ ] } ], - "maxItems": 3, - "minItems": 3 + "minItems": 3, + "maxItems": 3 } \ No newline at end of file diff --git a/schemars/tests/expected/transparent-struct.json b/schemars/tests/expected/transparent-struct.json index e83f905..7fc1c32 100644 --- a/schemars/tests/expected/transparent-struct.json +++ b/schemars/tests/expected/transparent-struct.json @@ -26,8 +26,8 @@ "format": "int32" } ], - "maxItems": 2, - "minItems": 2 + "minItems": 2, + "maxItems": 2 } } } \ No newline at end of file diff --git a/schemars/tests/expected/validate.json b/schemars/tests/expected/validate.json index 2d6e43c..04131ce 100644 --- a/schemars/tests/expected/validate.json +++ b/schemars/tests/expected/validate.json @@ -3,6 +3,30 @@ "title": "Struct", "type": "object", "properties": { + "min_max": { + "type": "number", + "format": "float", + "minimum": 0.01, + "maximum": 100 + }, + "min_max2": { + "type": "number", + "format": "float", + "minimum": 1, + "maximum": 1000 + }, + "regex_str1": { + "type": "string", + "pattern": "^[Hh]ello\\b" + }, + "regex_str2": { + "type": "string", + "pattern": "^[Hh]ello\\b" + }, + "regex_str3": { + "type": "string", + "pattern": "^\\d+$" + }, "contains_str1": { "type": "string", "pattern": "substring\\.\\.\\." @@ -15,40 +39,23 @@ "type": "string", "format": "email" }, + "tel": { + "type": "string", + "format": "phone" + }, "homepage": { "type": "string", "format": "uri" }, - "map_contains": { - "type": "object", - "additionalProperties": { - "type": "null" - }, - "required": [ - "map_key" - ] - }, - "min_max": { - "type": "number", - "format": "float", - "maximum": 100, - "minimum": 0.01 - }, - "min_max2": { - "type": "number", - "format": "float", - "maximum": 1000, - "minimum": 1 - }, "non_empty_str": { "type": "string", - "maxLength": 100, - "minLength": 1 + "minLength": 1, + "maxLength": 100 }, "non_empty_str2": { "type": "string", - "maxLength": 1000, - "minLength": 1 + "minLength": 1, + "maxLength": 1000 }, "pair": { "type": "array", @@ -56,28 +63,21 @@ "type": "integer", "format": "int32" }, - "maxItems": 2, - "minItems": 2 - }, - "regex_str1": { - "type": "string", - "pattern": "^[Hh]ello\\b" - }, - "regex_str2": { - "type": "string", - "pattern": "^[Hh]ello\\b" + "minItems": 2, + "maxItems": 2 }, - "regex_str3": { - "type": "string", - "pattern": "^\\d+$" + "map_contains": { + "type": "object", + "additionalProperties": { + "type": "null" + }, + "required": [ + "map_key" + ] }, "required_option": { "type": "boolean" }, - "tel": { - "type": "string", - "format": "phone" - }, "x": { "type": "integer", "format": "int32" diff --git a/schemars/tests/expected/validate_inner.json b/schemars/tests/expected/validate_inner.json index f8a7eca..ba4ee9e 100644 --- a/schemars/tests/expected/validate_inner.json +++ b/schemars/tests/expected/validate_inner.json @@ -7,11 +7,11 @@ "type": "array", "items": { "type": "string", - "maxLength": 100, - "minLength": 5 + "minLength": 5, + "maxLength": 100 }, - "maxItems": 2, - "minItems": 2 + "minItems": 2, + "maxItems": 2 }, "slice_str_contains": { "type": "array", @@ -20,45 +20,45 @@ "pattern": "substring\\.\\.\\." } }, - "vec_i32_range": { + "vec_str_regex": { "type": "array", "items": { - "type": "integer", - "format": "int32", - "maximum": 10, - "minimum": -10 + "type": "string", + "pattern": "^[Hh]ello\\b" } }, "vec_str_length": { "type": "array", "items": { "type": "string", - "maxLength": 100, - "minLength": 1 + "minLength": 1, + "maxLength": 100 } }, "vec_str_length2": { "type": "array", "items": { "type": "string", - "maxLength": 100, - "minLength": 1 + "minLength": 1, + "maxLength": 100 }, - "maxItems": 3, - "minItems": 1 + "minItems": 1, + "maxItems": 3 }, - "vec_str_regex": { + "vec_str_url": { "type": "array", "items": { "type": "string", - "pattern": "^[Hh]ello\\b" + "format": "uri" } }, - "vec_str_url": { + "vec_i32_range": { "type": "array", "items": { - "type": "string", - "format": "uri" + "type": "integer", + "format": "int32", + "minimum": -10, + "maximum": 10 } } }, diff --git a/schemars/tests/expected/validate_newtype.json b/schemars/tests/expected/validate_newtype.json index 4c1146e..89bec96 100644 --- a/schemars/tests/expected/validate_newtype.json +++ b/schemars/tests/expected/validate_newtype.json @@ -3,6 +3,6 @@ "title": "NewType", "type": "integer", "format": "uint8", - "maximum": 10, - "minimum": 0 + "minimum": 0, + "maximum": 10 } \ No newline at end of file diff --git a/schemars/tests/expected/validate_schemars_attrs.json b/schemars/tests/expected/validate_schemars_attrs.json index fa2dbcd..4fdba53 100644 --- a/schemars/tests/expected/validate_schemars_attrs.json +++ b/schemars/tests/expected/validate_schemars_attrs.json @@ -3,6 +3,30 @@ "title": "Struct2", "type": "object", "properties": { + "min_max": { + "type": "number", + "format": "float", + "minimum": 0.01, + "maximum": 100 + }, + "min_max2": { + "type": "number", + "format": "float", + "minimum": 1, + "maximum": 1000 + }, + "regex_str1": { + "type": "string", + "pattern": "^[Hh]ello\\b" + }, + "regex_str2": { + "type": "string", + "pattern": "^[Hh]ello\\b" + }, + "regex_str3": { + "type": "string", + "pattern": "^\\d+$" + }, "contains_str1": { "type": "string", "pattern": "substring\\.\\.\\." @@ -15,40 +39,23 @@ "type": "string", "format": "email" }, + "tel": { + "type": "string", + "format": "phone" + }, "homepage": { "type": "string", "format": "uri" }, - "map_contains": { - "type": "object", - "additionalProperties": { - "type": "null" - }, - "required": [ - "map_key" - ] - }, - "min_max": { - "type": "number", - "format": "float", - "maximum": 100, - "minimum": 0.01 - }, - "min_max2": { - "type": "number", - "format": "float", - "maximum": 1000, - "minimum": 1 - }, "non_empty_str": { "type": "string", - "maxLength": 100, - "minLength": 1 + "minLength": 1, + "maxLength": 100 }, "non_empty_str2": { "type": "string", - "maxLength": 1000, - "minLength": 1 + "minLength": 1, + "maxLength": 1000 }, "pair": { "type": "array", @@ -56,28 +63,21 @@ "type": "integer", "format": "int32" }, - "maxItems": 2, - "minItems": 2 - }, - "regex_str1": { - "type": "string", - "pattern": "^[Hh]ello\\b" - }, - "regex_str2": { - "type": "string", - "pattern": "^[Hh]ello\\b" + "minItems": 2, + "maxItems": 2 }, - "regex_str3": { - "type": "string", - "pattern": "^\\d+$" + "map_contains": { + "type": "object", + "additionalProperties": { + "type": "null" + }, + "required": [ + "map_key" + ] }, "required_option": { "type": "boolean" }, - "tel": { - "type": "string", - "format": "phone" - }, "x": { "type": "integer", "format": "int32" diff --git a/schemars/tests/expected/validate_tuple.json b/schemars/tests/expected/validate_tuple.json index 619b8c4..2a0e266 100644 --- a/schemars/tests/expected/validate_tuple.json +++ b/schemars/tests/expected/validate_tuple.json @@ -6,13 +6,13 @@ { "type": "integer", "format": "uint8", - "maximum": 10, - "minimum": 0 + "minimum": 0, + "maximum": 10 }, { "type": "boolean" } ], - "maxItems": 2, - "minItems": 2 + "minItems": 2, + "maxItems": 2 } \ No newline at end of file From a2de062ad7c338419bc2539b45dfd94079425eab Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Sun, 19 May 2024 18:07:51 +0100 Subject: [PATCH 11/31] Use `$defs` instead of `definitions` in draft 2019-09 This also changes the strategy for running visitors on subschemas - it is now done eagerly, as soon as they're added to the definitions map. --- schemars/src/gen.rs | 106 +++++++++++------- schemars/src/visit.rs | 2 +- .../doc_comments_struct_ref_siblings.json | 4 +- .../expected/schema_settings-2019_09.json | 4 +- .../expected/schema_settings-openapi3.json | 56 ++++----- 5 files changed, 98 insertions(+), 74 deletions(-) diff --git a/schemars/src/gen.rs b/schemars/src/gen.rs index 20d2603..7d1d7d2 100644 --- a/schemars/src/gen.rs +++ b/schemars/src/gen.rs @@ -75,7 +75,7 @@ impl SchemaSettings { SchemaSettings { option_nullable: false, option_add_null_type: true, - definitions_path: "#/definitions/".to_owned(), + definitions_path: "#/$defs/".to_owned(), meta_schema: Some("https://json-schema.org/draft/2019-09/schema".to_owned()), visitors: Vec::new(), inline_subschemas: false, @@ -256,7 +256,8 @@ impl SchemaGenerator { // insert into definitions BEFORE calling json_schema to avoid infinite recursion self.definitions.insert(name.clone(), dummy); - let schema = self.json_schema_internal::(id); + let mut schema = self.json_schema_internal::(id); + Self::run_visitors(&mut schema, &mut self.settings.visitors); self.definitions.insert(name, schema.to_value()); } @@ -309,16 +310,12 @@ impl SchemaGenerator { object.insert("$schema".into(), meta_schema.into()); } - if !self.definitions.is_empty() { - object.insert( - "definitions".into(), - serde_json::Value::Object(self.definitions.clone()), - ); - } - - for visitor in &mut self.settings.visitors { - visitor.visit_schema(&mut schema); - } + Self::add_definitions( + object, + self.definitions.clone(), + &self.settings.definitions_path, + ); + Self::run_visitors(&mut schema, &mut self.settings.visitors); schema } @@ -340,16 +337,8 @@ impl SchemaGenerator { object.insert("$schema".into(), meta_schema.into()); } - if !self.definitions.is_empty() { - object.insert( - "definitions".into(), - serde_json::Value::Object(self.definitions), - ); - } - - for visitor in &mut self.settings.visitors { - visitor.visit_schema(&mut schema); - } + Self::add_definitions(object, self.definitions, &self.settings.definitions_path); + Self::run_visitors(&mut schema, &mut self.settings.visitors); schema } @@ -377,16 +366,12 @@ impl SchemaGenerator { object.insert("$schema".into(), meta_schema.into()); } - if !self.definitions.is_empty() { - object.insert( - "definitions".into(), - serde_json::Value::Object(self.definitions.clone()), - ); - } - - for visitor in &mut self.settings.visitors { - visitor.visit_schema(&mut schema); - } + Self::add_definitions( + object, + self.definitions.clone(), + &self.settings.definitions_path, + ); + Self::run_visitors(&mut schema, &mut self.settings.visitors); Ok(schema) } @@ -414,16 +399,8 @@ impl SchemaGenerator { object.insert("$schema".into(), meta_schema.into()); } - if !self.definitions.is_empty() { - object.insert( - "definitions".into(), - serde_json::Value::Object(self.definitions), - ); - } - - for visitor in &mut self.settings.visitors { - visitor.visit_schema(&mut schema); - } + Self::add_definitions(object, self.definitions, &self.settings.definitions_path); + Self::run_visitors(&mut schema, &mut self.settings.visitors); Ok(schema) } @@ -453,6 +430,51 @@ impl SchemaGenerator { let pss = PendingSchemaState::new(self, id); T::json_schema(pss.gen) } + + fn add_definitions( + schema_object: &mut Map, + mut definitions: Map, + path: &str, + ) { + if definitions.is_empty() { + return; + } + + let target = match Self::json_pointer(schema_object, path) { + Some(d) => d, + None => return, + }; + + target.append(&mut definitions); + } + + fn json_pointer<'a>( + mut object: &'a mut Map, + pointer: &str, + ) -> Option<&'a mut Map> { + let segments = pointer.strip_prefix("#/")?.strip_suffix('/')?.split('/'); + + for mut segment in segments { + let replaced: String; + if segment.contains('~') { + replaced = segment.replace("~1", "/").replace("~0", "~"); + segment = &replaced; + } + + object = object + .entry(segment) + .or_insert(Value::Object(Map::default())) + .as_object_mut()?; + } + + Some(object) + } + + fn run_visitors(schema: &mut Schema, visitors: &mut [Box]) { + for visitor in visitors { + visitor.visit_schema(schema); + } + } } /// A [Visitor](Visitor) which implements additional traits required to be included in a [SchemaSettings]. diff --git a/schemars/src/visit.rs b/schemars/src/visit.rs index 285aae2..9af8482 100644 --- a/schemars/src/visit.rs +++ b/schemars/src/visit.rs @@ -79,7 +79,7 @@ pub fn visit_schema(v: &mut V, schema: &mut Schema) { v.visit_schema(subschema) } } - "properties" | "patternProperties" | "definitions" | "$defs" => { + "properties" | "patternProperties" => { if let Some(obj) = value.as_object_mut() { for value in obj.values_mut() { if let Ok(subschema) = value.try_into() { diff --git a/schemars/tests/expected/doc_comments_struct_ref_siblings.json b/schemars/tests/expected/doc_comments_struct_ref_siblings.json index a3dcbe0..6b6caeb 100644 --- a/schemars/tests/expected/doc_comments_struct_ref_siblings.json +++ b/schemars/tests/expected/doc_comments_struct_ref_siblings.json @@ -14,7 +14,7 @@ }, "my_unit": { "description": "A unit struct instance", - "$ref": "#/definitions/MyUnitStruct" + "$ref": "#/$defs/MyUnitStruct" } }, "required": [ @@ -22,7 +22,7 @@ "my_undocumented_bool", "my_unit" ], - "definitions": { + "$defs": { "MyUnitStruct": { "title": "A Unit", "type": "null" diff --git a/schemars/tests/expected/schema_settings-2019_09.json b/schemars/tests/expected/schema_settings-2019_09.json index 22f962f..568ce85 100644 --- a/schemars/tests/expected/schema_settings-2019_09.json +++ b/schemars/tests/expected/schema_settings-2019_09.json @@ -19,7 +19,7 @@ "inner": { "anyOf": [ { - "$ref": "#/definitions/Inner" + "$ref": "#/$defs/Inner" }, { "type": "null" @@ -32,7 +32,7 @@ "values", "value" ], - "definitions": { + "$defs": { "Inner": { "oneOf": [ { diff --git a/schemars/tests/expected/schema_settings-openapi3.json b/schemars/tests/expected/schema_settings-openapi3.json index c4e5199..6ff2604 100644 --- a/schemars/tests/expected/schema_settings-openapi3.json +++ b/schemars/tests/expected/schema_settings-openapi3.json @@ -27,34 +27,36 @@ "values", "value" ], - "definitions": { - "Inner": { - "oneOf": [ - { - "type": "string", - "enum": [ - "UndocumentedUnit1", - "UndocumentedUnit2" - ] - }, - { - "description": "This is a documented unit variant", - "type": "string", - "enum": [ - "DocumentedUnit" - ] - }, - { - "type": "object", - "properties": { - "ValueNewType": {} + "components": { + "schemas": { + "Inner": { + "oneOf": [ + { + "type": "string", + "enum": [ + "UndocumentedUnit1", + "UndocumentedUnit2" + ] }, - "required": [ - "ValueNewType" - ], - "additionalProperties": false - } - ] + { + "description": "This is a documented unit variant", + "type": "string", + "enum": [ + "DocumentedUnit" + ] + }, + { + "type": "object", + "properties": { + "ValueNewType": {} + }, + "required": [ + "ValueNewType" + ], + "additionalProperties": false + } + ] + } } } } \ No newline at end of file From 46369e3cac01f5130a54b8ecbd7b9903fa494c17 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Sun, 19 May 2024 20:49:45 +0100 Subject: [PATCH 12/31] Support JSON Schema draft 2020-12 and use it by default (#294) --- schemars/src/gen.rs | 33 +++++--- schemars/src/json_schema_impls/tuple.rs | 2 +- schemars/src/ser.rs | 2 +- schemars/src/visit.rs | 43 ++++++---- schemars/tests/expected/arrayvec.json | 2 +- schemars/tests/expected/arrayvec_string.json | 2 +- schemars/tests/expected/bigdecimal04.json | 2 +- schemars/tests/expected/bound.json | 2 +- schemars/tests/expected/bytes.json | 4 +- schemars/tests/expected/chrono-types.json | 2 +- schemars/tests/expected/crate_alias.json | 2 +- schemars/tests/expected/default.json | 16 ++-- schemars/tests/expected/deprecated-enum.json | 2 +- .../tests/expected/deprecated-struct.json | 2 +- .../tests/expected/doc_comments_enum.json | 2 +- .../tests/expected/doc_comments_override.json | 2 +- .../tests/expected/doc_comments_struct.json | 10 +-- .../expected/duration_and_systemtime.json | 8 +- schemars/tests/expected/either.json | 2 +- .../expected/enum-adjacent-tagged-duf.json | 10 +-- .../tests/expected/enum-adjacent-tagged.json | 10 +-- .../tests/expected/enum-external-duf.json | 10 +-- schemars/tests/expected/enum-external.json | 10 +-- .../tests/expected/enum-internal-duf.json | 2 +- schemars/tests/expected/enum-internal.json | 2 +- .../tests/expected/enum-repr-with-attrs.json | 2 +- schemars/tests/expected/enum-repr.json | 2 +- .../expected/enum-simple-internal-duf.json | 2 +- .../tests/expected/enum-simple-internal.json | 2 +- schemars/tests/expected/enum-unit-doc.json | 2 +- .../tests/expected/enum-untagged-duf.json | 10 +-- schemars/tests/expected/enum-untagged.json | 10 +-- schemars/tests/expected/enumset.json | 6 +- schemars/tests/expected/examples.json | 2 +- schemars/tests/expected/flatten.json | 2 +- schemars/tests/expected/from_json_value.json | 2 +- schemars/tests/expected/indexmap.json | 2 +- .../expected/inline-subschemas-recursive.json | 12 +-- .../tests/expected/inline-subschemas.json | 2 +- schemars/tests/expected/macro_built_enum.json | 6 +- .../tests/expected/macro_built_struct.json | 2 +- schemars/tests/expected/no-variants.json | 2 +- schemars/tests/expected/nonzero_ints.json | 2 +- schemars/tests/expected/os_strings.json | 8 +- .../tests/expected/property-name-struct.json | 2 +- schemars/tests/expected/range.json | 10 +-- schemars/tests/expected/remote_derive.json | 22 ++--- .../tests/expected/remote_derive_generic.json | 10 +-- schemars/tests/expected/result.json | 10 +-- schemars/tests/expected/rust_decimal.json | 2 +- schemars/tests/expected/same_name.json | 8 +- .../expected/schema-name-const-generics.json | 2 +- .../tests/expected/schema-name-custom.json | 6 +- .../tests/expected/schema-name-default.json | 6 +- .../expected/schema-name-mixed-generics.json | 8 +- .../expected/schema_settings-2019_09.json | 22 ++++- .../expected/schema_settings-2020_12.json | 83 +++++++++++++++++++ .../expected/schema_settings-openapi3.json | 22 ++++- schemars/tests/expected/schema_settings.json | 22 ++++- .../schema_with-enum-adjacent-tagged.json | 4 +- .../expected/schema_with-enum-external.json | 4 +- .../expected/schema_with-enum-internal.json | 2 +- .../expected/schema_with-enum-untagged.json | 4 +- .../tests/expected/schema_with-newtype.json | 2 +- .../tests/expected/schema_with-struct.json | 2 +- .../schema_with-transparent-newtype.json | 2 +- .../tests/expected/schema_with-tuple.json | 4 +- schemars/tests/expected/semver.json | 2 +- .../tests/expected/skip_enum_variants.json | 2 +- .../tests/expected/skip_struct_fields.json | 2 +- .../tests/expected/skip_tuple_fields.json | 4 +- schemars/tests/expected/smallvec.json | 2 +- schemars/tests/expected/smol_str.json | 2 +- schemars/tests/expected/struct-newtype.json | 2 +- .../struct-normal-additional-properties.json | 2 +- schemars/tests/expected/struct-normal.json | 2 +- schemars/tests/expected/struct-tuple.json | 4 +- schemars/tests/expected/struct-unit.json | 2 +- .../tests/expected/transparent-struct.json | 8 +- schemars/tests/expected/url.json | 2 +- schemars/tests/expected/uuid.json | 2 +- schemars/tests/expected/validate.json | 2 +- schemars/tests/expected/validate_inner.json | 2 +- schemars/tests/expected/validate_newtype.json | 2 +- .../expected/validate_schemars_attrs.json | 2 +- schemars/tests/expected/validate_tuple.json | 4 +- schemars/tests/schema_settings.rs | 6 ++ schemars_derive/src/schema_exprs.rs | 2 +- 88 files changed, 369 insertions(+), 210 deletions(-) create mode 100644 schemars/tests/expected/schema_settings-2020_12.json diff --git a/schemars/src/gen.rs b/schemars/src/gen.rs index 7d1d7d2..fbeff77 100644 --- a/schemars/src/gen.rs +++ b/schemars/src/gen.rs @@ -18,18 +18,18 @@ use std::{any::Any, collections::HashSet, fmt::Debug}; /// Settings to customize how Schemas are generated. /// -/// The default settings currently conform to [JSON Schema Draft 7](https://json-schema.org/specification-links.html#draft-7), but this is liable to change in a future version of Schemars if support for other JSON Schema versions is added. -/// If you require your generated schemas to conform to draft 7, consider using the [`draft07`](#method.draft07) method. +/// The default settings currently conform to [JSON Schema 2020-12](https://json-schema.org/specification-links#2020-12), but this is liable to change in a future version of Schemars if support for other JSON Schema versions is added. +/// If you rely on generated schemas conforming to draft 2020-12, consider using the [`SchemaSettings::draft2020_12()`] method. #[derive(Debug, Clone)] #[non_exhaustive] pub struct SchemaSettings { - /// If `true`, schemas for [`Option`](Option) will include a `nullable` property. + /// If `true`, schemas for [`Option`] will include a `nullable` property. /// /// This is not part of the JSON Schema spec, but is used in Swagger/OpenAPI schemas. /// /// Defaults to `false`. pub option_nullable: bool, - /// If `true`, schemas for [`Option`](Option) will have `null` added to their [`type`](../schema/struct.SchemaObject.html#structfield.instance_type). + /// If `true`, schemas for [`Option`] will have `null` added to their `type` property. /// /// Defaults to `true`. pub option_add_null_type: bool, @@ -39,9 +39,9 @@ pub struct SchemaSettings { pub definitions_path: String, /// The URI of the meta-schema describing the structure of the generated schemas. /// - /// Defaults to `"http://json-schema.org/draft-07/schema#"`. + /// Defaults to `"https://json-schema.org/draft/2020-12/schema"`. pub meta_schema: Option, - /// A list of visitors that get applied to all generated root schemas. + /// A list of visitors that get applied to all generated schemas. pub visitors: Vec>, /// Inline all subschemas instead of using references. /// @@ -53,30 +53,42 @@ pub struct SchemaSettings { impl Default for SchemaSettings { fn default() -> SchemaSettings { - SchemaSettings::draft07() + SchemaSettings::draft2020_12() } } impl SchemaSettings { - /// Creates `SchemaSettings` that conform to [JSON Schema Draft 7](https://json-schema.org/specification-links.html#draft-7). + /// Creates `SchemaSettings` that conform to [JSON Schema Draft 7](https://json-schema.org/specification-links#draft-7). pub fn draft07() -> SchemaSettings { SchemaSettings { option_nullable: false, option_add_null_type: true, definitions_path: "#/definitions/".to_owned(), meta_schema: Some("http://json-schema.org/draft-07/schema#".to_owned()), - visitors: vec![Box::new(RemoveRefSiblings)], + visitors: vec![Box::new(RemoveRefSiblings), Box::new(ReplacePrefixItems)], inline_subschemas: false, } } - /// Creates `SchemaSettings` that conform to [JSON Schema 2019-09](https://json-schema.org/specification-links.html#2019-09-formerly-known-as-draft-8). + /// Creates `SchemaSettings` that conform to [JSON Schema 2019-09](https://json-schema.org/specification-links#draft-2019-09-(formerly-known-as-draft-8)). pub fn draft2019_09() -> SchemaSettings { SchemaSettings { option_nullable: false, option_add_null_type: true, definitions_path: "#/$defs/".to_owned(), meta_schema: Some("https://json-schema.org/draft/2019-09/schema".to_owned()), + visitors: vec![Box::new(ReplacePrefixItems)], + inline_subschemas: false, + } + } + + /// Creates `SchemaSettings` that conform to [JSON Schema 2020-12](https://json-schema.org/specification-links#2020-12). + pub fn draft2020_12() -> SchemaSettings { + SchemaSettings { + option_nullable: false, + option_add_null_type: true, + definitions_path: "#/$defs/".to_owned(), + meta_schema: Some("https://json-schema.org/draft/2020-12/schema".to_owned()), visitors: Vec::new(), inline_subschemas: false, } @@ -99,6 +111,7 @@ impl SchemaSettings { }), Box::new(SetSingleExample), Box::new(ReplaceConstValue), + Box::new(ReplacePrefixItems), ], inline_subschemas: false, } diff --git a/schemars/src/json_schema_impls/tuple.rs b/schemars/src/json_schema_impls/tuple.rs index ab9a36d..204169e 100644 --- a/schemars/src/json_schema_impls/tuple.rs +++ b/schemars/src/json_schema_impls/tuple.rs @@ -25,7 +25,7 @@ macro_rules! tuple_impls { fn json_schema(gen: &mut SchemaGenerator) -> Schema { json_schema!({ "type": "array", - "items": [ + "prefixItems": [ $(gen.subschema_for::<$name>()),+ ], "minItems": $len, diff --git a/schemars/src/ser.rs b/schemars/src/ser.rs index 8be7b34..75ae6ca 100644 --- a/schemars/src/ser.rs +++ b/schemars/src/ser.rs @@ -378,7 +378,7 @@ impl serde::ser::SerializeTuple for SerializeTuple<'_> { let len = self.items.len(); let mut schema = json_schema!({ "type": "array", - "items": self.items, + "prefixItems": self.items, "maxItems": len, "minItems": len, }); diff --git a/schemars/src/visit.rs b/schemars/src/visit.rs index 9af8482..0b7eb7d 100644 --- a/schemars/src/visit.rs +++ b/schemars/src/visit.rs @@ -51,15 +51,15 @@ pub fn visit_schema(v: &mut V, schema: &mut Schema) { | "if" | "then" | "else" - | "additionalItems" | "contains" | "additionalProperties" - | "propertyNames" => { + | "propertyNames" + | "items" => { if let Ok(subschema) = value.try_into() { v.visit_schema(subschema) } } - "allOf" | "anyOf" | "oneOf" => { + "allOf" | "anyOf" | "oneOf" | "prefixItems" => { if let Some(array) = value.as_array_mut() { for value in array { if let Ok(subschema) = value.try_into() { @@ -68,17 +68,6 @@ pub fn visit_schema(v: &mut V, schema: &mut Schema) { } } } - "items" => { - if let Some(array) = value.as_array_mut() { - for value in array { - if let Ok(subschema) = value.try_into() { - v.visit_schema(subschema) - } - } - } else if let Ok(subschema) = value.try_into() { - v.visit_schema(subschema) - } - } "properties" | "patternProperties" => { if let Some(obj) = value.as_object_mut() { for value in obj.values_mut() { @@ -127,7 +116,7 @@ impl Visitor for ReplaceBoolSchemas { /// This visitor will restructure JSON Schema objects so that the `$ref` property will never appear alongside any other properties. /// -/// This is useful for dialects of JSON Schema (e.g. Draft 7) that do not support other properties alongside `$ref`. +/// This is useful for versions of JSON Schema (e.g. Draft 7) that do not support other properties alongside `$ref`. #[derive(Debug, Clone)] pub struct RemoveRefSiblings; @@ -188,3 +177,27 @@ impl Visitor for ReplaceConstValue { } } } + +/// This visitor will rename the `prefixItems` schema property to `items`. +/// +/// If the schema contains both `prefixItems` and `items`, then this additionally renames `items` to `additionalItems`. +/// +/// This is useful for versions of JSON Schema (e.g. Draft 7) that do not support the `prefixItems` property. +#[derive(Debug, Clone)] +pub struct ReplacePrefixItems; + +impl Visitor for ReplacePrefixItems { + fn visit_schema(&mut self, schema: &mut Schema) { + visit_schema(self, schema); + + if let Some(obj) = schema.as_object_mut() { + if let Some(prefix_items) = obj.remove("prefixItems") { + let previous_items = obj.insert("items".to_owned(), prefix_items); + + if let Some(previous_items) = previous_items { + obj.insert("additionalItems".to_owned(), previous_items); + } + } + } + } +} diff --git a/schemars/tests/expected/arrayvec.json b/schemars/tests/expected/arrayvec.json index 3de09a0..998d087 100644 --- a/schemars/tests/expected/arrayvec.json +++ b/schemars/tests/expected/arrayvec.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Array_up_to_size_16_of_int32", "type": "array", "items": { diff --git a/schemars/tests/expected/arrayvec_string.json b/schemars/tests/expected/arrayvec_string.json index ad174d8..0604332 100644 --- a/schemars/tests/expected/arrayvec_string.json +++ b/schemars/tests/expected/arrayvec_string.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "string", "type": "string" } \ No newline at end of file diff --git a/schemars/tests/expected/bigdecimal04.json b/schemars/tests/expected/bigdecimal04.json index 855db6f..e94ca27 100644 --- a/schemars/tests/expected/bigdecimal04.json +++ b/schemars/tests/expected/bigdecimal04.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Decimal", "type": "string", "pattern": "^-?[0-9]+(\\.[0-9]+)?$" diff --git a/schemars/tests/expected/bound.json b/schemars/tests/expected/bound.json index e4ceb13..a5645c6 100644 --- a/schemars/tests/expected/bound.json +++ b/schemars/tests/expected/bound.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyContainer", "type": "object", "properties": { diff --git a/schemars/tests/expected/bytes.json b/schemars/tests/expected/bytes.json index 8618a7f..80486c1 100644 --- a/schemars/tests/expected/bytes.json +++ b/schemars/tests/expected/bytes.json @@ -1,8 +1,8 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Tuple_of_Array_of_uint8_and_Array_of_uint8", "type": "array", - "items": [ + "prefixItems": [ { "type": "array", "items": { diff --git a/schemars/tests/expected/chrono-types.json b/schemars/tests/expected/chrono-types.json index ce03621..de26130 100644 --- a/schemars/tests/expected/chrono-types.json +++ b/schemars/tests/expected/chrono-types.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "ChronoTypes", "type": "object", "properties": { diff --git a/schemars/tests/expected/crate_alias.json b/schemars/tests/expected/crate_alias.json index e88a449..c2f1430 100644 --- a/schemars/tests/expected/crate_alias.json +++ b/schemars/tests/expected/crate_alias.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Struct", "type": "object", "properties": { diff --git a/schemars/tests/expected/default.json b/schemars/tests/expected/default.json index 57e346d..69f1fec 100644 --- a/schemars/tests/expected/default.json +++ b/schemars/tests/expected/default.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", "properties": { @@ -16,21 +16,17 @@ ] }, "my_struct2": { - "default": "i:0 b:false", - "allOf": [ - { - "$ref": "#/definitions/MyStruct2" - } - ] + "$ref": "#/$defs/MyStruct2", + "default": "i:0 b:false" }, "my_struct2_default_skipped": { - "$ref": "#/definitions/MyStruct2" + "$ref": "#/$defs/MyStruct2" }, "not_serialize": { - "$ref": "#/definitions/NotSerialize" + "$ref": "#/$defs/NotSerialize" } }, - "definitions": { + "$defs": { "MyStruct2": { "type": "object", "properties": { diff --git a/schemars/tests/expected/deprecated-enum.json b/schemars/tests/expected/deprecated-enum.json index e871160..a61c322 100644 --- a/schemars/tests/expected/deprecated-enum.json +++ b/schemars/tests/expected/deprecated-enum.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "DeprecatedEnum", "oneOf": [ { diff --git a/schemars/tests/expected/deprecated-struct.json b/schemars/tests/expected/deprecated-struct.json index 2b25c34..b7396ab 100644 --- a/schemars/tests/expected/deprecated-struct.json +++ b/schemars/tests/expected/deprecated-struct.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "DeprecatedStruct", "type": "object", "properties": { diff --git a/schemars/tests/expected/doc_comments_enum.json b/schemars/tests/expected/doc_comments_enum.json index 7d9321f..b2e8f94 100644 --- a/schemars/tests/expected/doc_comments_enum.json +++ b/schemars/tests/expected/doc_comments_enum.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "This is the enum's title", "description": "This is the enum's description.", "oneOf": [ diff --git a/schemars/tests/expected/doc_comments_override.json b/schemars/tests/expected/doc_comments_override.json index 0f90433..b184d2a 100644 --- a/schemars/tests/expected/doc_comments_override.json +++ b/schemars/tests/expected/doc_comments_override.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "OverrideDocs struct", "description": "New description", "type": "object", diff --git a/schemars/tests/expected/doc_comments_struct.json b/schemars/tests/expected/doc_comments_struct.json index 103a71a..655121a 100644 --- a/schemars/tests/expected/doc_comments_struct.json +++ b/schemars/tests/expected/doc_comments_struct.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "This is the struct's title", "description": "This is the struct's description.", "type": "object", @@ -14,11 +14,7 @@ }, "my_unit": { "description": "A unit struct instance", - "allOf": [ - { - "$ref": "#/definitions/MyUnitStruct" - } - ] + "$ref": "#/$defs/MyUnitStruct" } }, "required": [ @@ -26,7 +22,7 @@ "my_undocumented_bool", "my_unit" ], - "definitions": { + "$defs": { "MyUnitStruct": { "title": "A Unit", "type": "null" diff --git a/schemars/tests/expected/duration_and_systemtime.json b/schemars/tests/expected/duration_and_systemtime.json index bb25dcd..7e301e5 100644 --- a/schemars/tests/expected/duration_and_systemtime.json +++ b/schemars/tests/expected/duration_and_systemtime.json @@ -1,20 +1,20 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", "properties": { "duration": { - "$ref": "#/definitions/Duration" + "$ref": "#/$defs/Duration" }, "time": { - "$ref": "#/definitions/SystemTime" + "$ref": "#/$defs/SystemTime" } }, "required": [ "duration", "time" ], - "definitions": { + "$defs": { "Duration": { "type": "object", "properties": { diff --git a/schemars/tests/expected/either.json b/schemars/tests/expected/either.json index 807c9c9..18d8f51 100644 --- a/schemars/tests/expected/either.json +++ b/schemars/tests/expected/either.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Either_int32_or_Either_boolean_or_null", "anyOf": [ { diff --git a/schemars/tests/expected/enum-adjacent-tagged-duf.json b/schemars/tests/expected/enum-adjacent-tagged-duf.json index b9cb5fa..dfb3bb8 100644 --- a/schemars/tests/expected/enum-adjacent-tagged-duf.json +++ b/schemars/tests/expected/enum-adjacent-tagged-duf.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Adjacent", "oneOf": [ { @@ -49,7 +49,7 @@ ] }, "c": { - "$ref": "#/definitions/UnitStruct" + "$ref": "#/$defs/UnitStruct" } }, "required": [ @@ -68,7 +68,7 @@ ] }, "c": { - "$ref": "#/definitions/Struct" + "$ref": "#/$defs/Struct" } }, "required": [ @@ -121,7 +121,7 @@ }, "c": { "type": "array", - "items": [ + "prefixItems": [ { "type": "integer", "format": "int32" @@ -176,7 +176,7 @@ "additionalProperties": false } ], - "definitions": { + "$defs": { "UnitStruct": { "type": "null" }, diff --git a/schemars/tests/expected/enum-adjacent-tagged.json b/schemars/tests/expected/enum-adjacent-tagged.json index de77713..c631ae5 100644 --- a/schemars/tests/expected/enum-adjacent-tagged.json +++ b/schemars/tests/expected/enum-adjacent-tagged.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Adjacent", "oneOf": [ { @@ -47,7 +47,7 @@ ] }, "c": { - "$ref": "#/definitions/UnitStruct" + "$ref": "#/$defs/UnitStruct" } }, "required": [ @@ -65,7 +65,7 @@ ] }, "c": { - "$ref": "#/definitions/Struct" + "$ref": "#/$defs/Struct" } }, "required": [ @@ -115,7 +115,7 @@ }, "c": { "type": "array", - "items": [ + "prefixItems": [ { "type": "integer", "format": "int32" @@ -167,7 +167,7 @@ ] } ], - "definitions": { + "$defs": { "UnitStruct": { "type": "null" }, diff --git a/schemars/tests/expected/enum-external-duf.json b/schemars/tests/expected/enum-external-duf.json index a483535..76be5b3 100644 --- a/schemars/tests/expected/enum-external-duf.json +++ b/schemars/tests/expected/enum-external-duf.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "External", "oneOf": [ { @@ -28,7 +28,7 @@ "type": "object", "properties": { "unitStructNewType": { - "$ref": "#/definitions/UnitStruct" + "$ref": "#/$defs/UnitStruct" } }, "required": [ @@ -40,7 +40,7 @@ "type": "object", "properties": { "structNewType": { - "$ref": "#/definitions/Struct" + "$ref": "#/$defs/Struct" } }, "required": [ @@ -79,7 +79,7 @@ "properties": { "tuple": { "type": "array", - "items": [ + "prefixItems": [ { "type": "integer", "format": "int32" @@ -111,7 +111,7 @@ "additionalProperties": false } ], - "definitions": { + "$defs": { "UnitStruct": { "type": "null" }, diff --git a/schemars/tests/expected/enum-external.json b/schemars/tests/expected/enum-external.json index 8a1de2b..3c660fb 100644 --- a/schemars/tests/expected/enum-external.json +++ b/schemars/tests/expected/enum-external.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "External", "oneOf": [ { @@ -28,7 +28,7 @@ "type": "object", "properties": { "unitStructNewType": { - "$ref": "#/definitions/UnitStruct" + "$ref": "#/$defs/UnitStruct" } }, "required": [ @@ -40,7 +40,7 @@ "type": "object", "properties": { "structNewType": { - "$ref": "#/definitions/Struct" + "$ref": "#/$defs/Struct" } }, "required": [ @@ -78,7 +78,7 @@ "properties": { "tuple": { "type": "array", - "items": [ + "prefixItems": [ { "type": "integer", "format": "int32" @@ -110,7 +110,7 @@ "additionalProperties": false } ], - "definitions": { + "$defs": { "UnitStruct": { "type": "null" }, diff --git a/schemars/tests/expected/enum-internal-duf.json b/schemars/tests/expected/enum-internal-duf.json index de51ba1..73e4743 100644 --- a/schemars/tests/expected/enum-internal-duf.json +++ b/schemars/tests/expected/enum-internal-duf.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Internal", "oneOf": [ { diff --git a/schemars/tests/expected/enum-internal.json b/schemars/tests/expected/enum-internal.json index a5dffe3..2fd9770 100644 --- a/schemars/tests/expected/enum-internal.json +++ b/schemars/tests/expected/enum-internal.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Internal", "oneOf": [ { diff --git a/schemars/tests/expected/enum-repr-with-attrs.json b/schemars/tests/expected/enum-repr-with-attrs.json index 7070de8..89d941b 100644 --- a/schemars/tests/expected/enum-repr-with-attrs.json +++ b/schemars/tests/expected/enum-repr-with-attrs.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Renamed", "description": "Description from comment", "type": "integer", diff --git a/schemars/tests/expected/enum-repr.json b/schemars/tests/expected/enum-repr.json index 92d6f3a..b3e6bb9 100644 --- a/schemars/tests/expected/enum-repr.json +++ b/schemars/tests/expected/enum-repr.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Enum", "type": "integer", "enum": [ diff --git a/schemars/tests/expected/enum-simple-internal-duf.json b/schemars/tests/expected/enum-simple-internal-duf.json index 7fc20c1..aaf1eef 100644 --- a/schemars/tests/expected/enum-simple-internal-duf.json +++ b/schemars/tests/expected/enum-simple-internal-duf.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "SimpleInternal", "oneOf": [ { diff --git a/schemars/tests/expected/enum-simple-internal.json b/schemars/tests/expected/enum-simple-internal.json index 050089c..e955d2a 100644 --- a/schemars/tests/expected/enum-simple-internal.json +++ b/schemars/tests/expected/enum-simple-internal.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "SimpleInternal", "oneOf": [ { diff --git a/schemars/tests/expected/enum-unit-doc.json b/schemars/tests/expected/enum-unit-doc.json index 86160e8..1000435 100644 --- a/schemars/tests/expected/enum-unit-doc.json +++ b/schemars/tests/expected/enum-unit-doc.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "SoundOfMusic", "oneOf": [ { diff --git a/schemars/tests/expected/enum-untagged-duf.json b/schemars/tests/expected/enum-untagged-duf.json index 13a7d0a..58bdbe1 100644 --- a/schemars/tests/expected/enum-untagged-duf.json +++ b/schemars/tests/expected/enum-untagged-duf.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Untagged", "anyOf": [ { @@ -12,10 +12,10 @@ } }, { - "$ref": "#/definitions/UnitStruct" + "$ref": "#/$defs/UnitStruct" }, { - "$ref": "#/definitions/Struct" + "$ref": "#/$defs/Struct" }, { "type": "object", @@ -36,7 +36,7 @@ }, { "type": "array", - "items": [ + "prefixItems": [ { "type": "integer", "format": "int32" @@ -53,7 +53,7 @@ "format": "int32" } ], - "definitions": { + "$defs": { "UnitStruct": { "type": "null" }, diff --git a/schemars/tests/expected/enum-untagged.json b/schemars/tests/expected/enum-untagged.json index ed72b43..643cd20 100644 --- a/schemars/tests/expected/enum-untagged.json +++ b/schemars/tests/expected/enum-untagged.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Untagged", "anyOf": [ { @@ -12,10 +12,10 @@ } }, { - "$ref": "#/definitions/UnitStruct" + "$ref": "#/$defs/UnitStruct" }, { - "$ref": "#/definitions/Struct" + "$ref": "#/$defs/Struct" }, { "type": "object", @@ -35,7 +35,7 @@ }, { "type": "array", - "items": [ + "prefixItems": [ { "type": "integer", "format": "int32" @@ -52,7 +52,7 @@ "format": "int32" } ], - "definitions": { + "$defs": { "UnitStruct": { "type": "null" }, diff --git a/schemars/tests/expected/enumset.json b/schemars/tests/expected/enumset.json index 0f9baa2..72a39a1 100644 --- a/schemars/tests/expected/enumset.json +++ b/schemars/tests/expected/enumset.json @@ -1,12 +1,12 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Set_of_Foo", "type": "array", "uniqueItems": true, "items": { - "$ref": "#/definitions/Foo" + "$ref": "#/$defs/Foo" }, - "definitions": { + "$defs": { "Foo": { "type": "string", "enum": [ diff --git a/schemars/tests/expected/examples.json b/schemars/tests/expected/examples.json index 9f0a505..b891aa0 100644 --- a/schemars/tests/expected/examples.json +++ b/schemars/tests/expected/examples.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Struct", "type": "object", "properties": { diff --git a/schemars/tests/expected/flatten.json b/schemars/tests/expected/flatten.json index 4ea7094..7dfd54e 100644 --- a/schemars/tests/expected/flatten.json +++ b/schemars/tests/expected/flatten.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Flat", "type": "object", "properties": { diff --git a/schemars/tests/expected/from_json_value.json b/schemars/tests/expected/from_json_value.json index 6cc1ecc..b217fad 100644 --- a/schemars/tests/expected/from_json_value.json +++ b/schemars/tests/expected/from_json_value.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "zero": { diff --git a/schemars/tests/expected/indexmap.json b/schemars/tests/expected/indexmap.json index 318bf9b..8ba90a8 100644 --- a/schemars/tests/expected/indexmap.json +++ b/schemars/tests/expected/indexmap.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "IndexMapTypes", "type": "object", "properties": { diff --git a/schemars/tests/expected/inline-subschemas-recursive.json b/schemars/tests/expected/inline-subschemas-recursive.json index 0c1eebe..21f7f31 100644 --- a/schemars/tests/expected/inline-subschemas-recursive.json +++ b/schemars/tests/expected/inline-subschemas-recursive.json @@ -1,12 +1,12 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "RecursiveOuter", "type": "object", "properties": { "direct": { "anyOf": [ { - "$ref": "#/definitions/RecursiveOuter" + "$ref": "#/$defs/RecursiveOuter" }, { "type": "null" @@ -20,7 +20,7 @@ ], "properties": { "recursive": { - "$ref": "#/definitions/RecursiveOuter" + "$ref": "#/$defs/RecursiveOuter" } }, "required": [ @@ -28,14 +28,14 @@ ] } }, - "definitions": { + "$defs": { "RecursiveOuter": { "type": "object", "properties": { "direct": { "anyOf": [ { - "$ref": "#/definitions/RecursiveOuter" + "$ref": "#/$defs/RecursiveOuter" }, { "type": "null" @@ -49,7 +49,7 @@ ], "properties": { "recursive": { - "$ref": "#/definitions/RecursiveOuter" + "$ref": "#/$defs/RecursiveOuter" } }, "required": [ diff --git a/schemars/tests/expected/inline-subschemas.json b/schemars/tests/expected/inline-subschemas.json index 7ba76d5..cbee457 100644 --- a/schemars/tests/expected/inline-subschemas.json +++ b/schemars/tests/expected/inline-subschemas.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyJob", "type": "object", "properties": { diff --git a/schemars/tests/expected/macro_built_enum.json b/schemars/tests/expected/macro_built_enum.json index 4fedbb2..b27df44 100644 --- a/schemars/tests/expected/macro_built_enum.json +++ b/schemars/tests/expected/macro_built_enum.json @@ -1,12 +1,12 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "OuterEnum", "oneOf": [ { "type": "object", "properties": { "InnerStruct": { - "$ref": "#/definitions/InnerStruct" + "$ref": "#/$defs/InnerStruct" } }, "required": [ @@ -15,7 +15,7 @@ "additionalProperties": false } ], - "definitions": { + "$defs": { "InnerStruct": { "type": "object", "properties": { diff --git a/schemars/tests/expected/macro_built_struct.json b/schemars/tests/expected/macro_built_struct.json index 74a279c..1d0fd08 100644 --- a/schemars/tests/expected/macro_built_struct.json +++ b/schemars/tests/expected/macro_built_struct.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "A", "type": "object", "properties": { diff --git a/schemars/tests/expected/no-variants.json b/schemars/tests/expected/no-variants.json index efe2853..14de7f4 100644 --- a/schemars/tests/expected/no-variants.json +++ b/schemars/tests/expected/no-variants.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "NoVariants", "type": "string", "enum": [] diff --git a/schemars/tests/expected/nonzero_ints.json b/schemars/tests/expected/nonzero_ints.json index 97dee99..432e3b0 100644 --- a/schemars/tests/expected/nonzero_ints.json +++ b/schemars/tests/expected/nonzero_ints.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", "properties": { diff --git a/schemars/tests/expected/os_strings.json b/schemars/tests/expected/os_strings.json index b422026..72e0be1 100644 --- a/schemars/tests/expected/os_strings.json +++ b/schemars/tests/expected/os_strings.json @@ -1,20 +1,20 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "OsStrings", "type": "object", "properties": { "owned": { - "$ref": "#/definitions/OsString" + "$ref": "#/$defs/OsString" }, "borrowed": { - "$ref": "#/definitions/OsString" + "$ref": "#/$defs/OsString" } }, "required": [ "owned", "borrowed" ], - "definitions": { + "$defs": { "OsString": { "oneOf": [ { diff --git a/schemars/tests/expected/property-name-struct.json b/schemars/tests/expected/property-name-struct.json index 1aa6e78..82ea9ed 100644 --- a/schemars/tests/expected/property-name-struct.json +++ b/schemars/tests/expected/property-name-struct.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", "properties": { diff --git a/schemars/tests/expected/range.json b/schemars/tests/expected/range.json index 25f7389..64db4bd 100644 --- a/schemars/tests/expected/range.json +++ b/schemars/tests/expected/range.json @@ -1,16 +1,16 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", "properties": { "range": { - "$ref": "#/definitions/Range_of_uint" + "$ref": "#/$defs/Range_of_uint" }, "inclusive": { - "$ref": "#/definitions/Range_of_double" + "$ref": "#/$defs/Range_of_double" }, "bound": { - "$ref": "#/definitions/Bound_of_string" + "$ref": "#/$defs/Bound_of_string" } }, "required": [ @@ -18,7 +18,7 @@ "inclusive", "bound" ], - "definitions": { + "$defs": { "Range_of_uint": { "type": "object", "properties": { diff --git a/schemars/tests/expected/remote_derive.json b/schemars/tests/expected/remote_derive.json index 0f27ad3..760630d 100644 --- a/schemars/tests/expected/remote_derive.json +++ b/schemars/tests/expected/remote_derive.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Process", "type": "object", "properties": { @@ -7,33 +7,25 @@ "type": "string" }, "wall_time": { - "$ref": "#/definitions/Duration" + "$ref": "#/$defs/Duration" }, "user_cpu_time": { + "$ref": "#/$defs/Duration", "default": { "secs": 0, "nanos": 0 - }, - "allOf": [ - { - "$ref": "#/definitions/Duration" - } - ] + } }, "system_cpu_time": { - "default": "0.000000000s", - "allOf": [ - { - "$ref": "#/definitions/Duration" - } - ] + "$ref": "#/$defs/Duration", + "default": "0.000000000s" } }, "required": [ "command_line", "wall_time" ], - "definitions": { + "$defs": { "Duration": { "type": "object", "properties": { diff --git a/schemars/tests/expected/remote_derive_generic.json b/schemars/tests/expected/remote_derive_generic.json index f6ba3ef..bef4d6a 100644 --- a/schemars/tests/expected/remote_derive_generic.json +++ b/schemars/tests/expected/remote_derive_generic.json @@ -1,16 +1,16 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct_for_int32", "type": "object", "properties": { "byte_or_bool2": { - "$ref": "#/definitions/Or_for_uint8_and_boolean" + "$ref": "#/$defs/Or_for_uint8_and_boolean" }, "unit_or_t2": { - "$ref": "#/definitions/Or_for_null_and_int32" + "$ref": "#/$defs/Or_for_null_and_int32" }, "s": { - "$ref": "#/definitions/Str" + "$ref": "#/$defs/Str" }, "fake_map": { "type": "object", @@ -29,7 +29,7 @@ "s", "fake_map" ], - "definitions": { + "$defs": { "Or_for_uint8_and_boolean": { "anyOf": [ { diff --git a/schemars/tests/expected/result.json b/schemars/tests/expected/result.json index 8a25a0e..b468835 100644 --- a/schemars/tests/expected/result.json +++ b/schemars/tests/expected/result.json @@ -1,27 +1,27 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Container", "type": "object", "properties": { "result1": { - "$ref": "#/definitions/Result_of_MyStruct_or_Array_of_string" + "$ref": "#/$defs/Result_of_MyStruct_or_Array_of_string" }, "result2": { - "$ref": "#/definitions/Result_of_boolean_or_null" + "$ref": "#/$defs/Result_of_boolean_or_null" } }, "required": [ "result1", "result2" ], - "definitions": { + "$defs": { "Result_of_MyStruct_or_Array_of_string": { "oneOf": [ { "type": "object", "properties": { "Ok": { - "$ref": "#/definitions/MyStruct" + "$ref": "#/$defs/MyStruct" } }, "required": [ diff --git a/schemars/tests/expected/rust_decimal.json b/schemars/tests/expected/rust_decimal.json index 855db6f..e94ca27 100644 --- a/schemars/tests/expected/rust_decimal.json +++ b/schemars/tests/expected/rust_decimal.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Decimal", "type": "string", "pattern": "^-?[0-9]+(\\.[0-9]+)?$" diff --git a/schemars/tests/expected/same_name.json b/schemars/tests/expected/same_name.json index ebea3fe..fb65854 100644 --- a/schemars/tests/expected/same_name.json +++ b/schemars/tests/expected/same_name.json @@ -1,20 +1,20 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Config2", "type": "object", "properties": { "a_cfg": { - "$ref": "#/definitions/Config" + "$ref": "#/$defs/Config" }, "b_cfg": { - "$ref": "#/definitions/Config2" + "$ref": "#/$defs/Config2" } }, "required": [ "a_cfg", "b_cfg" ], - "definitions": { + "$defs": { "Config": { "type": "object", "properties": { diff --git a/schemars/tests/expected/schema-name-const-generics.json b/schemars/tests/expected/schema-name-const-generics.json index 6bb3ab1..3c2edcc 100644 --- a/schemars/tests/expected/schema-name-const-generics.json +++ b/schemars/tests/expected/schema-name-const-generics.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "const-generics-z-42", "type": "object", "properties": { diff --git a/schemars/tests/expected/schema-name-custom.json b/schemars/tests/expected/schema-name-custom.json index 447edbd..70b4d98 100644 --- a/schemars/tests/expected/schema-name-custom.json +++ b/schemars/tests/expected/schema-name-custom.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "a-new-name-Array_of_string-int32-int32", "type": "object", "properties": { @@ -20,7 +20,7 @@ } }, "inner": { - "$ref": "#/definitions/another-new-name" + "$ref": "#/$defs/another-new-name" } }, "required": [ @@ -30,7 +30,7 @@ "w", "inner" ], - "definitions": { + "$defs": { "another-new-name": { "type": "object", "properties": { diff --git a/schemars/tests/expected/schema-name-default.json b/schemars/tests/expected/schema-name-default.json index bd2ab9a..2243ddd 100644 --- a/schemars/tests/expected/schema-name-default.json +++ b/schemars/tests/expected/schema-name-default.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct_for_int32_and_null_and_boolean_and_Array_of_string", "type": "object", "properties": { @@ -20,7 +20,7 @@ } }, "inner": { - "$ref": "#/definitions/MySimpleStruct" + "$ref": "#/$defs/MySimpleStruct" } }, "required": [ @@ -30,7 +30,7 @@ "w", "inner" ], - "definitions": { + "$defs": { "MySimpleStruct": { "type": "object", "properties": { diff --git a/schemars/tests/expected/schema-name-mixed-generics.json b/schemars/tests/expected/schema-name-mixed-generics.json index 4342410..85c6792 100644 --- a/schemars/tests/expected/schema-name-mixed-generics.json +++ b/schemars/tests/expected/schema-name-mixed-generics.json @@ -1,10 +1,10 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MixedGenericStruct_for_MyStruct_for_int32_and_null_and_boolean_and_Array_of_string_and_42_and_z", "type": "object", "properties": { "generic": { - "$ref": "#/definitions/MyStruct_for_int32_and_null_and_boolean_and_Array_of_string" + "$ref": "#/$defs/MyStruct_for_int32_and_null_and_boolean_and_Array_of_string" }, "foo": { "type": "integer", @@ -15,7 +15,7 @@ "generic", "foo" ], - "definitions": { + "$defs": { "MyStruct_for_int32_and_null_and_boolean_and_Array_of_string": { "type": "object", "properties": { @@ -36,7 +36,7 @@ } }, "inner": { - "$ref": "#/definitions/MySimpleStruct" + "$ref": "#/$defs/MySimpleStruct" } }, "required": [ diff --git a/schemars/tests/expected/schema_settings-2019_09.json b/schemars/tests/expected/schema_settings-2019_09.json index 568ce85..6b6dc61 100644 --- a/schemars/tests/expected/schema_settings-2019_09.json +++ b/schemars/tests/expected/schema_settings-2019_09.json @@ -25,12 +25,32 @@ "type": "null" } ] + }, + "tuples": { + "type": "array", + "items": { + "type": "array", + "items": [ + { + "type": "integer", + "format": "uint8", + "minimum": 0 + }, + { + "type": "integer", + "format": "int64" + } + ], + "minItems": 2, + "maxItems": 2 + } } }, "required": [ "int", "values", - "value" + "value", + "tuples" ], "$defs": { "Inner": { diff --git a/schemars/tests/expected/schema_settings-2020_12.json b/schemars/tests/expected/schema_settings-2020_12.json new file mode 100644 index 0000000..7964058 --- /dev/null +++ b/schemars/tests/expected/schema_settings-2020_12.json @@ -0,0 +1,83 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Outer", + "type": "object", + "properties": { + "int": { + "type": "integer", + "format": "int32", + "examples": [ + 8, + null + ] + }, + "values": { + "type": "object", + "additionalProperties": true + }, + "value": true, + "inner": { + "anyOf": [ + { + "$ref": "#/$defs/Inner" + }, + { + "type": "null" + } + ] + }, + "tuples": { + "type": "array", + "items": { + "type": "array", + "prefixItems": [ + { + "type": "integer", + "format": "uint8", + "minimum": 0 + }, + { + "type": "integer", + "format": "int64" + } + ], + "minItems": 2, + "maxItems": 2 + } + } + }, + "required": [ + "int", + "values", + "value", + "tuples" + ], + "$defs": { + "Inner": { + "oneOf": [ + { + "type": "string", + "enum": [ + "UndocumentedUnit1", + "UndocumentedUnit2" + ] + }, + { + "description": "This is a documented unit variant", + "type": "string", + "const": "DocumentedUnit" + }, + { + "type": "object", + "properties": { + "ValueNewType": true + }, + "required": [ + "ValueNewType" + ], + "additionalProperties": false + } + ] + } + } +} \ No newline at end of file diff --git a/schemars/tests/expected/schema_settings-openapi3.json b/schemars/tests/expected/schema_settings-openapi3.json index 6ff2604..e5032f0 100644 --- a/schemars/tests/expected/schema_settings-openapi3.json +++ b/schemars/tests/expected/schema_settings-openapi3.json @@ -20,12 +20,32 @@ "$ref": "#/components/schemas/Inner" } ] + }, + "tuples": { + "type": "array", + "items": { + "type": "array", + "items": [ + { + "type": "integer", + "format": "uint8", + "minimum": 0 + }, + { + "type": "integer", + "format": "int64" + } + ], + "minItems": 2, + "maxItems": 2 + } } }, "required": [ "int", "values", - "value" + "value", + "tuples" ], "components": { "schemas": { diff --git a/schemars/tests/expected/schema_settings.json b/schemars/tests/expected/schema_settings.json index 4a50262..a836cd6 100644 --- a/schemars/tests/expected/schema_settings.json +++ b/schemars/tests/expected/schema_settings.json @@ -25,12 +25,32 @@ "type": "null" } ] + }, + "tuples": { + "type": "array", + "items": { + "type": "array", + "items": [ + { + "type": "integer", + "format": "uint8", + "minimum": 0 + }, + { + "type": "integer", + "format": "int64" + } + ], + "minItems": 2, + "maxItems": 2 + } } }, "required": [ "int", "values", - "value" + "value", + "tuples" ], "definitions": { "Inner": { diff --git a/schemars/tests/expected/schema_with-enum-adjacent-tagged.json b/schemars/tests/expected/schema_with-enum-adjacent-tagged.json index ab848e3..3e7173d 100644 --- a/schemars/tests/expected/schema_with-enum-adjacent-tagged.json +++ b/schemars/tests/expected/schema_with-enum-adjacent-tagged.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Adjacent", "oneOf": [ { @@ -57,7 +57,7 @@ }, "c": { "type": "array", - "items": [ + "prefixItems": [ { "type": "boolean" }, diff --git a/schemars/tests/expected/schema_with-enum-external.json b/schemars/tests/expected/schema_with-enum-external.json index 5f28d2f..5f5fe83 100644 --- a/schemars/tests/expected/schema_with-enum-external.json +++ b/schemars/tests/expected/schema_with-enum-external.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "External", "oneOf": [ { @@ -39,7 +39,7 @@ "properties": { "tuple": { "type": "array", - "items": [ + "prefixItems": [ { "type": "boolean" }, diff --git a/schemars/tests/expected/schema_with-enum-internal.json b/schemars/tests/expected/schema_with-enum-internal.json index 90871ad..e6d707b 100644 --- a/schemars/tests/expected/schema_with-enum-internal.json +++ b/schemars/tests/expected/schema_with-enum-internal.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Internal", "oneOf": [ { diff --git a/schemars/tests/expected/schema_with-enum-untagged.json b/schemars/tests/expected/schema_with-enum-untagged.json index 834eb41..9057c87 100644 --- a/schemars/tests/expected/schema_with-enum-untagged.json +++ b/schemars/tests/expected/schema_with-enum-untagged.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Untagged", "anyOf": [ { @@ -18,7 +18,7 @@ }, { "type": "array", - "items": [ + "prefixItems": [ { "type": "boolean" }, diff --git a/schemars/tests/expected/schema_with-newtype.json b/schemars/tests/expected/schema_with-newtype.json index 8b709fb..eb4a2f6 100644 --- a/schemars/tests/expected/schema_with-newtype.json +++ b/schemars/tests/expected/schema_with-newtype.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Newtype", "type": "boolean" } \ No newline at end of file diff --git a/schemars/tests/expected/schema_with-struct.json b/schemars/tests/expected/schema_with-struct.json index 31caebe..0674f1e 100644 --- a/schemars/tests/expected/schema_with-struct.json +++ b/schemars/tests/expected/schema_with-struct.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Struct", "type": "object", "properties": { diff --git a/schemars/tests/expected/schema_with-transparent-newtype.json b/schemars/tests/expected/schema_with-transparent-newtype.json index 9f6afb3..2aeff68 100644 --- a/schemars/tests/expected/schema_with-transparent-newtype.json +++ b/schemars/tests/expected/schema_with-transparent-newtype.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "schema_fn", "type": "boolean" } \ No newline at end of file diff --git a/schemars/tests/expected/schema_with-tuple.json b/schemars/tests/expected/schema_with-tuple.json index 4e2c095..1296e33 100644 --- a/schemars/tests/expected/schema_with-tuple.json +++ b/schemars/tests/expected/schema_with-tuple.json @@ -1,8 +1,8 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Tuple", "type": "array", - "items": [ + "prefixItems": [ { "type": "boolean" }, diff --git a/schemars/tests/expected/semver.json b/schemars/tests/expected/semver.json index e8c59b9..9f5b155 100644 --- a/schemars/tests/expected/semver.json +++ b/schemars/tests/expected/semver.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "SemverTypes", "type": "object", "properties": { diff --git a/schemars/tests/expected/skip_enum_variants.json b/schemars/tests/expected/skip_enum_variants.json index d245734..bd3a893 100644 --- a/schemars/tests/expected/skip_enum_variants.json +++ b/schemars/tests/expected/skip_enum_variants.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyEnum", "oneOf": [ { diff --git a/schemars/tests/expected/skip_struct_fields.json b/schemars/tests/expected/skip_struct_fields.json index 48549ce..2a74c32 100644 --- a/schemars/tests/expected/skip_struct_fields.json +++ b/schemars/tests/expected/skip_struct_fields.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", "properties": { diff --git a/schemars/tests/expected/skip_tuple_fields.json b/schemars/tests/expected/skip_tuple_fields.json index 3cdce84..c3a4255 100644 --- a/schemars/tests/expected/skip_tuple_fields.json +++ b/schemars/tests/expected/skip_tuple_fields.json @@ -1,8 +1,8 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "TupleStruct", "type": "array", - "items": [ + "prefixItems": [ { "type": "number", "format": "float" diff --git a/schemars/tests/expected/smallvec.json b/schemars/tests/expected/smallvec.json index 7ce011d..be50ed7 100644 --- a/schemars/tests/expected/smallvec.json +++ b/schemars/tests/expected/smallvec.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Array_of_string", "type": "array", "items": { diff --git a/schemars/tests/expected/smol_str.json b/schemars/tests/expected/smol_str.json index ad174d8..0604332 100644 --- a/schemars/tests/expected/smol_str.json +++ b/schemars/tests/expected/smol_str.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "string", "type": "string" } \ No newline at end of file diff --git a/schemars/tests/expected/struct-newtype.json b/schemars/tests/expected/struct-newtype.json index 284e47d..e7b1cba 100644 --- a/schemars/tests/expected/struct-newtype.json +++ b/schemars/tests/expected/struct-newtype.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Newtype", "type": "integer", "format": "int32" diff --git a/schemars/tests/expected/struct-normal-additional-properties.json b/schemars/tests/expected/struct-normal-additional-properties.json index 529e2c4..2c55fb7 100644 --- a/schemars/tests/expected/struct-normal-additional-properties.json +++ b/schemars/tests/expected/struct-normal-additional-properties.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Struct", "type": "object", "properties": { diff --git a/schemars/tests/expected/struct-normal.json b/schemars/tests/expected/struct-normal.json index 3e73393..c49757c 100644 --- a/schemars/tests/expected/struct-normal.json +++ b/schemars/tests/expected/struct-normal.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Struct", "type": "object", "properties": { diff --git a/schemars/tests/expected/struct-tuple.json b/schemars/tests/expected/struct-tuple.json index c89e416..8960068 100644 --- a/schemars/tests/expected/struct-tuple.json +++ b/schemars/tests/expected/struct-tuple.json @@ -1,8 +1,8 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Tuple", "type": "array", - "items": [ + "prefixItems": [ { "type": "integer", "format": "int32" diff --git a/schemars/tests/expected/struct-unit.json b/schemars/tests/expected/struct-unit.json index 3252d20..46f6fcf 100644 --- a/schemars/tests/expected/struct-unit.json +++ b/schemars/tests/expected/struct-unit.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Unit", "type": "null" } \ No newline at end of file diff --git a/schemars/tests/expected/transparent-struct.json b/schemars/tests/expected/transparent-struct.json index 7fc1c32..2692069 100644 --- a/schemars/tests/expected/transparent-struct.json +++ b/schemars/tests/expected/transparent-struct.json @@ -1,12 +1,12 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "OuterStruct", "type": "object", "properties": { "inner": { "anyOf": [ { - "$ref": "#/definitions/InnerStruct" + "$ref": "#/$defs/InnerStruct" }, { "type": "null" @@ -14,10 +14,10 @@ ] } }, - "definitions": { + "$defs": { "InnerStruct": { "type": "array", - "items": [ + "prefixItems": [ { "type": "string" }, diff --git a/schemars/tests/expected/url.json b/schemars/tests/expected/url.json index 5722f9e..425bf85 100644 --- a/schemars/tests/expected/url.json +++ b/schemars/tests/expected/url.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "UrlTypes", "type": "object", "properties": { diff --git a/schemars/tests/expected/uuid.json b/schemars/tests/expected/uuid.json index 8ba1a01..98ae408 100644 --- a/schemars/tests/expected/uuid.json +++ b/schemars/tests/expected/uuid.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Uuid", "type": "string", "format": "uuid" diff --git a/schemars/tests/expected/validate.json b/schemars/tests/expected/validate.json index 04131ce..57c7f52 100644 --- a/schemars/tests/expected/validate.json +++ b/schemars/tests/expected/validate.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Struct", "type": "object", "properties": { diff --git a/schemars/tests/expected/validate_inner.json b/schemars/tests/expected/validate_inner.json index ba4ee9e..a77e0d7 100644 --- a/schemars/tests/expected/validate_inner.json +++ b/schemars/tests/expected/validate_inner.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Struct", "type": "object", "properties": { diff --git a/schemars/tests/expected/validate_newtype.json b/schemars/tests/expected/validate_newtype.json index 89bec96..cd835f5 100644 --- a/schemars/tests/expected/validate_newtype.json +++ b/schemars/tests/expected/validate_newtype.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "NewType", "type": "integer", "format": "uint8", diff --git a/schemars/tests/expected/validate_schemars_attrs.json b/schemars/tests/expected/validate_schemars_attrs.json index 4fdba53..5e7d31d 100644 --- a/schemars/tests/expected/validate_schemars_attrs.json +++ b/schemars/tests/expected/validate_schemars_attrs.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Struct2", "type": "object", "properties": { diff --git a/schemars/tests/expected/validate_tuple.json b/schemars/tests/expected/validate_tuple.json index 2a0e266..fa81224 100644 --- a/schemars/tests/expected/validate_tuple.json +++ b/schemars/tests/expected/validate_tuple.json @@ -1,8 +1,8 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Tuple", "type": "array", - "items": [ + "prefixItems": [ { "type": "integer", "format": "uint8", diff --git a/schemars/tests/schema_settings.rs b/schemars/tests/schema_settings.rs index 408856a..c165a8c 100644 --- a/schemars/tests/schema_settings.rs +++ b/schemars/tests/schema_settings.rs @@ -13,6 +13,7 @@ pub struct Outer { pub values: BTreeMap<&'static str, Value>, pub value: Value, pub inner: Option, + pub tuples: Vec<(u8, i64)>, } #[derive(JsonSchema)] @@ -40,6 +41,11 @@ fn schema_matches_2019_09() -> TestResult { test_generated_schema::("schema_settings-2019_09", SchemaSettings::draft2019_09()) } +#[test] +fn schema_matches_2020_12() -> TestResult { + test_generated_schema::("schema_settings-2020_12", SchemaSettings::draft2020_12()) +} + #[test] #[ignore = "Fails due to default/empty `Metadata` not being considered equal to `Option::None`, although they're conceptually the same and serialize to identical JSON"] fn schema_matches_openapi3() -> TestResult { diff --git a/schemars_derive/src/schema_exprs.rs b/schemars_derive/src/schema_exprs.rs index 6357172..87e789e 100644 --- a/schemars_derive/src/schema_exprs.rs +++ b/schemars_derive/src/schema_exprs.rs @@ -422,7 +422,7 @@ fn expr_for_tuple_struct(fields: &[Field]) -> TokenStream { quote! { schemars::json_schema!({ "type": "array", - "items": [#((#fields)),*], + "prefixItems": [#((#fields)),*], "minItems": #len, "maxItems": #len, }) From 354fa91b8fb9d3c802559557206e1f763fdf791e Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Mon, 20 May 2024 15:25:16 +0100 Subject: [PATCH 13/31] Allow running visitors against pre-2020-12 schemas --- schemars/src/visit.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/schemars/src/visit.rs b/schemars/src/visit.rs index 0b7eb7d..5d69a2f 100644 --- a/schemars/src/visit.rs +++ b/schemars/src/visit.rs @@ -46,6 +46,11 @@ pub trait Visitor { pub fn visit_schema(v: &mut V, schema: &mut Schema) { if let Some(obj) = schema.as_object_mut() { for (key, value) in obj { + // This is intentionally written to work with multiple JSON Schema versions, so that + // users can add their own visitors on the end of e.g. `SchemaSettings::draft07()` and + // they will still apply to all subschemas "as expected". + // This is why this match statement contains both `additionalProperties` (which was + // dropped in draft 2020-12) and `prefixItems` (which was added in draft 2020-12). match key.as_str() { "not" | "if" @@ -54,7 +59,7 @@ pub fn visit_schema(v: &mut V, schema: &mut Schema) { | "contains" | "additionalProperties" | "propertyNames" - | "items" => { + | "additionalItems" => { if let Ok(subschema) = value.try_into() { v.visit_schema(subschema) } @@ -68,6 +73,18 @@ pub fn visit_schema(v: &mut V, schema: &mut Schema) { } } } + // Support `items` array even though this is not allowed in draft 2020-12 (see above comment) + "items" => { + if let Some(array) = value.as_array_mut() { + for value in array { + if let Ok(subschema) = value.try_into() { + v.visit_schema(subschema) + } + } + } else if let Ok(subschema) = value.try_into() { + v.visit_schema(subschema) + } + } "properties" | "patternProperties" => { if let Some(obj) = value.as_object_mut() { for value in obj.values_mut() { From 5737c124a3bac67754425ffffb1c8ef4e4998617 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Thu, 23 May 2024 14:14:42 +0100 Subject: [PATCH 14/31] Revert to previous behaviour regarding visitors running on `$defs`/`definitions` lazily (only if/when root schema is created). Visitors will now always descend into `$defs`/`definitions`. If a generator is configured to use a different definitions path, then the visitor will also descend into that path (but a plain `Visitor` would NOT. --- schemars/src/gen.rs | 123 +++++++++++++++++++++++++----------------- schemars/src/visit.rs | 2 +- 2 files changed, 76 insertions(+), 49 deletions(-) diff --git a/schemars/src/gen.rs b/schemars/src/gen.rs index fbeff77..b4b52bf 100644 --- a/schemars/src/gen.rs +++ b/schemars/src/gen.rs @@ -35,7 +35,9 @@ pub struct SchemaSettings { pub option_add_null_type: bool, /// A JSON pointer to the expected location of referenceable subschemas within the resulting root schema. /// - /// Defaults to `"#/definitions/"`. + /// A single leading `#` and/or single trailing `/` are ignored. + /// + /// Defaults to `/$defs`. pub definitions_path: String, /// The URI of the meta-schema describing the structure of the generated schemas. /// @@ -63,7 +65,7 @@ impl SchemaSettings { SchemaSettings { option_nullable: false, option_add_null_type: true, - definitions_path: "#/definitions/".to_owned(), + definitions_path: "/definitions".to_owned(), meta_schema: Some("http://json-schema.org/draft-07/schema#".to_owned()), visitors: vec![Box::new(RemoveRefSiblings), Box::new(ReplacePrefixItems)], inline_subschemas: false, @@ -75,7 +77,7 @@ impl SchemaSettings { SchemaSettings { option_nullable: false, option_add_null_type: true, - definitions_path: "#/$defs/".to_owned(), + definitions_path: "/$defs".to_owned(), meta_schema: Some("https://json-schema.org/draft/2019-09/schema".to_owned()), visitors: vec![Box::new(ReplacePrefixItems)], inline_subschemas: false, @@ -87,7 +89,7 @@ impl SchemaSettings { SchemaSettings { option_nullable: false, option_add_null_type: true, - definitions_path: "#/$defs/".to_owned(), + definitions_path: "/$defs".to_owned(), meta_schema: Some("https://json-schema.org/draft/2020-12/schema".to_owned()), visitors: Vec::new(), inline_subschemas: false, @@ -99,7 +101,7 @@ impl SchemaSettings { SchemaSettings { option_nullable: true, option_add_null_type: false, - definitions_path: "#/components/schemas/".to_owned(), + definitions_path: "/components/schemas".to_owned(), meta_schema: Some( "https://spec.openapis.org/oas/3.0/schema/2021-09-28#/definitions/Schema" .to_owned(), @@ -250,7 +252,7 @@ impl SchemaGenerator { } }; - let reference = format!("{}{}", self.settings.definitions_path, name); + let reference = format!("#{}/{}", self.definitions_path_stripped(), name); if !self.definitions.contains_key(&name) { self.insert_new_subschema_for::(name, id); } @@ -269,8 +271,7 @@ impl SchemaGenerator { // insert into definitions BEFORE calling json_schema to avoid infinite recursion self.definitions.insert(name.clone(), dummy); - let mut schema = self.json_schema_internal::(id); - Self::run_visitors(&mut schema, &mut self.settings.visitors); + let schema = self.json_schema_internal::(id); self.definitions.insert(name, schema.to_value()); } @@ -323,12 +324,8 @@ impl SchemaGenerator { object.insert("$schema".into(), meta_schema.into()); } - Self::add_definitions( - object, - self.definitions.clone(), - &self.settings.definitions_path, - ); - Self::run_visitors(&mut schema, &mut self.settings.visitors); + self.add_definitions(object, self.definitions.clone()); + self.run_visitors(&mut schema); schema } @@ -346,12 +343,13 @@ impl SchemaGenerator { .entry("title") .or_insert_with(|| T::schema_name().into()); - if let Some(meta_schema) = self.settings.meta_schema { + if let Some(meta_schema) = std::mem::take(&mut self.settings.meta_schema) { object.insert("$schema".into(), meta_schema.into()); } - Self::add_definitions(object, self.definitions, &self.settings.definitions_path); - Self::run_visitors(&mut schema, &mut self.settings.visitors); + let definitions = self.take_definitions(); + self.add_definitions(object, definitions); + self.run_visitors(&mut schema); schema } @@ -379,12 +377,8 @@ impl SchemaGenerator { object.insert("$schema".into(), meta_schema.into()); } - Self::add_definitions( - object, - self.definitions.clone(), - &self.settings.definitions_path, - ); - Self::run_visitors(&mut schema, &mut self.settings.visitors); + self.add_definitions(object, self.definitions.clone()); + self.run_visitors(&mut schema); Ok(schema) } @@ -408,12 +402,13 @@ impl SchemaGenerator { object.insert("examples".into(), vec![example].into()); } - if let Some(meta_schema) = self.settings.meta_schema { + if let Some(meta_schema) = std::mem::take(&mut self.settings.meta_schema) { object.insert("$schema".into(), meta_schema.into()); } - Self::add_definitions(object, self.definitions, &self.settings.definitions_path); - Self::run_visitors(&mut schema, &mut self.settings.visitors); + let definitions = self.take_definitions(); + self.add_definitions(object, definitions); + self.run_visitors(&mut schema); Ok(schema) } @@ -445,15 +440,16 @@ impl SchemaGenerator { } fn add_definitions( + &mut self, schema_object: &mut Map, mut definitions: Map, - path: &str, ) { if definitions.is_empty() { return; } - let target = match Self::json_pointer(schema_object, path) { + let pointer = self.definitions_path_stripped(); + let target = match json_pointer_mut(schema_object, pointer, true) { Some(d) => d, None => return, }; @@ -461,33 +457,64 @@ impl SchemaGenerator { target.append(&mut definitions); } - fn json_pointer<'a>( - mut object: &'a mut Map, - pointer: &str, - ) -> Option<&'a mut Map> { - let segments = pointer.strip_prefix("#/")?.strip_suffix('/')?.split('/'); - - for mut segment in segments { - let replaced: String; - if segment.contains('~') { - replaced = segment.replace("~1", "/").replace("~0", "~"); - segment = &replaced; - } + fn run_visitors(&mut self, schema: &mut Schema) { + for visitor in self.visitors_mut() { + visitor.visit_schema(schema); + } - object = object - .entry(segment) - .or_insert(Value::Object(Map::default())) - .as_object_mut()?; + let pointer = self.definitions_path_stripped(); + // `$defs`` and `definitions` are both handled internally by `Visitor::visit_schema`. + // If the definitions are in any other location, explicitly visit them here to ensure + // they're run against any referenced subschemas. + if pointer != "/$defs" && pointer != "/definitions" { + if let Some(definitions) = schema + .as_object_mut() + .and_then(|so| json_pointer_mut(so, pointer, false)) + { + for subschema in definitions.values_mut().flat_map(<&mut Schema>::try_from) { + for visitor in self.visitors_mut() { + visitor.visit_schema(subschema); + } + } + } } + } - Some(object) + fn definitions_path_stripped(&self) -> &str { + let path = &self.settings.definitions_path; + let path = path.strip_prefix('#').unwrap_or(path); + path.strip_suffix('/').unwrap_or(path) } +} - fn run_visitors(schema: &mut Schema, visitors: &mut [Box]) { - for visitor in visitors { - visitor.visit_schema(schema); +fn json_pointer_mut<'a>( + mut object: &'a mut Map, + pointer: &str, + create_if_missing: bool, +) -> Option<&'a mut Map> { + let pointer = pointer.strip_prefix('/')?; + if pointer.is_empty() { + return Some(object); + } + + for mut segment in pointer.split('/') { + let replaced: String; + if segment.contains('~') { + replaced = segment.replace("~1", "/").replace("~0", "~"); + segment = &replaced; } + + use serde_json::map::Entry; + let next_value = match object.entry(segment) { + Entry::Occupied(o) => o.into_mut(), + Entry::Vacant(v) if create_if_missing => v.insert(Value::Object(Map::default())), + Entry::Vacant(_) => return None, + }; + + object = next_value.as_object_mut()?; } + + Some(object) } /// A [Visitor](Visitor) which implements additional traits required to be included in a [SchemaSettings]. diff --git a/schemars/src/visit.rs b/schemars/src/visit.rs index 5d69a2f..f82a9ea 100644 --- a/schemars/src/visit.rs +++ b/schemars/src/visit.rs @@ -85,7 +85,7 @@ pub fn visit_schema(v: &mut V, schema: &mut Schema) { v.visit_schema(subschema) } } - "properties" | "patternProperties" => { + "properties" | "patternProperties" | "$defs" | "definitions" => { if let Some(obj) = value.as_object_mut() { for value in obj.values_mut() { if let Ok(subschema) = value.try_into() { From 92d02b215dec0f5bb54348af1239ed4a8b62e3d6 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Thu, 23 May 2024 17:44:50 +0100 Subject: [PATCH 15/31] impl JsonSchema for Schema --- schemars/src/schema.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/schemars/src/schema.rs b/schemars/src/schema.rs index 27cb048..6eb55e8 100644 --- a/schemars/src/schema.rs +++ b/schemars/src/schema.rs @@ -2,8 +2,7 @@ JSON Schema types. */ -use ref_cast::ref_cast_custom; -use ref_cast::RefCastCustom; +use ref_cast::{ref_cast_custom, RefCastCustom}; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; @@ -175,6 +174,22 @@ impl From for Schema { } } +impl crate::JsonSchema for Schema { + fn schema_name() -> String { + "Schema".to_owned() + } + + fn schema_id() -> std::borrow::Cow<'static, str> { + "schemars::Schema".into() + } + + fn json_schema(_: &mut crate::gen::SchemaGenerator) -> Schema { + crate::json_schema!({ + "type": ["object", "boolean"] + }) + } +} + mod ser { use serde::ser::{Serialize, SerializeMap, SerializeSeq}; use serde_json::Value; From d1a39bf3e87a9e1ef1f6bdb4dd11dc26bf005efd Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Sun, 26 May 2024 15:20:56 +0100 Subject: [PATCH 16/31] Make `schema_name()` return `Cow<'static, str>` instead of `String` --- docs/2-implementing.md | 13 +++---- schemars/src/gen.rs | 35 +++++++++---------- schemars/src/json_schema_impls/array.rs | 13 ++++--- schemars/src/json_schema_impls/arrayvec07.rs | 4 +-- schemars/src/json_schema_impls/chrono04.rs | 12 +++---- schemars/src/json_schema_impls/core.rs | 24 ++++++------- schemars/src/json_schema_impls/decimal.rs | 8 ++--- schemars/src/json_schema_impls/either1.rs | 10 ++---- schemars/src/json_schema_impls/ffi.rs | 6 ++-- schemars/src/json_schema_impls/maps.rs | 6 ++-- schemars/src/json_schema_impls/mod.rs | 2 +- .../src/json_schema_impls/nonzero_signed.rs | 6 ++-- .../src/json_schema_impls/nonzero_unsigned.rs | 8 ++--- schemars/src/json_schema_impls/primitives.rs | 30 +++++----------- schemars/src/json_schema_impls/semver1.rs | 6 ++-- schemars/src/json_schema_impls/sequences.rs | 14 ++++---- schemars/src/json_schema_impls/serdejson.rs | 16 +++------ schemars/src/json_schema_impls/time.rs | 12 +++---- schemars/src/json_schema_impls/tuple.rs | 6 ++-- schemars/src/json_schema_impls/url2.rs | 6 ++-- schemars/src/json_schema_impls/uuid1.rs | 6 ++-- schemars/src/lib.rs | 18 +++++----- schemars/src/schema.rs | 4 +-- schemars_derive/src/lib.rs | 14 +++++--- schemars_derive/src/schema_exprs.rs | 4 +-- 25 files changed, 126 insertions(+), 157 deletions(-) diff --git a/docs/2-implementing.md b/docs/2-implementing.md index c8dfb6e..b3cea57 100644 --- a/docs/2-implementing.md +++ b/docs/2-implementing.md @@ -12,13 +12,11 @@ permalink: /implementing/ ## schema_name ```rust -fn schema_name() -> String; +fn schema_name() -> Cow<'static, str>; ``` This function returns the human-readable friendly name of the type's schema, which frequently is just the name of the type itself. The schema name is used as the title for root schemas, and the key within the root's `definitions` property for subschemas. -NB in a future version of schemars, it's likely that this function will be changed to return a `Cow<'static, str>`. - ## schema_id ```rust @@ -29,8 +27,7 @@ This function returns a unique identifier of the type's schema - if two types re ```rust fn schema_id() -> Cow<'static, str> { - Cow::Owned( - format!("[{}]", T::schema_id())) + format!("[{}]", T::schema_id()).into() } ``` @@ -40,14 +37,14 @@ For a type with no generic type arguments, a reasonable implementation of this f ```rust impl JsonSchema for NonGenericType { - fn schema_name() -> String { + fn schema_name() -> Cow<'static, str> { // Exclude the module path to make the name in generated schemas clearer. - "NonGenericType".to_owned() + "NonGenericType".into() } fn schema_id() -> Cow<'static, str> { // Include the module, in case a type with the same name is in another module/crate - Cow::Borrowed(concat!(module_path!(), "::NonGenericType")) + concat!(module_path!(), "::NonGenericType").into() } fn json_schema(_gen: &mut SchemaGenerator) -> Schema { diff --git a/schemars/src/gen.rs b/schemars/src/gen.rs index b4b52bf..5dca557 100644 --- a/schemars/src/gen.rs +++ b/schemars/src/gen.rs @@ -12,10 +12,11 @@ use crate::{visit::*, JsonSchema}; use dyn_clone::DynClone; use serde::Serialize; use serde_json::{Map, Value}; -use std::borrow::Cow; use std::collections::HashMap; use std::{any::Any, collections::HashSet, fmt::Debug}; +type CowStr = std::borrow::Cow<'static, str>; + /// Settings to customize how Schemas are generated. /// /// The default settings currently conform to [JSON Schema 2020-12](https://json-schema.org/specification-links#2020-12), but this is liable to change in a future version of Schemars if support for other JSON Schema versions is added. @@ -168,9 +169,9 @@ impl SchemaSettings { pub struct SchemaGenerator { settings: SchemaSettings, definitions: Map, - pending_schema_ids: HashSet>, - schema_id_to_name: HashMap, String>, - used_schema_names: HashSet, + pending_schema_ids: HashSet, + schema_id_to_name: HashMap, + used_schema_names: HashSet, } impl Clone for SchemaGenerator { @@ -233,11 +234,11 @@ impl SchemaGenerator { Some(n) => n, None => { let base_name = T::schema_name(); - let mut name = String::new(); + let mut name = CowStr::Borrowed(""); - if self.used_schema_names.contains(&base_name) { + if self.used_schema_names.contains(base_name.as_ref()) { for i in 2.. { - name = format!("{}{}", base_name, i); + name = format!("{}{}", base_name, i).into(); if !self.used_schema_names.contains(&name) { break; } @@ -253,7 +254,7 @@ impl SchemaGenerator { }; let reference = format!("#{}/{}", self.definitions_path_stripped(), name); - if !self.definitions.contains_key(&name) { + if !self.definitions.contains_key(name.as_ref()) { self.insert_new_subschema_for::(name, id); } Schema::new_ref(reference) @@ -262,18 +263,14 @@ impl SchemaGenerator { } } - fn insert_new_subschema_for( - &mut self, - name: String, - id: Cow<'static, str>, - ) { + fn insert_new_subschema_for(&mut self, name: CowStr, id: CowStr) { let dummy = false.into(); // insert into definitions BEFORE calling json_schema to avoid infinite recursion - self.definitions.insert(name.clone(), dummy); + self.definitions.insert(name.clone().into(), dummy); let schema = self.json_schema_internal::(id); - self.definitions.insert(name, schema.to_value()); + self.definitions.insert(name.into(), schema.to_value()); } /// Borrows the collection of all [referenceable](JsonSchema::is_referenceable) schemas that have been generated. @@ -413,15 +410,15 @@ impl SchemaGenerator { Ok(schema) } - fn json_schema_internal(&mut self, id: Cow<'static, str>) -> Schema { + fn json_schema_internal(&mut self, id: CowStr) -> Schema { struct PendingSchemaState<'a> { gen: &'a mut SchemaGenerator, - id: Cow<'static, str>, + id: CowStr, did_add: bool, } impl<'a> PendingSchemaState<'a> { - fn new(gen: &'a mut SchemaGenerator, id: Cow<'static, str>) -> Self { + fn new(gen: &'a mut SchemaGenerator, id: CowStr) -> Self { let did_add = gen.pending_schema_ids.insert(id.clone()); Self { gen, id, did_add } } @@ -480,6 +477,8 @@ impl SchemaGenerator { } } + /// Returns `self.settings.definitions_path` as a plain JSON pointer to the definitions object, + /// i.e. without a leading '#' or trailing '/' fn definitions_path_stripped(&self) -> &str { let path = &self.settings.definitions_path; let path = path.strip_prefix('#').unwrap_or(path); diff --git a/schemars/src/json_schema_impls/array.rs b/schemars/src/json_schema_impls/array.rs index bacc079..1da1abb 100644 --- a/schemars/src/json_schema_impls/array.rs +++ b/schemars/src/json_schema_impls/array.rs @@ -6,12 +6,12 @@ use std::borrow::Cow; impl JsonSchema for [T; 0] { no_ref_schema!(); - fn schema_name() -> String { - "EmptyArray".to_owned() + fn schema_name() -> Cow<'static, str> { + "EmptyArray".into() } fn schema_id() -> Cow<'static, str> { - Cow::Borrowed("[]") + "[]".into() } fn json_schema(_: &mut SchemaGenerator) -> Schema { @@ -28,13 +28,12 @@ macro_rules! array_impls { impl JsonSchema for [T; $len] { no_ref_schema!(); - fn schema_name() -> String { - format!("Array_size_{}_of_{}", $len, T::schema_name()) + fn schema_name() -> Cow<'static, str> { + format!("Array_size_{}_of_{}", $len, T::schema_name()).into() } fn schema_id() -> Cow<'static, str> { - Cow::Owned( - format!("[{}; {}]", $len, T::schema_id())) + format!("[{}; {}]", $len, T::schema_id()).into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { diff --git a/schemars/src/json_schema_impls/arrayvec07.rs b/schemars/src/json_schema_impls/arrayvec07.rs index 3d4d050..920f7c3 100644 --- a/schemars/src/json_schema_impls/arrayvec07.rs +++ b/schemars/src/json_schema_impls/arrayvec07.rs @@ -12,8 +12,8 @@ where { no_ref_schema!(); - fn schema_name() -> String { - format!("Array_up_to_size_{}_of_{}", CAP, T::schema_name()) + fn schema_name() -> std::borrow::Cow<'static, str> { + format!("Array_up_to_size_{}_of_{}", CAP, T::schema_name()).into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { diff --git a/schemars/src/json_schema_impls/chrono04.rs b/schemars/src/json_schema_impls/chrono04.rs index 5fd23e8..e8afa2a 100644 --- a/schemars/src/json_schema_impls/chrono04.rs +++ b/schemars/src/json_schema_impls/chrono04.rs @@ -6,12 +6,12 @@ use std::borrow::Cow; impl JsonSchema for Weekday { no_ref_schema!(); - fn schema_name() -> String { - "Weekday".to_owned() + fn schema_name() -> Cow<'static, str> { + "Weekday".into() } fn schema_id() -> Cow<'static, str> { - Cow::Borrowed("chrono::Weekday") + "chrono::Weekday".into() } fn json_schema(_: &mut SchemaGenerator) -> Schema { @@ -38,12 +38,12 @@ macro_rules! formatted_string_impl { impl $($desc)+ { no_ref_schema!(); - fn schema_name() -> String { - stringify!($ty).to_owned() + fn schema_name() -> Cow<'static, str> { + stringify!($ty).into() } fn schema_id() -> Cow<'static, str> { - Cow::Borrowed(stringify!(chrono::$ty)) + stringify!(chrono::$ty).into() } fn json_schema(_: &mut SchemaGenerator) -> Schema { diff --git a/schemars/src/json_schema_impls/core.rs b/schemars/src/json_schema_impls/core.rs index 16104f8..0ca4e10 100644 --- a/schemars/src/json_schema_impls/core.rs +++ b/schemars/src/json_schema_impls/core.rs @@ -7,12 +7,12 @@ use std::ops::{Bound, Range, RangeInclusive}; impl JsonSchema for Option { no_ref_schema!(); - fn schema_name() -> String { - format!("Nullable_{}", T::schema_name()) + fn schema_name() -> Cow<'static, str> { + format!("Nullable_{}", T::schema_name()).into() } fn schema_id() -> Cow<'static, str> { - Cow::Owned(format!("Option<{}>", T::schema_id())) + format!("Option<{}>", T::schema_id()).into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { @@ -69,12 +69,12 @@ impl JsonSchema for Option { } impl JsonSchema for Result { - fn schema_name() -> String { - format!("Result_of_{}_or_{}", T::schema_name(), E::schema_name()) + fn schema_name() -> Cow<'static, str> { + format!("Result_of_{}_or_{}", T::schema_name(), E::schema_name()).into() } fn schema_id() -> Cow<'static, str> { - Cow::Owned(format!("Result<{}, {}>", T::schema_id(), E::schema_id())) + format!("Result<{}, {}>", T::schema_id(), E::schema_id()).into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { @@ -100,12 +100,12 @@ impl JsonSchema for Result { } impl JsonSchema for Bound { - fn schema_name() -> String { - format!("Bound_of_{}", T::schema_name()) + fn schema_name() -> Cow<'static, str> { + format!("Bound_of_{}", T::schema_name()).into() } fn schema_id() -> Cow<'static, str> { - Cow::Owned(format!("Bound<{}>", T::schema_id())) + format!("Bound<{}>", T::schema_id()).into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { @@ -135,12 +135,12 @@ impl JsonSchema for Bound { } impl JsonSchema for Range { - fn schema_name() -> String { - format!("Range_of_{}", T::schema_name()) + fn schema_name() -> Cow<'static, str> { + format!("Range_of_{}", T::schema_name()).into() } fn schema_id() -> Cow<'static, str> { - Cow::Owned(format!("Range<{}>", T::schema_id())) + format!("Range<{}>", T::schema_id()).into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { diff --git a/schemars/src/json_schema_impls/decimal.rs b/schemars/src/json_schema_impls/decimal.rs index 462e3d5..0ca9840 100644 --- a/schemars/src/json_schema_impls/decimal.rs +++ b/schemars/src/json_schema_impls/decimal.rs @@ -7,12 +7,8 @@ macro_rules! decimal_impl { impl JsonSchema for $type { no_ref_schema!(); - fn schema_name() -> String { - "Decimal".to_owned() - } - - fn schema_id() -> Cow<'static, str> { - Cow::Borrowed("Decimal") + fn schema_name() -> Cow<'static, str> { + "Decimal".into() } fn json_schema(_: &mut SchemaGenerator) -> Schema { diff --git a/schemars/src/json_schema_impls/either1.rs b/schemars/src/json_schema_impls/either1.rs index 96ed8a8..334e973 100644 --- a/schemars/src/json_schema_impls/either1.rs +++ b/schemars/src/json_schema_impls/either1.rs @@ -6,16 +6,12 @@ use std::borrow::Cow; impl JsonSchema for Either { no_ref_schema!(); - fn schema_name() -> String { - format!("Either_{}_or_{}", L::schema_name(), R::schema_name()) + fn schema_name() -> Cow<'static, str> { + format!("Either_{}_or_{}", L::schema_name(), R::schema_name()).into() } fn schema_id() -> Cow<'static, str> { - Cow::Owned(format!( - "either::Either<{}, {}>", - L::schema_id(), - R::schema_id() - )) + format!("either::Either<{}, {}>", L::schema_id(), R::schema_id()).into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { diff --git a/schemars/src/json_schema_impls/ffi.rs b/schemars/src/json_schema_impls/ffi.rs index 4ad85c2..026d5ae 100644 --- a/schemars/src/json_schema_impls/ffi.rs +++ b/schemars/src/json_schema_impls/ffi.rs @@ -4,12 +4,12 @@ use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; impl JsonSchema for OsString { - fn schema_name() -> String { - "OsString".to_owned() + fn schema_name() -> Cow<'static, str> { + "OsString".into() } fn schema_id() -> Cow<'static, str> { - Cow::Borrowed("std::ffi::OsString") + "std::ffi::OsString".into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { diff --git a/schemars/src/json_schema_impls/maps.rs b/schemars/src/json_schema_impls/maps.rs index b934f8b..9a630a7 100644 --- a/schemars/src/json_schema_impls/maps.rs +++ b/schemars/src/json_schema_impls/maps.rs @@ -10,12 +10,12 @@ macro_rules! map_impl { { no_ref_schema!(); - fn schema_name() -> String { - format!("Map_of_{}", V::schema_name()) + fn schema_name() -> Cow<'static, str> { + format!("Map_of_{}", V::schema_name()).into() } fn schema_id() -> Cow<'static, str> { - Cow::Owned(format!("Map<{}>", V::schema_id())) + format!("Map<{}>", V::schema_id()).into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { diff --git a/schemars/src/json_schema_impls/mod.rs b/schemars/src/json_schema_impls/mod.rs index ebcb61b..e45e84c 100644 --- a/schemars/src/json_schema_impls/mod.rs +++ b/schemars/src/json_schema_impls/mod.rs @@ -13,7 +13,7 @@ macro_rules! forward_impl { <$target>::is_referenceable() } - fn schema_name() -> String { + fn schema_name() -> std::borrow::Cow<'static, str> { <$target>::schema_name() } diff --git a/schemars/src/json_schema_impls/nonzero_signed.rs b/schemars/src/json_schema_impls/nonzero_signed.rs index d1b5142..634acc1 100644 --- a/schemars/src/json_schema_impls/nonzero_signed.rs +++ b/schemars/src/json_schema_impls/nonzero_signed.rs @@ -8,12 +8,12 @@ macro_rules! nonzero_unsigned_impl { impl JsonSchema for $type { no_ref_schema!(); - fn schema_name() -> String { - stringify!($type).to_owned() + fn schema_name() -> Cow<'static, str> { + stringify!($type).into() } fn schema_id() -> Cow<'static, str> { - Cow::Borrowed(stringify!(std::num::$type)) + stringify!(std::num::$type).into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { diff --git a/schemars/src/json_schema_impls/nonzero_unsigned.rs b/schemars/src/json_schema_impls/nonzero_unsigned.rs index e3c6d1d..743af65 100644 --- a/schemars/src/json_schema_impls/nonzero_unsigned.rs +++ b/schemars/src/json_schema_impls/nonzero_unsigned.rs @@ -1,6 +1,6 @@ use crate::gen::SchemaGenerator; -use crate::Schema; use crate::JsonSchema; +use crate::Schema; use std::borrow::Cow; use std::num::*; @@ -9,12 +9,12 @@ macro_rules! nonzero_unsigned_impl { impl JsonSchema for $type { no_ref_schema!(); - fn schema_name() -> String { - stringify!($type).to_owned() + fn schema_name() -> Cow<'static, str> { + stringify!($type).into() } fn schema_id() -> Cow<'static, str> { - Cow::Borrowed(stringify!(std::num::$type)) + stringify!(std::num::$type).into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { diff --git a/schemars/src/json_schema_impls/primitives.rs b/schemars/src/json_schema_impls/primitives.rs index 63121fe..b65a93a 100644 --- a/schemars/src/json_schema_impls/primitives.rs +++ b/schemars/src/json_schema_impls/primitives.rs @@ -9,12 +9,8 @@ macro_rules! simple_impl { impl JsonSchema for $type { no_ref_schema!(); - fn schema_name() -> String { - $instance_type.to_owned() - } - - fn schema_id() -> Cow<'static, str> { - Cow::Borrowed($instance_type) + fn schema_name() -> Cow<'static, str> { + $instance_type.into() } fn json_schema(_: &mut SchemaGenerator) -> Schema { @@ -28,12 +24,8 @@ macro_rules! simple_impl { impl JsonSchema for $type { no_ref_schema!(); - fn schema_name() -> String { - $format.to_owned() - } - - fn schema_id() -> Cow<'static, str> { - Cow::Borrowed($format) + fn schema_name() -> Cow<'static, str> { + $format.into() } fn json_schema(_: &mut SchemaGenerator) -> Schema { @@ -75,12 +67,8 @@ macro_rules! unsigned_impl { impl JsonSchema for $type { no_ref_schema!(); - fn schema_name() -> String { - $format.to_owned() - } - - fn schema_id() -> Cow<'static, str> { - Cow::Borrowed($format) + fn schema_name() -> Cow<'static, str> { + $format.into() } fn json_schema(_: &mut SchemaGenerator) -> Schema { @@ -104,12 +92,12 @@ unsigned_impl!(usize => "integer", "uint"); impl JsonSchema for char { no_ref_schema!(); - fn schema_name() -> String { - "Character".to_owned() + fn schema_name() -> Cow<'static, str> { + "Character".into() } fn schema_id() -> Cow<'static, str> { - Cow::Borrowed("char") + "char".into() } fn json_schema(_: &mut SchemaGenerator) -> Schema { diff --git a/schemars/src/json_schema_impls/semver1.rs b/schemars/src/json_schema_impls/semver1.rs index fb47f78..dbcdf21 100644 --- a/schemars/src/json_schema_impls/semver1.rs +++ b/schemars/src/json_schema_impls/semver1.rs @@ -6,12 +6,12 @@ use std::borrow::Cow; impl JsonSchema for Version { no_ref_schema!(); - fn schema_name() -> String { - "Version".to_owned() + fn schema_name() -> Cow<'static, str> { + "Version".into() } fn schema_id() -> Cow<'static, str> { - Cow::Borrowed("semver::Version") + "semver::Version".into() } fn json_schema(_: &mut SchemaGenerator) -> Schema { diff --git a/schemars/src/json_schema_impls/sequences.rs b/schemars/src/json_schema_impls/sequences.rs index d14d4d3..dcc680f 100644 --- a/schemars/src/json_schema_impls/sequences.rs +++ b/schemars/src/json_schema_impls/sequences.rs @@ -10,13 +10,12 @@ macro_rules! seq_impl { { no_ref_schema!(); - fn schema_name() -> String { - format!("Array_of_{}", T::schema_name()) + fn schema_name() -> Cow<'static, str> { + format!("Array_of_{}", T::schema_name()).into() } fn schema_id() -> Cow<'static, str> { - Cow::Owned( - format!("[{}]", T::schema_id())) + format!("[{}]", T::schema_id()).into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { @@ -37,13 +36,12 @@ macro_rules! set_impl { { no_ref_schema!(); - fn schema_name() -> String { - format!("Set_of_{}", T::schema_name()) + fn schema_name() -> Cow<'static, str> { + format!("Set_of_{}", T::schema_name()).into() } fn schema_id() -> Cow<'static, str> { - Cow::Owned( - format!("Set<{}>", T::schema_id())) + format!("Set<{}>", T::schema_id()).into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { diff --git a/schemars/src/json_schema_impls/serdejson.rs b/schemars/src/json_schema_impls/serdejson.rs index 5c6b58d..fe76748 100644 --- a/schemars/src/json_schema_impls/serdejson.rs +++ b/schemars/src/json_schema_impls/serdejson.rs @@ -7,12 +7,8 @@ use std::collections::BTreeMap; impl JsonSchema for Value { no_ref_schema!(); - fn schema_name() -> String { - "AnyValue".to_owned() - } - - fn schema_id() -> Cow<'static, str> { - Cow::Borrowed("AnyValue") + fn schema_name() -> Cow<'static, str> { + "AnyValue".into() } fn json_schema(_: &mut SchemaGenerator) -> Schema { @@ -25,12 +21,8 @@ forward_impl!(Map => BTreeMap); impl JsonSchema for Number { no_ref_schema!(); - fn schema_name() -> String { - "Number".to_owned() - } - - fn schema_id() -> Cow<'static, str> { - Cow::Borrowed("Number") + fn schema_name() -> Cow<'static, str> { + "Number".into() } fn json_schema(_: &mut SchemaGenerator) -> Schema { diff --git a/schemars/src/json_schema_impls/time.rs b/schemars/src/json_schema_impls/time.rs index 7317cfd..0f1eb74 100644 --- a/schemars/src/json_schema_impls/time.rs +++ b/schemars/src/json_schema_impls/time.rs @@ -4,12 +4,12 @@ use std::borrow::Cow; use std::time::{Duration, SystemTime}; impl JsonSchema for Duration { - fn schema_name() -> String { - "Duration".to_owned() + fn schema_name() -> Cow<'static, str> { + "Duration".into() } fn schema_id() -> Cow<'static, str> { - Cow::Borrowed("std::time::Duration") + "std::time::Duration".into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { @@ -25,12 +25,12 @@ impl JsonSchema for Duration { } impl JsonSchema for SystemTime { - fn schema_name() -> String { - "SystemTime".to_owned() + fn schema_name() -> Cow<'static, str> { + "SystemTime".into() } fn schema_id() -> Cow<'static, str> { - Cow::Borrowed("std::time::SystemTime") + "std::time::SystemTime".into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { diff --git a/schemars/src/json_schema_impls/tuple.rs b/schemars/src/json_schema_impls/tuple.rs index 204169e..2af816a 100644 --- a/schemars/src/json_schema_impls/tuple.rs +++ b/schemars/src/json_schema_impls/tuple.rs @@ -8,10 +8,10 @@ macro_rules! tuple_impls { impl<$($name: JsonSchema),+> JsonSchema for ($($name,)+) { no_ref_schema!(); - fn schema_name() -> String { + fn schema_name() -> Cow<'static, str> { let mut name = "Tuple_of_".to_owned(); name.push_str(&[$($name::schema_name()),+].join("_and_")); - name + name.into() } fn schema_id() -> Cow<'static, str> { @@ -19,7 +19,7 @@ macro_rules! tuple_impls { id.push_str(&[$($name::schema_id()),+].join(",")); id.push(')'); - Cow::Owned(id) + id.into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { diff --git a/schemars/src/json_schema_impls/url2.rs b/schemars/src/json_schema_impls/url2.rs index 2fcfd04..04fa1e8 100644 --- a/schemars/src/json_schema_impls/url2.rs +++ b/schemars/src/json_schema_impls/url2.rs @@ -6,12 +6,12 @@ use url2::Url; impl JsonSchema for Url { no_ref_schema!(); - fn schema_name() -> String { - "Url".to_owned() + fn schema_name() -> Cow<'static, str> { + "Url".into() } fn schema_id() -> Cow<'static, str> { - Cow::Borrowed("url::Url") + "url::Url".into() } fn json_schema(_: &mut SchemaGenerator) -> Schema { diff --git a/schemars/src/json_schema_impls/uuid1.rs b/schemars/src/json_schema_impls/uuid1.rs index 825f7a2..297914e 100644 --- a/schemars/src/json_schema_impls/uuid1.rs +++ b/schemars/src/json_schema_impls/uuid1.rs @@ -6,12 +6,12 @@ use uuid1::Uuid; impl JsonSchema for Uuid { no_ref_schema!(); - fn schema_name() -> String { - "Uuid".to_string() + fn schema_name() -> Cow<'static, str> { + "Uuid".into() } fn schema_id() -> Cow<'static, str> { - Cow::Borrowed("uuid::Uuid") + "uuid::Uuid".into() } fn json_schema(_: &mut SchemaGenerator) -> Schema { diff --git a/schemars/src/lib.rs b/schemars/src/lib.rs index 2059a24..27d06d6 100644 --- a/schemars/src/lib.rs +++ b/schemars/src/lib.rs @@ -58,14 +58,14 @@ pub use schema::Schema; /// struct NonGenericType; /// /// impl JsonSchema for NonGenericType { -/// fn schema_name() -> String { +/// fn schema_name() -> Cow<'static, str> { /// // Exclude the module path to make the name in generated schemas clearer. -/// "NonGenericType".to_owned() +/// "NonGenericType".into() /// } /// /// fn schema_id() -> Cow<'static, str> { /// // Include the module, in case a type with the same name is in another module/crate -/// Cow::Borrowed(concat!(module_path!(), "::NonGenericType")) +/// concat!(module_path!(), "::NonGenericType").into() /// } /// /// fn json_schema(_gen: &mut SchemaGenerator) -> Schema { @@ -85,16 +85,16 @@ pub use schema::Schema; /// struct GenericType(PhantomData); /// /// impl JsonSchema for GenericType { -/// fn schema_name() -> String { -/// format!("GenericType_{}", T::schema_name()) +/// fn schema_name() -> Cow<'static, str> { +/// format!("GenericType_{}", T::schema_name()).into() /// } /// /// fn schema_id() -> Cow<'static, str> { -/// Cow::Owned(format!( +/// format!( /// "{}::GenericType<{}>", /// module_path!(), /// T::schema_id() -/// )) +/// ).into() /// } /// /// fn json_schema(_gen: &mut SchemaGenerator) -> Schema { @@ -120,7 +120,7 @@ pub trait JsonSchema { /// The name of the generated JSON Schema. /// /// This is used as the title for root schemas, and the key within the root's `definitions` property for subschemas. - fn schema_name() -> String; + fn schema_name() -> Cow<'static, str>; /// Returns a string that uniquely identifies the schema produced by this type. /// @@ -130,7 +130,7 @@ pub trait JsonSchema { /// /// The default implementation returns the same value as `schema_name()`. fn schema_id() -> Cow<'static, str> { - Cow::Owned(Self::schema_name()) + Self::schema_name() } /// Generates a JSON Schema for this type. diff --git a/schemars/src/schema.rs b/schemars/src/schema.rs index 6eb55e8..91a3547 100644 --- a/schemars/src/schema.rs +++ b/schemars/src/schema.rs @@ -175,8 +175,8 @@ impl From for Schema { } impl crate::JsonSchema for Schema { - fn schema_name() -> String { - "Schema".to_owned() + fn schema_name() -> std::borrow::Cow<'static, str> { + "Schema".into() } fn schema_id() -> std::borrow::Cow<'static, str> { diff --git a/schemars_derive/src/lib.rs b/schemars_derive/src/lib.rs index c890f75..a4c0742 100644 --- a/schemars_derive/src/lib.rs +++ b/schemars_derive/src/lib.rs @@ -60,7 +60,7 @@ fn derive_json_schema(mut input: syn::DeriveInput, repr: bool) -> syn::Result::is_referenceable() } - fn schema_name() -> std::string::String { + fn schema_name() -> std::borrow::Cow<'static, str> { <#ty as schemars::JsonSchema>::schema_name() } @@ -104,7 +104,7 @@ fn derive_json_schema(mut input: syn::DeriveInput, repr: bool) -> syn::Result syn::Result syn::Result syn::Result std::string::String { + fn schema_name() -> std::borrow::Cow<'static, str> { #schema_name } diff --git a/schemars_derive/src/schema_exprs.rs b/schemars_derive/src/schema_exprs.rs index 87e789e..9fbc9ee 100644 --- a/schemars_derive/src/schema_exprs.rs +++ b/schemars_derive/src/schema_exprs.rs @@ -114,8 +114,8 @@ fn type_for_schema(with_attr: &WithAttr) -> (syn::Type, Option) { false } - fn schema_name() -> std::string::String { - #fn_name.to_string() + fn schema_name() -> std::borrow::Cow<'static, str> { + std::borrow::Cow::Borrowed(#fn_name) } fn schema_id() -> std::borrow::Cow<'static, str> { From 5076e8b6c9b00e703991e2fa562f13e8465f060d Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Sun, 26 May 2024 16:51:42 +0100 Subject: [PATCH 17/31] Replace `is_referenceable()` with `always_inline_schema()` --- docs/2-implementing.md | 10 +++++----- schemars/src/gen.rs | 16 ++++++++-------- schemars/src/json_schema_impls/array.rs | 4 ++-- schemars/src/json_schema_impls/arrayvec07.rs | 2 +- schemars/src/json_schema_impls/chrono04.rs | 4 ++-- schemars/src/json_schema_impls/core.rs | 2 +- schemars/src/json_schema_impls/decimal.rs | 2 +- schemars/src/json_schema_impls/either1.rs | 2 +- schemars/src/json_schema_impls/maps.rs | 2 +- schemars/src/json_schema_impls/mod.rs | 10 +++++----- schemars/src/json_schema_impls/nonzero_signed.rs | 2 +- .../src/json_schema_impls/nonzero_unsigned.rs | 2 +- schemars/src/json_schema_impls/primitives.rs | 8 ++++---- schemars/src/json_schema_impls/semver1.rs | 2 +- schemars/src/json_schema_impls/sequences.rs | 4 ++-- schemars/src/json_schema_impls/serdejson.rs | 4 ++-- schemars/src/json_schema_impls/tuple.rs | 2 +- schemars/src/json_schema_impls/url2.rs | 2 +- schemars/src/json_schema_impls/uuid1.rs | 2 +- schemars/src/lib.rs | 15 ++++++++------- schemars_derive/src/lib.rs | 4 ++-- schemars_derive/src/schema_exprs.rs | 4 ++-- 22 files changed, 53 insertions(+), 52 deletions(-) diff --git a/docs/2-implementing.md b/docs/2-implementing.md index b3cea57..10375e4 100644 --- a/docs/2-implementing.md +++ b/docs/2-implementing.md @@ -63,14 +63,14 @@ This function creates the JSON schema itself. The `gen` argument can be used to `json_schema` should not return a `$ref` schema. -## is_referenceable (optional) +## always_inline_schema (optional) ```rust -fn is_referenceable() -> bool; +fn always_inline_schema() -> bool; ``` -If this function returns `true`, then Schemars can re-use the generate schema where possible by adding it to the root schema's `definitions` and having other schemas reference it using the `$ref` keyword. This can greatly simplify schemas that include a particular type multiple times, especially if that type's schema is fairly complex. +If this function returns `false`, then Schemars can re-use the generate schema where possible by adding it to the root schema's `definitions` and having other schemas reference it using the `$ref` keyword. This can greatly simplify schemas that include a particular type multiple times, especially if that type's schema is fairly complex. -Generally, this should return `false` for types with simple schemas (such as primitives). For more complex types, it should return `true`. For recursive types, this **must** return `true` to prevent infinite cycles when generating schemas. +Generally, this should return `true` for types with simple schemas (such as primitives). For more complex types, it should return `false`. For recursive types, this **must** return `false` to prevent infinite cycles when generating schemas. -The default implementation of this function returns `true` to reduce the chance of someone inadvertently causing infinite cycles with recursive types. +The default implementation of this function returns `false` to reduce the chance of someone inadvertently causing infinite cycles with recursive types. diff --git a/schemars/src/gen.rs b/schemars/src/gen.rs index 5dca557..336a715 100644 --- a/schemars/src/gen.rs +++ b/schemars/src/gen.rs @@ -219,14 +219,14 @@ impl SchemaGenerator { /// Generates a JSON Schema for the type `T`, and returns either the schema itself or a `$ref` schema referencing `T`'s schema. /// - /// If `T` is [referenceable](JsonSchema::is_referenceable), this will add `T`'s schema to this generator's definitions, and + /// If `T` is not [inlined](JsonSchema::always_inline_schema), this will add `T`'s schema to this generator's definitions, and /// return a `$ref` schema referencing that schema. Otherwise, this method behaves identically to [`JsonSchema::json_schema`]. /// - /// If `T`'s schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will + /// If `T`'s schema depends on any [non-inlined](JsonSchema::always_inline_schema) schemas, then this method will /// add them to the `SchemaGenerator`'s schema definitions. pub fn subschema_for(&mut self) -> Schema { let id = T::schema_id(); - let return_ref = T::is_referenceable() + let return_ref = !T::always_inline_schema() && (!self.settings.inline_subschemas || self.pending_schema_ids.contains(&id)); if return_ref { @@ -273,7 +273,7 @@ impl SchemaGenerator { self.definitions.insert(name.into(), schema.to_value()); } - /// Borrows the collection of all [referenceable](JsonSchema::is_referenceable) schemas that have been generated. + /// Borrows the collection of all [non-inlined](JsonSchema::always_inline_schema) schemas that have been generated. /// /// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the values are the schemas /// themselves. @@ -281,7 +281,7 @@ impl SchemaGenerator { &self.definitions } - /// Mutably borrows the collection of all [referenceable](JsonSchema::is_referenceable) schemas that have been generated. + /// Mutably borrows the collection of all [non-inlined](JsonSchema::always_inline_schema) schemas that have been generated. /// /// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the values are the schemas /// themselves. @@ -289,7 +289,7 @@ impl SchemaGenerator { &mut self.definitions } - /// Returns the collection of all [referenceable](JsonSchema::is_referenceable) schemas that have been generated, + /// Returns the collection of all [non-inlined](JsonSchema::always_inline_schema) schemas that have been generated, /// leaving an empty map in its place. /// /// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the values are the schemas @@ -305,7 +305,7 @@ impl SchemaGenerator { /// 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 [non-inlined](JsonSchema::always_inline_schema) 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) -> Schema { @@ -329,7 +329,7 @@ impl SchemaGenerator { /// 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 [non-inlined](JsonSchema::always_inline_schema) 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) -> Schema { let mut schema = self.json_schema_internal::(T::schema_id()); diff --git a/schemars/src/json_schema_impls/array.rs b/schemars/src/json_schema_impls/array.rs index 1da1abb..7dccbfb 100644 --- a/schemars/src/json_schema_impls/array.rs +++ b/schemars/src/json_schema_impls/array.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; // Does not require T: JsonSchema. impl JsonSchema for [T; 0] { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { "EmptyArray".into() @@ -26,7 +26,7 @@ macro_rules! array_impls { ($($len:tt)+) => { $( impl JsonSchema for [T; $len] { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { format!("Array_size_{}_of_{}", $len, T::schema_name()).into() diff --git a/schemars/src/json_schema_impls/arrayvec07.rs b/schemars/src/json_schema_impls/arrayvec07.rs index 920f7c3..cd5f154 100644 --- a/schemars/src/json_schema_impls/arrayvec07.rs +++ b/schemars/src/json_schema_impls/arrayvec07.rs @@ -10,7 +10,7 @@ impl JsonSchema for ArrayVec where T: JsonSchema, { - no_ref_schema!(); + always_inline!(); fn schema_name() -> std::borrow::Cow<'static, str> { format!("Array_up_to_size_{}_of_{}", CAP, T::schema_name()).into() diff --git a/schemars/src/json_schema_impls/chrono04.rs b/schemars/src/json_schema_impls/chrono04.rs index e8afa2a..2635e56 100644 --- a/schemars/src/json_schema_impls/chrono04.rs +++ b/schemars/src/json_schema_impls/chrono04.rs @@ -4,7 +4,7 @@ use chrono04::prelude::*; use std::borrow::Cow; impl JsonSchema for Weekday { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { "Weekday".into() @@ -36,7 +36,7 @@ macro_rules! formatted_string_impl { }; ($ty:ident, $format:literal, $($desc:tt)+) => { impl $($desc)+ { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { stringify!($ty).into() diff --git a/schemars/src/json_schema_impls/core.rs b/schemars/src/json_schema_impls/core.rs index 0ca4e10..d49f392 100644 --- a/schemars/src/json_schema_impls/core.rs +++ b/schemars/src/json_schema_impls/core.rs @@ -5,7 +5,7 @@ use std::borrow::Cow; use std::ops::{Bound, Range, RangeInclusive}; impl JsonSchema for Option { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { format!("Nullable_{}", T::schema_name()).into() diff --git a/schemars/src/json_schema_impls/decimal.rs b/schemars/src/json_schema_impls/decimal.rs index 0ca9840..3266ccc 100644 --- a/schemars/src/json_schema_impls/decimal.rs +++ b/schemars/src/json_schema_impls/decimal.rs @@ -5,7 +5,7 @@ use std::borrow::Cow; macro_rules! decimal_impl { ($type:ty) => { impl JsonSchema for $type { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { "Decimal".into() diff --git a/schemars/src/json_schema_impls/either1.rs b/schemars/src/json_schema_impls/either1.rs index 334e973..fcc3479 100644 --- a/schemars/src/json_schema_impls/either1.rs +++ b/schemars/src/json_schema_impls/either1.rs @@ -4,7 +4,7 @@ use either1::Either; use std::borrow::Cow; impl JsonSchema for Either { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { format!("Either_{}_or_{}", L::schema_name(), R::schema_name()).into() diff --git a/schemars/src/json_schema_impls/maps.rs b/schemars/src/json_schema_impls/maps.rs index 9a630a7..19ae5cf 100644 --- a/schemars/src/json_schema_impls/maps.rs +++ b/schemars/src/json_schema_impls/maps.rs @@ -8,7 +8,7 @@ macro_rules! map_impl { where V: JsonSchema, { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { format!("Map_of_{}", V::schema_name()).into() diff --git a/schemars/src/json_schema_impls/mod.rs b/schemars/src/json_schema_impls/mod.rs index e45e84c..ca9ff28 100644 --- a/schemars/src/json_schema_impls/mod.rs +++ b/schemars/src/json_schema_impls/mod.rs @@ -1,7 +1,7 @@ -macro_rules! no_ref_schema { +macro_rules! always_inline { () => { - fn is_referenceable() -> bool { - false + fn always_inline_schema() -> bool { + true } }; } @@ -9,8 +9,8 @@ macro_rules! no_ref_schema { macro_rules! forward_impl { (($($impl:tt)+) => $target:ty) => { impl $($impl)+ { - fn is_referenceable() -> bool { - <$target>::is_referenceable() + fn always_inline_schema() -> bool { + <$target>::always_inline_schema() } fn schema_name() -> std::borrow::Cow<'static, str> { diff --git a/schemars/src/json_schema_impls/nonzero_signed.rs b/schemars/src/json_schema_impls/nonzero_signed.rs index 634acc1..c9d0919 100644 --- a/schemars/src/json_schema_impls/nonzero_signed.rs +++ b/schemars/src/json_schema_impls/nonzero_signed.rs @@ -6,7 +6,7 @@ use std::num::*; macro_rules! nonzero_unsigned_impl { ($type:ty => $primitive:ty) => { impl JsonSchema for $type { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { stringify!($type).into() diff --git a/schemars/src/json_schema_impls/nonzero_unsigned.rs b/schemars/src/json_schema_impls/nonzero_unsigned.rs index 743af65..afabc05 100644 --- a/schemars/src/json_schema_impls/nonzero_unsigned.rs +++ b/schemars/src/json_schema_impls/nonzero_unsigned.rs @@ -7,7 +7,7 @@ use std::num::*; macro_rules! nonzero_unsigned_impl { ($type:ty => $primitive:ty) => { impl JsonSchema for $type { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { stringify!($type).into() diff --git a/schemars/src/json_schema_impls/primitives.rs b/schemars/src/json_schema_impls/primitives.rs index b65a93a..a3bcce6 100644 --- a/schemars/src/json_schema_impls/primitives.rs +++ b/schemars/src/json_schema_impls/primitives.rs @@ -7,7 +7,7 @@ use std::path::{Path, PathBuf}; macro_rules! simple_impl { ($type:ty => $instance_type:literal) => { impl JsonSchema for $type { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { $instance_type.into() @@ -22,7 +22,7 @@ macro_rules! simple_impl { }; ($type:ty => $instance_type:literal, $format:literal) => { impl JsonSchema for $type { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { $format.into() @@ -65,7 +65,7 @@ simple_impl!(SocketAddrV6 => "string"); macro_rules! unsigned_impl { ($type:ty => $instance_type:literal, $format:literal) => { impl JsonSchema for $type { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { $format.into() @@ -90,7 +90,7 @@ unsigned_impl!(u128 => "integer", "uint128"); unsigned_impl!(usize => "integer", "uint"); impl JsonSchema for char { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { "Character".into() diff --git a/schemars/src/json_schema_impls/semver1.rs b/schemars/src/json_schema_impls/semver1.rs index dbcdf21..eb121fb 100644 --- a/schemars/src/json_schema_impls/semver1.rs +++ b/schemars/src/json_schema_impls/semver1.rs @@ -4,7 +4,7 @@ use semver1::Version; use std::borrow::Cow; impl JsonSchema for Version { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { "Version".into() diff --git a/schemars/src/json_schema_impls/sequences.rs b/schemars/src/json_schema_impls/sequences.rs index dcc680f..c131339 100644 --- a/schemars/src/json_schema_impls/sequences.rs +++ b/schemars/src/json_schema_impls/sequences.rs @@ -8,7 +8,7 @@ macro_rules! seq_impl { where T: JsonSchema, { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { format!("Array_of_{}", T::schema_name()).into() @@ -34,7 +34,7 @@ macro_rules! set_impl { where T: JsonSchema, { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { format!("Set_of_{}", T::schema_name()).into() diff --git a/schemars/src/json_schema_impls/serdejson.rs b/schemars/src/json_schema_impls/serdejson.rs index fe76748..cd25326 100644 --- a/schemars/src/json_schema_impls/serdejson.rs +++ b/schemars/src/json_schema_impls/serdejson.rs @@ -5,7 +5,7 @@ use std::borrow::Cow; use std::collections::BTreeMap; impl JsonSchema for Value { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { "AnyValue".into() @@ -19,7 +19,7 @@ impl JsonSchema for Value { forward_impl!(Map => BTreeMap); impl JsonSchema for Number { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { "Number".into() diff --git a/schemars/src/json_schema_impls/tuple.rs b/schemars/src/json_schema_impls/tuple.rs index 2af816a..c05a5f8 100644 --- a/schemars/src/json_schema_impls/tuple.rs +++ b/schemars/src/json_schema_impls/tuple.rs @@ -6,7 +6,7 @@ macro_rules! tuple_impls { ($($len:expr => ($($name:ident)+))+) => { $( impl<$($name: JsonSchema),+> JsonSchema for ($($name,)+) { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { let mut name = "Tuple_of_".to_owned(); diff --git a/schemars/src/json_schema_impls/url2.rs b/schemars/src/json_schema_impls/url2.rs index 04fa1e8..5a467d9 100644 --- a/schemars/src/json_schema_impls/url2.rs +++ b/schemars/src/json_schema_impls/url2.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use url2::Url; impl JsonSchema for Url { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { "Url".into() diff --git a/schemars/src/json_schema_impls/uuid1.rs b/schemars/src/json_schema_impls/uuid1.rs index 297914e..a56c10c 100644 --- a/schemars/src/json_schema_impls/uuid1.rs +++ b/schemars/src/json_schema_impls/uuid1.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use uuid1::Uuid; impl JsonSchema for Uuid { - no_ref_schema!(); + always_inline!(); fn schema_name() -> Cow<'static, str> { "Uuid".into() diff --git a/schemars/src/lib.rs b/schemars/src/lib.rs index 27d06d6..f252324 100644 --- a/schemars/src/lib.rs +++ b/schemars/src/lib.rs @@ -107,14 +107,15 @@ pub use schema::Schema; /// pub trait JsonSchema { - /// Whether JSON Schemas generated for this type should be re-used where possible using the `$ref` keyword. + /// Whether JSON Schemas generated for this type should be included directly in parent schemas, rather than being + /// re-used where possible using the `$ref` keyword. /// - /// For trivial types (such as primitives), this should return `false`. For more complex types, it should return `true`. - /// For recursive types, this **must** return `true` to prevent infinite cycles when generating schemas. + /// For trivial types (such as primitives), this should return `true`. For more complex types, it should return `false`. + /// For recursive types, this **must** return `false` to prevent infinite cycles when generating schemas. /// - /// By default, this returns `true`. - fn is_referenceable() -> bool { - true + /// By default, this returns `false`. + fn always_inline_schema() -> bool { + false } /// The name of the generated JSON Schema. @@ -135,7 +136,7 @@ pub trait JsonSchema { /// Generates a JSON Schema for this type. /// - /// If the returned schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will + /// If the returned schema depends on any [non-inlined](JsonSchema::always_inline_schema) schemas, then this method will /// add them to the [`SchemaGenerator`](gen::SchemaGenerator)'s schema definitions. /// /// This should not return a `$ref` schema. diff --git a/schemars_derive/src/lib.rs b/schemars_derive/src/lib.rs index a4c0742..c4cfaa8 100644 --- a/schemars_derive/src/lib.rs +++ b/schemars_derive/src/lib.rs @@ -56,8 +56,8 @@ fn derive_json_schema(mut input: syn::DeriveInput, repr: bool) -> syn::Result bool { - <#ty as schemars::JsonSchema>::is_referenceable() + fn always_inline_schema() -> bool { + <#ty as schemars::JsonSchema>::always_inline_schema() } fn schema_name() -> std::borrow::Cow<'static, str> { diff --git a/schemars_derive/src/schema_exprs.rs b/schemars_derive/src/schema_exprs.rs index 9fbc9ee..f3f527d 100644 --- a/schemars_derive/src/schema_exprs.rs +++ b/schemars_derive/src/schema_exprs.rs @@ -110,8 +110,8 @@ fn type_for_schema(with_attr: &WithAttr) -> (syn::Type, Option) { struct #ty_name; impl schemars::JsonSchema for #ty_name { - fn is_referenceable() -> bool { - false + fn always_inline_schema() -> bool { + true } fn schema_name() -> std::borrow::Cow<'static, str> { From b955e927ea55301c582251052ac90769c00d0dd4 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Sun, 26 May 2024 21:22:48 +0100 Subject: [PATCH 18/31] Regenerate example schemas --- docs/_includes/examples/custom_serialization.schema.json | 2 +- docs/_includes/examples/doc_comments.schema.json | 6 +++--- docs/_includes/examples/enum_repr.schema.json | 2 +- docs/_includes/examples/from_value.schema.json | 2 +- docs/_includes/examples/main.schema.json | 6 +++--- docs/_includes/examples/remote_derive.schema.json | 8 ++++---- docs/_includes/examples/schemars_attrs.schema.json | 6 +++--- docs/_includes/examples/serde_attrs.schema.json | 6 +++--- docs/_includes/examples/validate.schema.json | 2 +- schemars/examples/custom_serialization.schema.json | 2 +- schemars/examples/doc_comments.schema.json | 6 +++--- schemars/examples/enum_repr.schema.json | 2 +- schemars/examples/from_value.schema.json | 2 +- schemars/examples/main.schema.json | 6 +++--- schemars/examples/remote_derive.schema.json | 8 ++++---- schemars/examples/schemars_attrs.schema.json | 6 +++--- schemars/examples/serde_attrs.schema.json | 6 +++--- schemars/examples/validate.schema.json | 2 +- 18 files changed, 40 insertions(+), 40 deletions(-) diff --git a/docs/_includes/examples/custom_serialization.schema.json b/docs/_includes/examples/custom_serialization.schema.json index 1475c25..1aa0942 100644 --- a/docs/_includes/examples/custom_serialization.schema.json +++ b/docs/_includes/examples/custom_serialization.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", "properties": { diff --git a/docs/_includes/examples/doc_comments.schema.json b/docs/_includes/examples/doc_comments.schema.json index 501b79c..417d3be 100644 --- a/docs/_includes/examples/doc_comments.schema.json +++ b/docs/_includes/examples/doc_comments.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "My Amazing Struct", "description": "This struct shows off generating a schema with a custom title and description.", "type": "object", @@ -18,7 +18,7 @@ "description": "This enum might be set, or it might not.", "anyOf": [ { - "$ref": "#/definitions/MyEnum" + "$ref": "#/$defs/MyEnum" }, { "type": "null" @@ -30,7 +30,7 @@ "my_int", "my_bool" ], - "definitions": { + "$defs": { "MyEnum": { "title": "My Amazing Enum", "oneOf": [ diff --git a/docs/_includes/examples/enum_repr.schema.json b/docs/_includes/examples/enum_repr.schema.json index 04841b7..22d2233 100644 --- a/docs/_includes/examples/enum_repr.schema.json +++ b/docs/_includes/examples/enum_repr.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "SmallPrime", "type": "integer", "enum": [ diff --git a/docs/_includes/examples/from_value.schema.json b/docs/_includes/examples/from_value.schema.json index 237a90e..64bad01 100644 --- a/docs/_includes/examples/from_value.schema.json +++ b/docs/_includes/examples/from_value.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", "properties": { diff --git a/docs/_includes/examples/main.schema.json b/docs/_includes/examples/main.schema.json index bcfe137..5eab909 100644 --- a/docs/_includes/examples/main.schema.json +++ b/docs/_includes/examples/main.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", "properties": { @@ -13,7 +13,7 @@ "my_nullable_enum": { "anyOf": [ { - "$ref": "#/definitions/MyEnum" + "$ref": "#/$defs/MyEnum" }, { "type": "null" @@ -25,7 +25,7 @@ "my_int", "my_bool" ], - "definitions": { + "$defs": { "MyEnum": { "oneOf": [ { diff --git a/docs/_includes/examples/remote_derive.schema.json b/docs/_includes/examples/remote_derive.schema.json index b6315cb..df69751 100644 --- a/docs/_includes/examples/remote_derive.schema.json +++ b/docs/_includes/examples/remote_derive.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Process", "type": "object", "properties": { @@ -9,11 +9,11 @@ "durations": { "type": "array", "items": { - "$ref": "#/definitions/Duration" + "$ref": "#/$defs/Duration" } }, "wall_time": { - "$ref": "#/definitions/Duration" + "$ref": "#/$defs/Duration" } }, "required": [ @@ -21,7 +21,7 @@ "wall_time", "durations" ], - "definitions": { + "$defs": { "Duration": { "type": "object", "properties": { diff --git a/docs/_includes/examples/schemars_attrs.schema.json b/docs/_includes/examples/schemars_attrs.schema.json index 9637e1b..ba96767 100644 --- a/docs/_includes/examples/schemars_attrs.schema.json +++ b/docs/_includes/examples/schemars_attrs.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", "properties": { @@ -9,7 +9,7 @@ "myNullableEnum": { "anyOf": [ { - "$ref": "#/definitions/MyEnum" + "$ref": "#/$defs/MyEnum" }, { "type": "null" @@ -37,7 +37,7 @@ "myBool", "myVecStr" ], - "definitions": { + "$defs": { "MyEnum": { "anyOf": [ { diff --git a/docs/_includes/examples/serde_attrs.schema.json b/docs/_includes/examples/serde_attrs.schema.json index 9bb2f82..8179899 100644 --- a/docs/_includes/examples/serde_attrs.schema.json +++ b/docs/_includes/examples/serde_attrs.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", "properties": { @@ -9,7 +9,7 @@ "myNullableEnum": { "anyOf": [ { - "$ref": "#/definitions/MyEnum" + "$ref": "#/$defs/MyEnum" }, { "type": "null" @@ -27,7 +27,7 @@ "myNumber", "myBool" ], - "definitions": { + "$defs": { "MyEnum": { "anyOf": [ { diff --git a/docs/_includes/examples/validate.schema.json b/docs/_includes/examples/validate.schema.json index 308f631..e9f8a1d 100644 --- a/docs/_includes/examples/validate.schema.json +++ b/docs/_includes/examples/validate.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", "properties": { diff --git a/schemars/examples/custom_serialization.schema.json b/schemars/examples/custom_serialization.schema.json index 1475c25..1aa0942 100644 --- a/schemars/examples/custom_serialization.schema.json +++ b/schemars/examples/custom_serialization.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", "properties": { diff --git a/schemars/examples/doc_comments.schema.json b/schemars/examples/doc_comments.schema.json index 501b79c..417d3be 100644 --- a/schemars/examples/doc_comments.schema.json +++ b/schemars/examples/doc_comments.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "My Amazing Struct", "description": "This struct shows off generating a schema with a custom title and description.", "type": "object", @@ -18,7 +18,7 @@ "description": "This enum might be set, or it might not.", "anyOf": [ { - "$ref": "#/definitions/MyEnum" + "$ref": "#/$defs/MyEnum" }, { "type": "null" @@ -30,7 +30,7 @@ "my_int", "my_bool" ], - "definitions": { + "$defs": { "MyEnum": { "title": "My Amazing Enum", "oneOf": [ diff --git a/schemars/examples/enum_repr.schema.json b/schemars/examples/enum_repr.schema.json index 04841b7..22d2233 100644 --- a/schemars/examples/enum_repr.schema.json +++ b/schemars/examples/enum_repr.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "SmallPrime", "type": "integer", "enum": [ diff --git a/schemars/examples/from_value.schema.json b/schemars/examples/from_value.schema.json index 237a90e..64bad01 100644 --- a/schemars/examples/from_value.schema.json +++ b/schemars/examples/from_value.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", "properties": { diff --git a/schemars/examples/main.schema.json b/schemars/examples/main.schema.json index bcfe137..5eab909 100644 --- a/schemars/examples/main.schema.json +++ b/schemars/examples/main.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", "properties": { @@ -13,7 +13,7 @@ "my_nullable_enum": { "anyOf": [ { - "$ref": "#/definitions/MyEnum" + "$ref": "#/$defs/MyEnum" }, { "type": "null" @@ -25,7 +25,7 @@ "my_int", "my_bool" ], - "definitions": { + "$defs": { "MyEnum": { "oneOf": [ { diff --git a/schemars/examples/remote_derive.schema.json b/schemars/examples/remote_derive.schema.json index b6315cb..df69751 100644 --- a/schemars/examples/remote_derive.schema.json +++ b/schemars/examples/remote_derive.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Process", "type": "object", "properties": { @@ -9,11 +9,11 @@ "durations": { "type": "array", "items": { - "$ref": "#/definitions/Duration" + "$ref": "#/$defs/Duration" } }, "wall_time": { - "$ref": "#/definitions/Duration" + "$ref": "#/$defs/Duration" } }, "required": [ @@ -21,7 +21,7 @@ "wall_time", "durations" ], - "definitions": { + "$defs": { "Duration": { "type": "object", "properties": { diff --git a/schemars/examples/schemars_attrs.schema.json b/schemars/examples/schemars_attrs.schema.json index 9637e1b..ba96767 100644 --- a/schemars/examples/schemars_attrs.schema.json +++ b/schemars/examples/schemars_attrs.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", "properties": { @@ -9,7 +9,7 @@ "myNullableEnum": { "anyOf": [ { - "$ref": "#/definitions/MyEnum" + "$ref": "#/$defs/MyEnum" }, { "type": "null" @@ -37,7 +37,7 @@ "myBool", "myVecStr" ], - "definitions": { + "$defs": { "MyEnum": { "anyOf": [ { diff --git a/schemars/examples/serde_attrs.schema.json b/schemars/examples/serde_attrs.schema.json index 9bb2f82..8179899 100644 --- a/schemars/examples/serde_attrs.schema.json +++ b/schemars/examples/serde_attrs.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", "properties": { @@ -9,7 +9,7 @@ "myNullableEnum": { "anyOf": [ { - "$ref": "#/definitions/MyEnum" + "$ref": "#/$defs/MyEnum" }, { "type": "null" @@ -27,7 +27,7 @@ "myNumber", "myBool" ], - "definitions": { + "$defs": { "MyEnum": { "anyOf": [ { diff --git a/schemars/examples/validate.schema.json b/schemars/examples/validate.schema.json index 308f631..e9f8a1d 100644 --- a/schemars/examples/validate.schema.json +++ b/schemars/examples/validate.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", "properties": { From 70aa0c085a1ec5ba132c885551d2f96f5d059d3b Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Mon, 27 May 2024 11:09:15 +0100 Subject: [PATCH 19/31] Update doc comments and make `SchemaGenerator` available from crate root --- README.md | 38 +++++------ .../examples/custom_serialization.rs | 3 +- schemars/examples/custom_serialization.rs | 3 +- schemars/src/_private.rs | 9 +-- schemars/src/gen.rs | 35 +++++----- schemars/src/lib.rs | 23 ++++--- schemars/src/macros.rs | 33 +++++++--- schemars/src/schema.rs | 65 +++++++++++++++++-- schemars/src/ser.rs | 3 +- schemars/tests/schema_with_enum.rs | 2 +- schemars/tests/schema_with_struct.rs | 2 +- schemars_derive/src/lib.rs | 6 +- schemars_derive/src/schema_exprs.rs | 2 +- 13 files changed, 150 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index 3dc9b73..679aa64 100644 --- a/README.md +++ b/README.md @@ -43,10 +43,9 @@ println!("{}", serde_json::to_string_pretty(&schema).unwrap()); ```json { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", - "required": ["my_bool", "my_int"], "properties": { "my_bool": { "type": "boolean" @@ -58,7 +57,7 @@ println!("{}", serde_json::to_string_pretty(&schema).unwrap()); "my_nullable_enum": { "anyOf": [ { - "$ref": "#/definitions/MyEnum" + "$ref": "#/$defs/MyEnum" }, { "type": "null" @@ -66,26 +65,25 @@ println!("{}", serde_json::to_string_pretty(&schema).unwrap()); ] } }, - "definitions": { + "required": ["my_int", "my_bool"], + "$defs": { "MyEnum": { - "anyOf": [ + "oneOf": [ { "type": "object", - "required": ["StringNewType"], "properties": { "StringNewType": { "type": "string" } }, - "additionalProperties": false + "additionalProperties": false, + "required": ["StringNewType"] }, { "type": "object", - "required": ["StructVariant"], "properties": { "StructVariant": { "type": "object", - "required": ["floats"], "properties": { "floats": { "type": "array", @@ -94,10 +92,12 @@ println!("{}", serde_json::to_string_pretty(&schema).unwrap()); "format": "float" } } - } + }, + "required": ["floats"] } }, - "additionalProperties": false + "additionalProperties": false, + "required": ["StructVariant"] } ] } @@ -142,24 +142,23 @@ println!("{}", serde_json::to_string_pretty(&schema).unwrap()); ```json { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MyStruct", "type": "object", - "required": ["myBool", "myNumber"], "properties": { "myBool": { "type": "boolean" }, "myNullableEnum": { - "default": null, "anyOf": [ { - "$ref": "#/definitions/MyEnum" + "$ref": "#/$defs/MyEnum" }, { "type": "null" } - ] + ], + "default": null }, "myNumber": { "type": "integer", @@ -167,7 +166,8 @@ println!("{}", serde_json::to_string_pretty(&schema).unwrap()); } }, "additionalProperties": false, - "definitions": { + "required": ["myNumber", "myBool"], + "$defs": { "MyEnum": { "anyOf": [ { @@ -175,7 +175,6 @@ println!("{}", serde_json::to_string_pretty(&schema).unwrap()); }, { "type": "object", - "required": ["floats"], "properties": { "floats": { "type": "array", @@ -184,7 +183,8 @@ println!("{}", serde_json::to_string_pretty(&schema).unwrap()); "format": "float" } } - } + }, + "required": ["floats"] } ] } diff --git a/docs/_includes/examples/custom_serialization.rs b/docs/_includes/examples/custom_serialization.rs index c119ea5..c32203f 100644 --- a/docs/_includes/examples/custom_serialization.rs +++ b/docs/_includes/examples/custom_serialization.rs @@ -1,5 +1,4 @@ -use schemars::Schema; -use schemars::{gen::SchemaGenerator, schema_for, JsonSchema}; +use schemars::{schema_for, JsonSchema, Schema, SchemaGenerator}; use serde::{Deserialize, Serialize}; // `int_as_string` and `bool_as_string` use the schema for `String`. diff --git a/schemars/examples/custom_serialization.rs b/schemars/examples/custom_serialization.rs index 7ef6ced..80c81f4 100644 --- a/schemars/examples/custom_serialization.rs +++ b/schemars/examples/custom_serialization.rs @@ -1,6 +1,5 @@ extern crate apistos_schemars as schemars; -use schemars::Schema; -use schemars::{gen::SchemaGenerator, schema_for, JsonSchema}; +use schemars::{schema_for, JsonSchema, Schema, SchemaGenerator}; use serde::{Deserialize, Serialize}; // `int_as_string` and `bool_as_string` use the schema for `String`. diff --git a/schemars/src/_private.rs b/schemars/src/_private.rs index 4be6c27..cdaec3a 100644 --- a/schemars/src/_private.rs +++ b/schemars/src/_private.rs @@ -1,11 +1,6 @@ -use crate::gen::SchemaGenerator; -use crate::JsonSchema; -use crate::Schema; +use crate::{JsonSchema, Schema, SchemaGenerator}; use serde::Serialize; -use serde_json::json; -use serde_json::map::Entry; -use serde_json::Map; -use serde_json::Value; +use serde_json::{json, map::Entry, Map, Value}; // Helper for generating schemas for flattened `Option` fields. pub fn json_schema_for_flatten( diff --git a/schemars/src/gen.rs b/schemars/src/gen.rs index 336a715..b6f12dc 100644 --- a/schemars/src/gen.rs +++ b/schemars/src/gen.rs @@ -12,8 +12,8 @@ use crate::{visit::*, JsonSchema}; use dyn_clone::DynClone; use serde::Serialize; use serde_json::{Map, Value}; -use std::collections::HashMap; -use std::{any::Any, collections::HashSet, fmt::Debug}; +use std::collections::{HashMap, HashSet}; +use std::{any::Any, fmt::Debug}; type CowStr = std::borrow::Cow<'static, str>; @@ -38,7 +38,7 @@ pub struct SchemaSettings { /// /// A single leading `#` and/or single trailing `/` are ignored. /// - /// Defaults to `/$defs`. + /// Defaults to `"/$defs"`. pub definitions_path: String, /// The URI of the meta-schema describing the structure of the generated schemas. /// @@ -55,6 +55,8 @@ pub struct SchemaSettings { } impl Default for SchemaSettings { + /// The default settings currently conform to [JSON Schema 2020-12](https://json-schema.org/specification-links#2020-12), but this is liable to change in a future version of Schemars if support for other JSON Schema versions is added. + /// If you rely on generated schemas conforming to draft 2020-12, consider using the [`SchemaSettings::draft2020_12()`] method. fn default() -> SchemaSettings { SchemaSettings::draft2020_12() } @@ -97,7 +99,7 @@ impl SchemaSettings { } } - /// Creates `SchemaSettings` that conform to [OpenAPI 3.0](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#schema). pub fn openapi3() -> SchemaSettings { SchemaSettings { option_nullable: true, @@ -155,7 +157,7 @@ impl SchemaSettings { /// # Example /// ``` ///# extern crate apistos_schemars as schemars; -/// use schemars::{JsonSchema, gen::SchemaGenerator}; +/// use schemars::{JsonSchema, SchemaGenerator}; /// /// #[derive(JsonSchema)] /// struct MyStruct { @@ -206,7 +208,7 @@ impl SchemaGenerator { /// # Example /// ``` ///# extern crate apistos_schemars as schemars; - /// use schemars::gen::SchemaGenerator; + /// use schemars::SchemaGenerator; /// /// let gen = SchemaGenerator::default(); /// let settings = gen.settings(); @@ -290,7 +292,7 @@ impl SchemaGenerator { } /// Returns the collection of all [non-inlined](JsonSchema::always_inline_schema) schemas that have been generated, - /// leaving an empty map in its place. + /// leaving an empty `Map` in its place. /// /// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the values are the schemas /// themselves. @@ -303,11 +305,10 @@ impl SchemaGenerator { self.settings.visitors.iter_mut().map(|v| v.as_mut()) } - /// Generates a root JSON Schema for the type `T`. + /// Generates a JSON Schema for the type `T`. /// /// If `T`'s schema depends on any [non-inlined](JsonSchema::always_inline_schema) 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) + /// include them in the returned `Schema` at the [definitions path](SchemaSettings::definitions_path) (by default `"$defs"`). pub fn root_schema_for(&mut self) -> Schema { let mut schema = self.json_schema_internal::(T::schema_id()); @@ -327,10 +328,10 @@ impl SchemaGenerator { schema } - /// Consumes `self` and generates a root JSON Schema for the type `T`. + /// Consumes `self` and generates a JSON Schema for the type `T`. /// /// If `T`'s schema depends on any [non-inlined](JsonSchema::always_inline_schema) schemas, then this method will - /// include them in the returned `SchemaObject`'s [`definitions`](../schema/struct.Metadata.html#structfield.definitions) + /// include them in the returned `Schema` at the [definitions path](SchemaSettings::definitions_path) (by default `"$defs"`). pub fn into_root_schema_for(mut self) -> Schema { let mut schema = self.json_schema_internal::(T::schema_id()); @@ -351,10 +352,12 @@ impl SchemaGenerator { schema } - /// Generates a root JSON Schema for the given example value. + /// Generates a JSON Schema for the given example value. /// /// If the value implements [`JsonSchema`](crate::JsonSchema), then prefer using the [`root_schema_for()`](Self::root_schema_for()) /// function which will generally produce a more precise schema, particularly when the value contains any enums. + /// + /// If the `Serialize` implementation of the value decides to fail, this will return an [`Err`]. pub fn root_schema_for_value( &mut self, value: &T, @@ -380,10 +383,12 @@ impl SchemaGenerator { Ok(schema) } - /// Consumes `self` and generates a root JSON Schema for the given example value. + /// Consumes `self` and generates a JSON Schema for the given example value. /// /// If the value implements [`JsonSchema`](crate::JsonSchema), then prefer using the [`into_root_schema_for()!`](Self::into_root_schema_for()) /// function which will generally produce a more precise schema, particularly when the value contains any enums. + /// + /// If the `Serialize` implementation of the value decides to fail, this will return an [`Err`]. pub fn into_root_schema_for_value( mut self, value: &T, @@ -516,7 +521,7 @@ fn json_pointer_mut<'a>( Some(object) } -/// A [Visitor](Visitor) which implements additional traits required to be included in a [SchemaSettings]. +/// A [Visitor] which implements additional traits required to be included in a [SchemaSettings]. /// /// You will rarely need to use this trait directly as it is automatically implemented for any type which implements all of: /// - [`Visitor`] diff --git a/schemars/src/lib.rs b/schemars/src/lib.rs index f252324..5ab96e1 100644 --- a/schemars/src/lib.rs +++ b/schemars/src/lib.rs @@ -11,7 +11,9 @@ mod macros; /// outside of `schemars`, and should not be considered part of the public API. #[doc(hidden)] pub mod _private; +/// Types for generating JSON schemas. pub mod gen; +/// Types for recursively modifying JSON schemas. pub mod visit; #[cfg(feature = "schemars_derive")] @@ -25,6 +27,7 @@ pub use schemars_derive::*; #[doc(hidden)] pub use serde_json as _serde_json; +pub use gen::SchemaGenerator; pub use schema::Schema; /// A type which can be described as a JSON Schema document. @@ -52,7 +55,7 @@ pub use schema::Schema; /// For non-generic types, the type name/path are suitable for this: /// ``` ///# extern crate apistos_schemars as schemars; -/// use schemars::{gen::SchemaGenerator, Schema, JsonSchema}; +/// use schemars::{SchemaGenerator, Schema, JsonSchema, json_schema}; /// use std::borrow::Cow; /// /// struct NonGenericType; @@ -69,7 +72,9 @@ pub use schema::Schema; /// } /// /// fn json_schema(_gen: &mut SchemaGenerator) -> Schema { -/// todo!() +/// json_schema!({ +/// "foo": "bar" +/// }) /// } /// } /// @@ -79,7 +84,7 @@ pub use schema::Schema; /// But generic type parameters which may affect the generated schema should typically be included in the name/ID: /// ``` ///# extern crate apistos_schemars as schemars; -/// use schemars::{gen::SchemaGenerator, Schema, JsonSchema}; +/// use schemars::{SchemaGenerator, Schema, JsonSchema, json_schema}; /// use std::{borrow::Cow, marker::PhantomData}; /// /// struct GenericType(PhantomData); @@ -98,7 +103,9 @@ pub use schema::Schema; /// } /// /// fn json_schema(_gen: &mut SchemaGenerator) -> Schema { -/// todo!() +/// json_schema!({ +/// "foo": "bar" +/// }) /// } /// } /// @@ -129,7 +136,7 @@ pub trait JsonSchema { /// If two types produce different schemas, then they **must** have different `schema_id()`s, /// but two types that produce identical schemas should *ideally* have the same `schema_id()`. /// - /// The default implementation returns the same value as `schema_name()`. + /// The default implementation returns the same value as [`schema_name()`](JsonSchema::schema_name). fn schema_id() -> Cow<'static, str> { Self::schema_name() } @@ -137,14 +144,14 @@ pub trait JsonSchema { /// Generates a JSON Schema for this type. /// /// If the returned schema depends on any [non-inlined](JsonSchema::always_inline_schema) schemas, then this method will - /// add them to the [`SchemaGenerator`](gen::SchemaGenerator)'s schema definitions. + /// add them to the [`SchemaGenerator`](SchemaGenerator)'s schema definitions. /// /// This should not return a `$ref` schema. - fn json_schema(gen: &mut gen::SchemaGenerator) -> Schema; + fn json_schema(gen: &mut SchemaGenerator) -> Schema; // TODO document and bring into public API? #[doc(hidden)] - fn _schemars_private_non_optional_json_schema(gen: &mut gen::SchemaGenerator) -> Schema { + fn _schemars_private_non_optional_json_schema(gen: &mut SchemaGenerator) -> Schema { Self::json_schema(gen) } diff --git a/schemars/src/macros.rs b/schemars/src/macros.rs index bae7ce0..aea504d 100644 --- a/schemars/src/macros.rs +++ b/schemars/src/macros.rs @@ -18,11 +18,12 @@ #[macro_export] macro_rules! schema_for { ($type:ty) => { - $crate::gen::SchemaGenerator::default().into_root_schema_for::<$type>() + $crate::SchemaGenerator::default().into_root_schema_for::<$type>() }; } -/// Generates a [`RootSchema`](crate::schema::RootSchema) for the given type using default settings. +/// Generates a [`Schema`](crate::Schema) for the given type using default settings. +/// The default settings currently conform to [JSON Schema 2020-12](https://json-schema.org/specification-links#2020-12), but this is liable to change in a future version of Schemars if support for other JSON Schema versions is added. /// /// The type must implement [`JsonSchema`](crate::JsonSchema). /// @@ -42,22 +43,23 @@ macro_rules! schema_for { #[macro_export] macro_rules! schema_for { ($type:ty) => { - $crate::gen::SchemaGenerator::default().into_root_schema_for::<$type>() + $crate::SchemaGenerator::default().into_root_schema_for::<$type>() }; ($_:expr) => { compile_error!("This argument to `schema_for!` is not a type - did you mean to use `schema_for_value!` instead?") }; } -/// Generates a [`RootSchema`](crate::schema::RootSchema) for the given example value using default settings. +/// Generates a [`Schema`](crate::Schema) for the given example value using default settings. +/// The default settings currently conform to [JSON Schema 2020-12](https://json-schema.org/specification-links#2020-12), but this is liable to change in a future version of Schemars if support for other JSON Schema versions is added. /// /// The value must implement [`Serialize`](serde::Serialize). If the value also implements [`JsonSchema`](crate::JsonSchema), -/// then prefer using the [`schema_for!`](schema_for) macro which will generally produce a more precise schema, +/// then prefer using the [`schema_for!(Type)`](schema_for) macro which will generally produce a more precise schema, /// particularly when the value contains any enums. /// /// If the `Serialize` implementation of the value decides to fail, this macro will panic. -/// For a non-panicking alternative, create a [`SchemaGenerator`](crate::gen::SchemaGenerator) and use -/// its [`into_root_schema_for_value`](crate::gen::SchemaGenerator::into_root_schema_for_value) method. +/// For a non-panicking alternative, create a [`SchemaGenerator`](crate::SchemaGenerator) and use +/// its [`into_root_schema_for_value`](crate::SchemaGenerator::into_root_schema_for_value) method. /// /// # Example /// ``` @@ -74,13 +76,26 @@ macro_rules! schema_for { #[macro_export] macro_rules! schema_for_value { ($value:expr) => { - $crate::gen::SchemaGenerator::default() + $crate::SchemaGenerator::default() .into_root_schema_for_value(&$value) .unwrap() }; } -// TODO doc +/// Construct a [`Schema`](crate::Schema) from a JSON literal. This can either be a JSON object, or a boolean (`true` or `false`). +/// +/// You can interpolate variables or expressions into a JSON object using the same rules as the [`serde_json::json`] macro. +/// +/// # Example +/// ``` +/// use schemars::{Schema, json_schema}; +/// +/// let desc = "A helpful description."; +/// let my_schema: Schema = json_schema!({ +/// "description": desc, +/// "type": ["object", "null"] +/// }); +/// ``` #[macro_export] macro_rules! json_schema { ( diff --git a/schemars/src/schema.rs b/schemars/src/schema.rs index 91a3547..dd9e866 100644 --- a/schemars/src/schema.rs +++ b/schemars/src/schema.rs @@ -7,6 +7,54 @@ use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; /// A JSON Schema. +/// +/// This wraps a JSON [`Value`] that must be either an [object](Value::Object) or a [bool](Value::Bool). +/// +/// A custom JSON schema can be created using the [`json_schema!`](crate::json_schema) macro: +/// ``` +/// use schemars::{Schema, json_schema}; +/// +/// let my_schema: Schema = json_schema!({ +/// "type": ["object", "null"] +/// }); +/// ``` +/// +/// Because a `Schema` is a thin wrapper around a `Value`, you can also use [`TryFrom::try_from`]/[`TryInto::try_into`] to create a `Schema` from an existing `Value`. +/// This operation is fallible, because only [objects](Value::Object) and [bools](Value::Bool) can be converted in this way. +/// ``` +/// use schemars::{Schema, json_schema}; +/// use serde_json::json; +/// +/// let json_object = json!({"type": ["object", "null"]}); +/// let object_schema: Schema = json_object.try_into().unwrap(); +/// +/// let json_bool = json!(true); +/// let bool_schema: Schema = json_bool.try_into().unwrap(); +/// +/// let json_string = json!("This is neither an object nor a bool!"); +/// assert!(Schema::try_from(json_string).is_err()); +/// +/// // You can also convert a `&Value`/`&mut Value` to a `&Schema`/`&mut Schema` the same way: +/// +/// let json_object = json!({"type": ["object", "null"]}); +/// let object_schema_ref: &Schema = (&json_object).try_into().unwrap(); +/// +/// let mut json_object = json!({"type": ["object", "null"]}); +/// let object_schema_mut: &mut Schema = (&mut json_object).try_into().unwrap(); +/// +/// ``` +/// +/// Similarly, you can use [`From`]/[`Into`] to (infallibly) create a `Schema` from an existing [`Map`] or [`bool`]. +/// ``` +/// use schemars::{Schema, json_schema}; +/// use serde_json::{Map, json}; +/// +/// let mut map = Map::new(); +/// map.insert("type".to_owned(), json!(["object", "null"])); +/// let object_schema: Schema = map.into(); +/// +/// let bool_schema: Schema = true.into(); +/// ``` #[derive(Debug, Clone, PartialEq, RefCastCustom)] #[repr(transparent)] pub struct Schema(Value); @@ -32,28 +80,32 @@ impl Serialize for Schema { } impl Schema { - pub fn new() -> Self { - Self(Value::Object(Map::new())) - } - + /// Creates a new schema object with a single string property `"$ref"`. + /// + /// The given reference string should be a URI reference. This will usually be a JSON Pointer + /// in [URI Fragment representation](https://tools.ietf.org/html/rfc6901#section-6). pub fn new_ref(reference: String) -> Self { let mut map = Map::new(); map.insert("$ref".to_owned(), Value::String(reference)); Self(Value::Object(map)) } + /// Borrows the `Schema`'s underlying JSON value. pub fn as_value(&self) -> &Value { &self.0 } + /// If the `Schema`'s underlying JSON value is a bool, returns the bool value. pub fn as_bool(&self) -> Option { self.0.as_bool() } + /// If the `Schema`'s underlying JSON value is an object, borrows the object as a `Map` of properties. pub fn as_object(&self) -> Option<&Map> { self.0.as_object() } + /// If the `Schema`'s underlying JSON value is an object, mutably borrows the object as a `Map` of properties. pub fn as_object_mut(&mut self) -> Option<&mut Map> { self.0.as_object_mut() } @@ -66,10 +118,15 @@ impl Schema { } } + /// Returns the `Schema`'s underlying JSON value. pub fn to_value(self) -> Value { self.0 } + /// Converts the `Schema` (if it wraps a bool value) into an equivalent object schema. Then mutably borrows the object as a `Map` of properties. + /// + /// `true` is transformed into an empty schema `{}`, which successfully validates against all possible values. + /// `false` is transformed into the schema `{"not": {}}`, which does not successfully validate against any value. pub fn ensure_object(&mut self) -> &mut Map { if let Some(b) = self.as_bool() { let mut map = Map::new(); diff --git a/schemars/src/ser.rs b/schemars/src/ser.rs index 75ae6ca..f42c187 100644 --- a/schemars/src/ser.rs +++ b/schemars/src/ser.rs @@ -1,5 +1,4 @@ -use crate::gen::SchemaGenerator; -use crate::{json_schema, JsonSchema, Schema}; +use crate::{json_schema, JsonSchema, Schema, SchemaGenerator}; use serde_json::{Error, Map, Value}; use std::fmt::Display; diff --git a/schemars/tests/schema_with_enum.rs b/schemars/tests/schema_with_enum.rs index 7a6fc65..9953ae7 100644 --- a/schemars/tests/schema_with_enum.rs +++ b/schemars/tests/schema_with_enum.rs @@ -3,7 +3,7 @@ extern crate apistos_schemars as schemars; use schemars::JsonSchema; use util::*; -fn schema_fn(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema { +fn schema_fn(gen: &mut schemars::SchemaGenerator) -> schemars::Schema { ::json_schema(gen) } diff --git a/schemars/tests/schema_with_struct.rs b/schemars/tests/schema_with_struct.rs index 19b6e67..1d47842 100644 --- a/schemars/tests/schema_with_struct.rs +++ b/schemars/tests/schema_with_struct.rs @@ -3,7 +3,7 @@ extern crate apistos_schemars as schemars; use schemars::JsonSchema; use util::*; -fn schema_fn(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema { +fn schema_fn(gen: &mut schemars::SchemaGenerator) -> schemars::Schema { ::json_schema(gen) } diff --git a/schemars_derive/src/lib.rs b/schemars_derive/src/lib.rs index c4cfaa8..bd427bd 100644 --- a/schemars_derive/src/lib.rs +++ b/schemars_derive/src/lib.rs @@ -68,11 +68,11 @@ fn derive_json_schema(mut input: syn::DeriveInput, repr: bool) -> syn::Result::schema_id() } - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema { + fn json_schema(gen: &mut schemars::SchemaGenerator) -> schemars::Schema { <#ty as schemars::JsonSchema>::json_schema(gen) } - fn _schemars_private_non_optional_json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema { + fn _schemars_private_non_optional_json_schema(gen: &mut schemars::SchemaGenerator) -> schemars::Schema { <#ty as schemars::JsonSchema>::_schemars_private_non_optional_json_schema(gen) } @@ -186,7 +186,7 @@ fn derive_json_schema(mut input: syn::DeriveInput, repr: bool) -> syn::Result schemars::Schema { + fn json_schema(gen: &mut schemars::SchemaGenerator) -> schemars::Schema { #schema_expr } }; diff --git a/schemars_derive/src/schema_exprs.rs b/schemars_derive/src/schema_exprs.rs index f3f527d..48da5ef 100644 --- a/schemars_derive/src/schema_exprs.rs +++ b/schemars_derive/src/schema_exprs.rs @@ -127,7 +127,7 @@ fn type_for_schema(with_attr: &WithAttr) -> (syn::Type, Option) { )) } - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema { + fn json_schema(gen: &mut schemars::SchemaGenerator) -> schemars::Schema { #fun(gen) } } From dce7142064dbe0b9c7b967404b39ad9d0e98e185 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Mon, 27 May 2024 11:14:43 +0100 Subject: [PATCH 20/31] v1.0.0-alpha.1 --- schemars/Cargo.toml | 4 ++-- schemars_derive/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/schemars/Cargo.toml b/schemars/Cargo.toml index 40b0a60..8054b45 100644 --- a/schemars/Cargo.toml +++ b/schemars/Cargo.toml @@ -2,7 +2,7 @@ name = "apistos-schemars" description = "Generate JSON Schemas from Rust code" repository = "https://github.com/netwo-io/apistos-schemars" -version = "0.8.21" +version = "1.0.0-alpha.1" edition = "2021" license = "MIT" readme = "README.md" @@ -12,7 +12,7 @@ build = "build.rs" rust-version = "1.60" [dependencies] -schemars_derive = { package = "apistos-schemars_derive", version = "=0.8.21", optional = true, path = "../schemars_derive" } +schemars_derive = { package = "apistos-schemars_derive", version = "=1.0.0-alpha.1", optional = true, path = "../schemars_derive" } serde = "1.0" serde_json = "1.0.25" dyn-clone = "1.0" diff --git a/schemars_derive/Cargo.toml b/schemars_derive/Cargo.toml index 5b34406..3d957c3 100644 --- a/schemars_derive/Cargo.toml +++ b/schemars_derive/Cargo.toml @@ -2,7 +2,7 @@ name = "apistos-schemars_derive" description = "Macros for #[derive(JsonSchema)], for use with schemars" repository = "https://github.com/netwo-io/apistos-schemars" -version = "0.8.21" +version = "1.0.0-alpha.1" edition = "2021" license = "MIT" readme = "README.md" From db8fda064b7f6106a4ae8c1fedc60ae64c2e3b30 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Mon, 27 May 2024 14:25:45 +0100 Subject: [PATCH 21/31] Update readme for v1 --- README.md | 32 +++++++++++++------------------- schemars/Cargo.toml | 18 +++++++++--------- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 679aa64..de7488c 100644 --- a/README.md +++ b/README.md @@ -260,34 +260,28 @@ println!("{}", serde_json::to_string_pretty(&schema).unwrap()); ## Feature Flags - `derive` (enabled by default) - provides `#[derive(JsonSchema)]` macro -- `impl_json_schema` - implements `JsonSchema` for Schemars types themselves -- `preserve_order` - keep the order of struct fields in `Schema` and `SchemaObject` +- `preserve_order` - keep the order of struct fields in `Schema` properties - `raw_value` - implements `JsonSchema` for `serde_json::value::RawValue` (enables the serde_json `raw_value` feature) Schemars can implement `JsonSchema` on types from several popular crates, enabled via feature flags (dependency versions are shown in brackets): -- `chrono` - [chrono](https://crates.io/crates/chrono) (^0.4) -- `indexmap1` - [indexmap](https://crates.io/crates/indexmap) (^1.2) -- `indexmap2` - [indexmap](https://crates.io/crates/indexmap) (^2.0) -- `ipnetwork` - [ipnetwork](https://crates.io/crates/ipnetwork) (^0.20) -- `either` - [either](https://crates.io/crates/either) (^1.3) -- `uuid08` - [uuid](https://crates.io/crates/uuid) (^0.8) -- `uuid1` - [uuid](https://crates.io/crates/uuid) (^1.0) -- `smallvec` - [smallvec](https://crates.io/crates/smallvec) (^1.0) -- `arrayvec05` - [arrayvec](https://crates.io/crates/arrayvec) (^0.5) - `arrayvec07` - [arrayvec](https://crates.io/crates/arrayvec) (^0.7) -- `url` - [url](https://crates.io/crates/url) (^2.0) -- `bytes` - [bytes](https://crates.io/crates/bytes) (^1.0) -- `enumset` - [enumset](https://crates.io/crates/enumset) (^1.0) -- `rust_decimal` - [rust_decimal](https://crates.io/crates/rust_decimal) (^1.0) -- `bigdecimal03` - [bigdecimal](https://crates.io/crates/bigdecimal) (^0.3) - `bigdecimal04` - [bigdecimal](https://crates.io/crates/bigdecimal) (^0.4) -- `smol_str` - [smol_str](https://crates.io/crates/smol_str) (^0.1.17) -- `semver` - [semver](https://crates.io/crates/semver) (^1.0.9) +- `bytes1` - [bytes](https://crates.io/crates/bytes) (^1.0) +- `chrono04` - [chrono](https://crates.io/crates/chrono) (^0.4) +- `either1` - [either](https://crates.io/crates/either) (^1.3) +- `enumset1` - [enumset](https://crates.io/crates/enumset) (^1.0) +- `indexmap2` - [indexmap](https://crates.io/crates/indexmap) (^2.0) +- `rust_decimal1` - [rust_decimal](https://crates.io/crates/rust_decimal) (^1.0) +- `semver1` - [semver](https://crates.io/crates/semver) (^1.0.9) +- `smallvec1` - [smallvec](https://crates.io/crates/smallvec) (^1.0) +- `smol_str02` - [smol_str](https://crates.io/crates/smol_str) (^0.2.1) +- `url2` - [url](https://crates.io/crates/url) (^2.0) +- `uuid1` - [uuid](https://crates.io/crates/uuid) (^1.0) For example, to implement `JsonSchema` on types from `chrono`, enable it as a feature in the `schemars` dependency in your `Cargo.toml` like so: ```toml [dependencies] -schemars = { version = "0.8", features = ["chrono"] } +schemars = { version = "1.0.0-alpha.1", features = ["chrono04"] } ``` diff --git a/schemars/Cargo.toml b/schemars/Cargo.toml index 8054b45..734d3df 100644 --- a/schemars/Cargo.toml +++ b/schemars/Cargo.toml @@ -19,19 +19,19 @@ dyn-clone = "1.0" ref-cast = "1.0.22" # optional dependencies -chrono04 = { version = "0.4", default-features = false, optional = true, package = "chrono" } -indexmap2 = { version = "2.0", default-features = false, optional = true, package = "indexmap" } -either1 = { version = "1.3", default-features = false, optional = true, package = "either" } -uuid1 = { version = "1.0", default-features = false, optional = true, package = "uuid" } -smallvec1 = { version = "1.0", default-features = false, optional = true, package = "smallvec" } arrayvec07 = { version = "0.7", default-features = false, optional = true, package = "arrayvec" } -url2 = { version = "2.0", default-features = false, optional = true, package = "url" } -bytes1 = { version = "1.0", default-features = false, optional = true, package = "bytes" } -rust_decimal1 = { version = "1", default-features = false, optional = true, package = "rust_decimal"} bigdecimal04 = { version = "0.4", default-features = false, optional = true, package = "bigdecimal" } +bytes1 = { version = "1.0", default-features = false, optional = true, package = "bytes" } +chrono04 = { version = "0.4", default-features = false, optional = true, package = "chrono" } +either1 = { version = "1.3", default-features = false, optional = true, package = "either" } enumset1 = { version = "1.0", default-features = false, optional = true, package = "enumset" } -smol_str02 = { version = "0.2.1", default-features = false, optional = true, package = "smol_str" } +indexmap2 = { version = "2.0", default-features = false, optional = true, package = "indexmap" } +rust_decimal1 = { version = "1", default-features = false, optional = true, package = "rust_decimal"} semver1 = { version = "1.0.9", default-features = false, optional = true, package = "semver" } +smallvec1 = { version = "1.0", default-features = false, optional = true, package = "smallvec" } +smol_str02 = { version = "0.2.1", default-features = false, optional = true, package = "smol_str" } +url2 = { version = "2.0", default-features = false, optional = true, package = "url" } +uuid1 = { version = "1.0", default-features = false, optional = true, package = "uuid" } [dev-dependencies] pretty_assertions = "1.2.1" From 10eacde0ca9fadd1d8089bb5d28df60622cc6d37 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Wed, 5 Jun 2024 21:09:52 +0100 Subject: [PATCH 22/31] Add `#[schemars(extend("key" = value))]` attribute (#297) --- schemars/tests/expected/default.json | 4 +- .../tests/expected/extend_enum_adjacent.json | 101 ++++++++++++++++++ .../tests/expected/extend_enum_external.json | 73 +++++++++++++ .../tests/expected/extend_enum_internal.json | 55 ++++++++++ .../tests/expected/extend_enum_untagged.json | 46 ++++++++ schemars/tests/expected/extend_struct.json | 27 +++++ .../tests/expected/from_value_2019_09.json | 6 +- .../tests/expected/from_value_draft07.json | 6 +- .../tests/expected/from_value_openapi3.json | 6 +- .../expected/schema_settings-2019_09.json | 6 +- .../expected/schema_settings-openapi3.json | 6 +- schemars/tests/expected/schema_settings.json | 6 +- schemars/tests/extend.rs | 96 +++++++++++++++++ schemars/tests/ui/invalid_extend.rs | 11 ++ schemars/tests/ui/invalid_extend.stderr | 35 ++++++ schemars_derive/src/attr/mod.rs | 54 +++++++++- schemars_derive/src/metadata.rs | 7 ++ schemars_derive/src/schema_exprs.rs | 8 +- 18 files changed, 527 insertions(+), 26 deletions(-) create mode 100644 schemars/tests/expected/extend_enum_adjacent.json create mode 100644 schemars/tests/expected/extend_enum_external.json create mode 100644 schemars/tests/expected/extend_enum_internal.json create mode 100644 schemars/tests/expected/extend_enum_untagged.json create mode 100644 schemars/tests/expected/extend_struct.json create mode 100644 schemars/tests/extend.rs create mode 100644 schemars/tests/ui/invalid_extend.rs create mode 100644 schemars/tests/ui/invalid_extend.stderr diff --git a/schemars/tests/expected/default.json b/schemars/tests/expected/default.json index 69f1fec..73640a3 100644 --- a/schemars/tests/expected/default.json +++ b/schemars/tests/expected/default.json @@ -9,11 +9,11 @@ "default": 0 }, "my_optional_string": { - "default": null, "type": [ "string", "null" - ] + ], + "default": null }, "my_struct2": { "$ref": "#/$defs/MyStruct2", diff --git a/schemars/tests/expected/extend_enum_adjacent.json b/schemars/tests/expected/extend_enum_adjacent.json new file mode 100644 index 0000000..6241e07 --- /dev/null +++ b/schemars/tests/expected/extend_enum_adjacent.json @@ -0,0 +1,101 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Adjacent", + "oneOf": [ + { + "type": "object", + "properties": { + "t": { + "type": "string", + "enum": [ + "Unit" + ] + } + }, + "required": [ + "t" + ], + "foo": "bar" + }, + { + "type": "object", + "properties": { + "t": { + "type": "string", + "enum": [ + "NewType" + ] + }, + "c": true + }, + "required": [ + "t", + "c" + ], + "foo": "bar" + }, + { + "type": "object", + "properties": { + "t": { + "type": "string", + "enum": [ + "Tuple" + ] + }, + "c": { + "type": "array", + "prefixItems": [ + { + "type": "integer", + "format": "int32" + }, + { + "type": "boolean" + } + ], + "minItems": 2, + "maxItems": 2 + } + }, + "required": [ + "t", + "c" + ], + "foo": "bar" + }, + { + "type": "object", + "properties": { + "t": { + "type": "string", + "enum": [ + "Struct" + ] + }, + "c": { + "type": "object", + "properties": { + "i": { + "type": "integer", + "format": "int32" + }, + "b": { + "type": "boolean" + } + }, + "required": [ + "i", + "b" + ] + } + }, + "required": [ + "t", + "c" + ], + "foo": "bar" + } + ], + "foo": "bar" +} \ No newline at end of file diff --git a/schemars/tests/expected/extend_enum_external.json b/schemars/tests/expected/extend_enum_external.json new file mode 100644 index 0000000..c15d47f --- /dev/null +++ b/schemars/tests/expected/extend_enum_external.json @@ -0,0 +1,73 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "External", + "oneOf": [ + { + "type": "string", + "const": "Unit", + "foo": "bar" + }, + { + "type": "object", + "properties": { + "NewType": true + }, + "required": [ + "NewType" + ], + "additionalProperties": false, + "foo": "bar" + }, + { + "type": "object", + "properties": { + "Tuple": { + "type": "array", + "prefixItems": [ + { + "type": "integer", + "format": "int32" + }, + { + "type": "boolean" + } + ], + "minItems": 2, + "maxItems": 2 + } + }, + "required": [ + "Tuple" + ], + "additionalProperties": false, + "foo": "bar" + }, + { + "type": "object", + "properties": { + "Struct": { + "type": "object", + "properties": { + "i": { + "type": "integer", + "format": "int32" + }, + "b": { + "type": "boolean" + } + }, + "required": [ + "i", + "b" + ] + } + }, + "required": [ + "Struct" + ], + "additionalProperties": false, + "foo": "bar" + } + ], + "foo": "bar" +} \ No newline at end of file diff --git a/schemars/tests/expected/extend_enum_internal.json b/schemars/tests/expected/extend_enum_internal.json new file mode 100644 index 0000000..0dee817 --- /dev/null +++ b/schemars/tests/expected/extend_enum_internal.json @@ -0,0 +1,55 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Internal", + "oneOf": [ + { + "type": "object", + "properties": { + "typeProperty": { + "type": "string", + "const": "Unit" + } + }, + "required": [ + "typeProperty" + ], + "foo": "bar" + }, + { + "type": "object", + "properties": { + "typeProperty": { + "type": "string", + "const": "NewType" + } + }, + "required": [ + "typeProperty" + ], + "foo": "bar" + }, + { + "type": "object", + "properties": { + "i": { + "type": "integer", + "format": "int32" + }, + "b": { + "type": "boolean" + }, + "typeProperty": { + "type": "string", + "const": "Struct" + } + }, + "required": [ + "typeProperty", + "i", + "b" + ], + "foo": "bar" + } + ], + "foo": "bar" +} \ No newline at end of file diff --git a/schemars/tests/expected/extend_enum_untagged.json b/schemars/tests/expected/extend_enum_untagged.json new file mode 100644 index 0000000..4f733fe --- /dev/null +++ b/schemars/tests/expected/extend_enum_untagged.json @@ -0,0 +1,46 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Untagged", + "anyOf": [ + { + "type": "null", + "foo": "bar" + }, + { + "foo": "bar" + }, + { + "type": "array", + "prefixItems": [ + { + "type": "integer", + "format": "int32" + }, + { + "type": "boolean" + } + ], + "minItems": 2, + "maxItems": 2, + "foo": "bar" + }, + { + "type": "object", + "properties": { + "i": { + "type": "integer", + "format": "int32" + }, + "b": { + "type": "boolean" + } + }, + "required": [ + "i", + "b" + ], + "foo": "bar" + } + ], + "foo": "bar" +} \ No newline at end of file diff --git a/schemars/tests/expected/extend_struct.json b/schemars/tests/expected/extend_struct.json new file mode 100644 index 0000000..fc7dd50 --- /dev/null +++ b/schemars/tests/expected/extend_struct.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Struct", + "type": "object", + "properties": { + "value": { + "foo": "bar" + }, + "int": { + "type": "overridden", + "format": "int32" + } + }, + "required": [ + "value", + "int" + ], + "msg": "hello world", + "obj": { + "array": [ + null, + null + ] + }, + "3": 3.0, + "pi": 3.14 +} \ No newline at end of file diff --git a/schemars/tests/expected/from_value_2019_09.json b/schemars/tests/expected/from_value_2019_09.json index 52c0524..939410d 100644 --- a/schemars/tests/expected/from_value_2019_09.json +++ b/schemars/tests/expected/from_value_2019_09.json @@ -35,6 +35,8 @@ }, "my_tuple": { "type": "array", + "minItems": 2, + "maxItems": 2, "items": [ { "type": "string", @@ -44,9 +46,7 @@ { "type": "integer" } - ], - "maxItems": 2, - "minItems": 2 + ] } } } diff --git a/schemars/tests/expected/from_value_draft07.json b/schemars/tests/expected/from_value_draft07.json index de89fad..721e871 100644 --- a/schemars/tests/expected/from_value_draft07.json +++ b/schemars/tests/expected/from_value_draft07.json @@ -35,6 +35,8 @@ }, "my_tuple": { "type": "array", + "minItems": 2, + "maxItems": 2, "items": [ { "type": "string", @@ -44,9 +46,7 @@ { "type": "integer" } - ], - "maxItems": 2, - "minItems": 2 + ] } } } diff --git a/schemars/tests/expected/from_value_openapi3.json b/schemars/tests/expected/from_value_openapi3.json index 4e9dd2c..88f08a7 100644 --- a/schemars/tests/expected/from_value_openapi3.json +++ b/schemars/tests/expected/from_value_openapi3.json @@ -37,6 +37,8 @@ }, "my_tuple": { "type": "array", + "minItems": 2, + "maxItems": 2, "items": [ { "type": "string", @@ -46,9 +48,7 @@ { "type": "integer" } - ], - "maxItems": 2, - "minItems": 2 + ] } } } diff --git a/schemars/tests/expected/schema_settings-2019_09.json b/schemars/tests/expected/schema_settings-2019_09.json index 6b6dc61..bc99f15 100644 --- a/schemars/tests/expected/schema_settings-2019_09.json +++ b/schemars/tests/expected/schema_settings-2019_09.json @@ -30,6 +30,8 @@ "type": "array", "items": { "type": "array", + "maxItems": 2, + "minItems": 2, "items": [ { "type": "integer", @@ -40,9 +42,7 @@ "type": "integer", "format": "int64" } - ], - "minItems": 2, - "maxItems": 2 + ] } } }, diff --git a/schemars/tests/expected/schema_settings-openapi3.json b/schemars/tests/expected/schema_settings-openapi3.json index e5032f0..8365822 100644 --- a/schemars/tests/expected/schema_settings-openapi3.json +++ b/schemars/tests/expected/schema_settings-openapi3.json @@ -25,6 +25,8 @@ "type": "array", "items": { "type": "array", + "maxItems": 2, + "minItems": 2, "items": [ { "type": "integer", @@ -35,9 +37,7 @@ "type": "integer", "format": "int64" } - ], - "minItems": 2, - "maxItems": 2 + ] } } }, diff --git a/schemars/tests/expected/schema_settings.json b/schemars/tests/expected/schema_settings.json index a836cd6..4c435ad 100644 --- a/schemars/tests/expected/schema_settings.json +++ b/schemars/tests/expected/schema_settings.json @@ -30,6 +30,8 @@ "type": "array", "items": { "type": "array", + "maxItems": 2, + "minItems": 2, "items": [ { "type": "integer", @@ -40,9 +42,7 @@ "type": "integer", "format": "int64" } - ], - "minItems": 2, - "maxItems": 2 + ] } } }, diff --git a/schemars/tests/extend.rs b/schemars/tests/extend.rs new file mode 100644 index 0000000..08f42fa --- /dev/null +++ b/schemars/tests/extend.rs @@ -0,0 +1,96 @@ +mod util; +use schemars::JsonSchema; +use serde_json::Value; +use util::*; + +const THREE: f64 = 3.0; + +#[allow(dead_code)] +#[derive(JsonSchema)] +#[schemars(extend("msg" = concat!("hello ", "world"), "obj" = {"array": [null, ()]}))] +#[schemars(extend("3" = THREE), extend("pi" = THREE + 0.14))] +struct Struct { + #[schemars(extend("foo" = "bar"))] + value: Value, + #[schemars(extend("type" = "overridden"))] + int: i32, +} + +#[test] +fn doc_comments_struct() -> TestResult { + test_default_generated_schema::("extend_struct") +} + +#[allow(dead_code)] +#[derive(JsonSchema)] +#[schemars(extend("foo" = "bar"))] +enum External { + #[schemars(extend("foo" = "bar"))] + Unit, + #[schemars(extend("foo" = "bar"))] + NewType(Value), + #[schemars(extend("foo" = "bar"))] + Tuple(i32, bool), + #[schemars(extend("foo" = "bar"))] + Struct { i: i32, b: bool }, +} + +#[test] +fn doc_comments_enum_external() -> TestResult { + test_default_generated_schema::("extend_enum_external") +} + +#[allow(dead_code)] +#[derive(JsonSchema)] +#[schemars(tag = "typeProperty", extend("foo" = "bar"))] +enum Internal { + #[schemars(extend("foo" = "bar"))] + Unit, + #[schemars(extend("foo" = "bar"))] + NewType(Value), + #[schemars(extend("foo" = "bar"))] + Struct { i: i32, b: bool }, +} + +#[test] +fn doc_comments_enum_internal() -> TestResult { + test_default_generated_schema::("extend_enum_internal") +} + +#[allow(dead_code)] +#[derive(JsonSchema)] +#[schemars(untagged, extend("foo" = "bar"))] +enum Untagged { + #[schemars(extend("foo" = "bar"))] + Unit, + #[schemars(extend("foo" = "bar"))] + NewType(Value), + #[schemars(extend("foo" = "bar"))] + Tuple(i32, bool), + #[schemars(extend("foo" = "bar"))] + Struct { i: i32, b: bool }, +} + +#[test] +fn doc_comments_enum_untagged() -> TestResult { + test_default_generated_schema::("extend_enum_untagged") +} + +#[allow(dead_code)] +#[derive(JsonSchema)] +#[schemars(tag = "t", content = "c", extend("foo" = "bar"))] +enum Adjacent { + #[schemars(extend("foo" = "bar"))] + Unit, + #[schemars(extend("foo" = "bar"))] + NewType(Value), + #[schemars(extend("foo" = "bar"))] + Tuple(i32, bool), + #[schemars(extend("foo" = "bar"))] + Struct { i: i32, b: bool }, +} + +#[test] +fn doc_comments_enum_adjacent() -> TestResult { + test_default_generated_schema::("extend_enum_adjacent") +} diff --git a/schemars/tests/ui/invalid_extend.rs b/schemars/tests/ui/invalid_extend.rs new file mode 100644 index 0000000..b7295f7 --- /dev/null +++ b/schemars/tests/ui/invalid_extend.rs @@ -0,0 +1,11 @@ +use schemars::JsonSchema; + +#[derive(JsonSchema)] +#[schemars(extend(x))] +#[schemars(extend("x"))] +#[schemars(extend("x" = ))] +#[schemars(extend("y" = "ok!", "y" = "duplicated!"), extend("y" = "duplicated!"))] +#[schemars(extend("y" = "duplicated!"))] +pub struct Struct; + +fn main() {} diff --git a/schemars/tests/ui/invalid_extend.stderr b/schemars/tests/ui/invalid_extend.stderr new file mode 100644 index 0000000..d7d2179 --- /dev/null +++ b/schemars/tests/ui/invalid_extend.stderr @@ -0,0 +1,35 @@ +error: expected string literal + --> tests/ui/invalid_extend.rs:4:19 + | +4 | #[schemars(extend(x))] + | ^ + +error: expected `=` + --> tests/ui/invalid_extend.rs:5:22 + | +5 | #[schemars(extend("x"))] + | ^ + +error: Expected extension value + --> tests/ui/invalid_extend.rs:6:25 + | +6 | #[schemars(extend("x" = ))] + | ^ + +error: Duplicate extension key 'y' + --> tests/ui/invalid_extend.rs:7:32 + | +7 | #[schemars(extend("y" = "ok!", "y" = "duplicated!"), extend("y" = "duplicated!"))] + | ^^^ + +error: Duplicate extension key 'y' + --> tests/ui/invalid_extend.rs:7:61 + | +7 | #[schemars(extend("y" = "ok!", "y" = "duplicated!"), extend("y" = "duplicated!"))] + | ^^^ + +error: Duplicate extension key 'y' + --> tests/ui/invalid_extend.rs:8:19 + | +8 | #[schemars(extend("y" = "duplicated!"))] + | ^^^ diff --git a/schemars_derive/src/attr/mod.rs b/schemars_derive/src/attr/mod.rs index 86c9349..108cedc 100644 --- a/schemars_derive/src/attr/mod.rs +++ b/schemars_derive/src/attr/mod.rs @@ -10,7 +10,7 @@ use proc_macro2::{Group, Span, TokenStream, TokenTree}; use quote::ToTokens; use serde_derive_internals::Ctxt; use syn::parse::{self, Parse}; -use syn::{Meta, MetaNameValue}; +use syn::{LitStr, Meta, MetaNameValue}; // FIXME using the same struct for containers+variants+fields means that // with/schema_with are accepted (but ignored) on containers, and @@ -26,6 +26,7 @@ pub struct Attrs { pub repr: Option, pub crate_name: Option, pub is_renamed: bool, + pub extensions: Vec<(String, TokenStream)>, } #[derive(Debug)] @@ -68,6 +69,7 @@ impl Attrs { description: self.description.as_ref().and_then(none_if_empty), deprecated: self.deprecated, examples: &self.examples, + extensions: &self.extensions, read_only: false, write_only: false, default: None, @@ -162,6 +164,29 @@ impl Attrs { } } + Meta::List(m) if m.path.is_ident("extend") && attr_type == "schemars" => { + let parser = + syn::punctuated::Punctuated::::parse_terminated; + match m.parse_args_with(parser) { + Ok(extensions) => { + for extension in extensions { + let key = extension.key.value(); + // This is O(n^2) but should be fine with the typically small number of extensions. + // If this does become a problem, it can be changed to use IndexMap, or a separate Map with cloned keys. + if self.extensions.iter().any(|e| e.0 == key) { + errors.error_spanned_by( + extension.key, + format!("Duplicate extension key '{}'", key), + ); + } else { + self.extensions.push((key, extension.value)); + } + } + } + Err(err) => errors.syn_error(err), + } + } + _ if ignore_errors => {} Meta::List(m) if m.path.is_ident("inner") && attr_type == "schemars" => { @@ -198,7 +223,8 @@ impl Attrs { repr: None, crate_name: None, is_renamed: _, - } if examples.is_empty()) + extensions, + } if examples.is_empty() && extensions.is_empty()) } } @@ -322,3 +348,27 @@ fn respan_token_tree(mut token: TokenTree, span: Span) -> TokenTree { token.set_span(span); token } + +#[derive(Debug)] +struct Extension { + key: LitStr, + value: TokenStream, +} + +impl Parse for Extension { + fn parse(input: parse::ParseStream) -> syn::Result { + let key = input.parse::()?; + input.parse::()?; + let mut value = TokenStream::new(); + + while !input.is_empty() && !input.peek(Token![,]) { + value.extend([input.parse::()?]); + } + + if value.is_empty() { + return Err(syn::Error::new(input.span(), "Expected extension value")); + } + + Ok(Extension { key, value }) + } +} diff --git a/schemars_derive/src/metadata.rs b/schemars_derive/src/metadata.rs index 6de6834..6a3808c 100644 --- a/schemars_derive/src/metadata.rs +++ b/schemars_derive/src/metadata.rs @@ -9,6 +9,7 @@ pub struct SchemaMetadata<'a> { pub write_only: bool, pub examples: &'a [syn::Path], pub default: Option, + pub extensions: &'a [(String, TokenStream)], } impl<'a> SchemaMetadata<'a> { @@ -74,6 +75,12 @@ impl<'a> SchemaMetadata<'a> { }); } + for (k, v) in self.extensions { + setters.push(quote! { + obj.insert(#k.to_owned(), schemars::_serde_json::json!(#v)); + }); + } + setters } } diff --git a/schemars_derive/src/schema_exprs.rs b/schemars_derive/src/schema_exprs.rs index 48da5ef..1f5df99 100644 --- a/schemars_derive/src/schema_exprs.rs +++ b/schemars_derive/src/schema_exprs.rs @@ -232,13 +232,13 @@ fn expr_for_internal_tagged_enum<'a>( let name = variant.name(); let mut schema_expr = expr_for_internal_tagged_enum_variant(variant, deny_unknown_fields); - variant.attrs.as_metadata().apply_to_schema(&mut schema_expr); - - quote!({ + schema_expr = quote!({ let mut schema = #schema_expr; schemars::_private::apply_internal_enum_variant_tag(&mut schema, #tag_name, #name, #deny_unknown_fields); schema - }) + }); + variant.attrs.as_metadata().apply_to_schema(&mut schema_expr); + schema_expr }) .collect(); From 5092aeef38b4e44bb59a8251a5bb971a2073aca7 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Wed, 5 Jun 2024 21:15:16 +0100 Subject: [PATCH 23/31] v1.0.0-alpha.2 --- README.md | 2 +- schemars/Cargo.toml | 4 ++-- schemars_derive/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index de7488c..7181db0 100644 --- a/README.md +++ b/README.md @@ -283,5 +283,5 @@ For example, to implement `JsonSchema` on types from `chrono`, enable it as a fe ```toml [dependencies] -schemars = { version = "1.0.0-alpha.1", features = ["chrono04"] } +schemars = { version = "1.0.0-alpha.2", features = ["chrono04"] } ``` diff --git a/schemars/Cargo.toml b/schemars/Cargo.toml index 734d3df..efe6fb2 100644 --- a/schemars/Cargo.toml +++ b/schemars/Cargo.toml @@ -2,7 +2,7 @@ name = "apistos-schemars" description = "Generate JSON Schemas from Rust code" repository = "https://github.com/netwo-io/apistos-schemars" -version = "1.0.0-alpha.1" +version = "1.0.0-alpha.2" edition = "2021" license = "MIT" readme = "README.md" @@ -12,7 +12,7 @@ build = "build.rs" rust-version = "1.60" [dependencies] -schemars_derive = { package = "apistos-schemars_derive", version = "=1.0.0-alpha.1", optional = true, path = "../schemars_derive" } +schemars_derive = { package = "apistos-schemars_derive", version = "=1.0.0-alpha.2", optional = true, path = "../schemars_derive" } serde = "1.0" serde_json = "1.0.25" dyn-clone = "1.0" diff --git a/schemars_derive/Cargo.toml b/schemars_derive/Cargo.toml index 3d957c3..a0872de 100644 --- a/schemars_derive/Cargo.toml +++ b/schemars_derive/Cargo.toml @@ -2,7 +2,7 @@ name = "apistos-schemars_derive" description = "Macros for #[derive(JsonSchema)], for use with schemars" repository = "https://github.com/netwo-io/apistos-schemars" -version = "1.0.0-alpha.1" +version = "1.0.0-alpha.2" edition = "2021" license = "MIT" readme = "README.md" From 14aaa48f9287690a8206c13834c642c1a62d69d4 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Sun, 9 Jun 2024 19:01:24 +0100 Subject: [PATCH 24/31] Add separate docs for v0.8/v1 --- docs/1-deriving.md | 5 +- docs/1.1-attributes.md | 71 ++-- docs/2-implementing.md | 1 - docs/3-generating.md | 7 +- docs/4-features.md | 1 - docs/5-examples.md | 3 +- docs/Dockerfile | 14 + docs/Gemfile | 2 +- docs/_config.yml | 41 ++- docs/_includes/example_v0.md | 14 + .../examples_v0/custom_serialization.rs | 60 ++++ .../custom_serialization.schema.json | 25 ++ docs/_includes/examples_v0/custom_settings.rs | 24 ++ .../examples_v0/custom_settings.schema.json | 68 ++++ docs/_includes/examples_v0/doc_comments.rs | 33 ++ .../examples_v0/doc_comments.schema.json | 79 +++++ docs/_includes/examples_v0/enum_repr.rs | 15 + .../examples_v0/enum_repr.schema.json | 11 + docs/_includes/examples_v0/from_value.rs | 24 ++ .../examples_v0/from_value.schema.json | 23 ++ docs/_includes/examples_v0/main.rs | 19 ++ docs/_includes/examples_v0/main.schema.json | 70 ++++ docs/_includes/examples_v0/remote_derive.rs | 42 +++ .../examples_v0/remote_derive.schema.json | 43 +++ docs/_includes/examples_v0/schemars_attrs.rs | 30 ++ .../examples_v0/schemars_attrs.schema.json | 67 ++++ docs/_includes/examples_v0/serde_attrs.rs | 24 ++ .../examples_v0/serde_attrs.schema.json | 54 +++ docs/_includes/examples_v0/validate.rs | 24 ++ .../examples_v0/validate.schema.json | 64 ++++ docs/_layouts/v0.md | 10 + docs/_layouts/v1.md | 10 + docs/_sass/color_schemes/default.scss | 2 +- docs/_sass/custom/custom.scss | 4 +- docs/_v0/1-deriving.md | 36 ++ docs/_v0/1.1-attributes.md | 323 ++++++++++++++++++ docs/_v0/2-implementing.md | 78 +++++ docs/_v0/3-generating.md | 35 ++ docs/_v0/4-features.md | 39 +++ docs/_v0/5-examples.md | 8 + docs/_v0/examples/1-derive_jsonschema.md | 12 + docs/_v0/examples/2-serde_attrs.md | 14 + docs/_v0/examples/3-schemars_attrs.md | 12 + docs/_v0/examples/4-custom_settings.md | 12 + docs/_v0/examples/5-remote_derive.md | 16 + docs/_v0/examples/6-doc_comments.md | 12 + docs/_v0/examples/7-custom_serialization.md | 19 ++ docs/_v0/examples/8-enum_repr.md | 13 + docs/_v0/examples/9-from_value.md | 15 + docs/_v0/index.md | 20 ++ docs/docker-compose.yml | 10 + docs/examples/1-derive_jsonschema.md | 1 - docs/examples/2-serde_attrs.md | 5 +- docs/examples/3-schemars_attrs.md | 3 +- docs/examples/4-custom_settings.md | 1 - docs/examples/5-remote_derive.md | 1 - docs/examples/6-doc_comments.md | 1 - docs/examples/7-custom_serialization.md | 3 +- docs/examples/8-enum_repr.md | 1 - docs/examples/9-from_value.md | 1 - docs/index.md | 3 +- 61 files changed, 1620 insertions(+), 58 deletions(-) create mode 100644 docs/Dockerfile create mode 100644 docs/_includes/example_v0.md create mode 100644 docs/_includes/examples_v0/custom_serialization.rs create mode 100644 docs/_includes/examples_v0/custom_serialization.schema.json create mode 100644 docs/_includes/examples_v0/custom_settings.rs create mode 100644 docs/_includes/examples_v0/custom_settings.schema.json create mode 100644 docs/_includes/examples_v0/doc_comments.rs create mode 100644 docs/_includes/examples_v0/doc_comments.schema.json create mode 100644 docs/_includes/examples_v0/enum_repr.rs create mode 100644 docs/_includes/examples_v0/enum_repr.schema.json create mode 100644 docs/_includes/examples_v0/from_value.rs create mode 100644 docs/_includes/examples_v0/from_value.schema.json create mode 100644 docs/_includes/examples_v0/main.rs create mode 100644 docs/_includes/examples_v0/main.schema.json create mode 100644 docs/_includes/examples_v0/remote_derive.rs create mode 100644 docs/_includes/examples_v0/remote_derive.schema.json create mode 100644 docs/_includes/examples_v0/schemars_attrs.rs create mode 100644 docs/_includes/examples_v0/schemars_attrs.schema.json create mode 100644 docs/_includes/examples_v0/serde_attrs.rs create mode 100644 docs/_includes/examples_v0/serde_attrs.schema.json create mode 100644 docs/_includes/examples_v0/validate.rs create mode 100644 docs/_includes/examples_v0/validate.schema.json create mode 100644 docs/_layouts/v0.md create mode 100644 docs/_layouts/v1.md create mode 100644 docs/_v0/1-deriving.md create mode 100644 docs/_v0/1.1-attributes.md create mode 100644 docs/_v0/2-implementing.md create mode 100644 docs/_v0/3-generating.md create mode 100644 docs/_v0/4-features.md create mode 100644 docs/_v0/5-examples.md create mode 100644 docs/_v0/examples/1-derive_jsonschema.md create mode 100644 docs/_v0/examples/2-serde_attrs.md create mode 100644 docs/_v0/examples/3-schemars_attrs.md create mode 100644 docs/_v0/examples/4-custom_settings.md create mode 100644 docs/_v0/examples/5-remote_derive.md create mode 100644 docs/_v0/examples/6-doc_comments.md create mode 100644 docs/_v0/examples/7-custom_serialization.md create mode 100644 docs/_v0/examples/8-enum_repr.md create mode 100644 docs/_v0/examples/9-from_value.md create mode 100644 docs/_v0/index.md create mode 100644 docs/docker-compose.yml diff --git a/docs/1-deriving.md b/docs/1-deriving.md index 6e37fc8..2c844ee 100644 --- a/docs/1-deriving.md +++ b/docs/1-deriving.md @@ -1,5 +1,4 @@ --- -layout: default title: Deriving JsonSchema nav_order: 2 has_children: true @@ -12,6 +11,7 @@ permalink: /deriving/ The most important trait in Schemars is `JsonSchema`, and the most important function of that trait is `json_schema(...)` which returns a JSON schema describing the type. Implementing this manually on many types would be slow and error-prone, so Schemars includes a derive macro which can implement that trait for you. Any derived implementation of `JsonSchema` should create a schema that describes the JSON representation of the type if it were to be serialized by serde_json. Usually, all you need to do to use it is to add a `#[derive(JsonSchema)]` attribute to your type: + ```rust use schemars::{JsonSchema, schema_for}; @@ -28,7 +28,8 @@ fn main() { println!("{}", serialized); } ``` - diff --git a/docs/_v0/1.1-attributes.md b/docs/_v0/1.1-attributes.md new file mode 100644 index 0000000..b668d7f --- /dev/null +++ b/docs/_v0/1.1-attributes.md @@ -0,0 +1,323 @@ +--- +title: Attributes +parent: Deriving JsonSchema +nav_order: 1 +permalink: /v0/deriving/attributes/ +--- + + + +# Attributes + +You can add attributes to your types to customize Schemars's derived `JsonSchema` implementation. + +[Serde](https://serde.rs/) allows setting `#[serde(...)]` attributes which change how types are serialized, and Schemars will generally respect these attributes to ensure that generated schemas will match how the type is serialized by serde_json. `#[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's behaviour, or if you're just not using Serde. + +[Validator](https://github.com/Keats/validator) allows setting `#[validate(...)]` attributes to restrict valid values of particular fields, many of which will be used by Schemars to generate more accurate schemas. These can also be overridden by `#[schemars(...)]` attributes. + +
+ +TABLE OF CONTENTS + + +1. [Supported Serde Attributes](#supported-serde-attributes) + - [`rename`](#rename) + - [`rename_all`](#rename_all) + - [`tag` / `content` / `untagged`](#tag) + - [`default`](#default) + - [`skip`](#skip) + - [`skip_serializing`](#skip_serializing) + - [`skip_deserializing`](#skip_deserializing) + - [`flatten`](#flatten) + - [`with`](#with) + - [`bound`](#bound) +1. [Supported Validator Attributes](#supported-validator-attributes) + - [`email` / `phone` / `url`](#email-phone-url) + - [`length`](#length) + - [`range`](#range) + - [`regex`](#regex) + - [`contains`](#contains) + - [`required` / `required_nested`](#required) +1. [Other Attributes](#other-attributes) + - [`schema_with`](#schema_with) + - [`title` / `description`](#title-description) + - [`example`](#example) + - [`deprecated`](#deprecated) + - [`crate`](#crate) + - [Doc Comments (`doc`)](#doc) + +
+ +## Supported Serde Attributes + +
+ +

+ +`#[serde(rename = "name")]` / `#[schemars(rename = "name")]` + +

+ +Set on a struct, enum, field or variant to use the given name in the generated schema instead of the Rust name. When used on a struct or enum, the given name will be used as the title for root schemas, and the key within the root's `definitions` property for subschemas. + +If set on a struct or enum with generic type parameters, then the given name may contain them enclosed in curly braces (e.g. `{T}`) and they will be replaced with the concrete type names when the schema is generated. + +Serde docs: [container](https://serde.rs/container-attrs.html#rename) / [variant](https://serde.rs/variant-attrs.html#rename) / [field](https://serde.rs/field-attrs.html#rename) + +

+ +`#[serde(rename_all = "...")]` / `#[schemars(rename_all = "...")]` + +

+ +Set on a struct, enum or variant to rename all fields according to the given case convention (see the Serde docs for details). + +Serde docs: [container](https://serde.rs/container-attrs.html#rename_all) / [variant](https://serde.rs/variant-attrs.html#rename_all) + +

+ +`#[serde(tag = "type")]` / `#[schemars(tag = "type")]`
+`#[serde(tag = "t", content = "c")]` / `#[schemars(tag = "t", content = "c")]`
+`#[serde(untagged)]` / `#[schemars(untagged)]` + +

+ +Set on an enum to generate the schema for the [internally tagged](https://serde.rs/enum-representations.html#internally-tagged), [adjacently tagged](https://serde.rs/enum-representations.html#adjacently-tagged), or [untagged](https://serde.rs/enum-representations.html#untagged) representation of this enum. + +Serde docs: [`tag`](https://serde.rs/container-attrs.html#tag) / [`tag`+`content`](https://serde.rs/container-attrs.html#tag--content) / [`untagged`](https://serde.rs/container-attrs.html#untagged) + +

+ +`#[serde(default)]` / `#[schemars(default)]` / `#[serde(default = "path")]` / `#[schemars(default = "path")]` + +

+ +Set on a struct or field to give fields a default value, which excludes them from the schema's `required` properties. The default will also be set on the field's schema's `default` property, unless it is skipped by a [`skip_serializing_if`](https://serde.rs/field-attrs.html#skip_serializing_if) attribute on the field. Any [`serialize_with`](https://serde.rs/field-attrs.html#serialize_with) or [`with`](https://serde.rs/field-attrs.html#with) attribute set on the field will be used to serialize the default value. + +Serde docs: [container](https://serde.rs/container-attrs.html#default) / [field](https://serde.rs/field-attrs.html#default) + +

+ +`#[serde(skip)]` / `#[schemars(skip)]` + +

+ +Set on a variant or field to prevent it from appearing in any generated schema. + +Serde docs: [variant](https://serde.rs/variant-attrs.html#skip) / [field](https://serde.rs/field-attrs.html#skip) + +

+ +`#[serde(skip_serializing)]` / `#[schemars(skip_serializing)]` + +

+ +Set on a field of a (non-tuple) struct to set the `writeOnly` property on that field's schema. Serde also allows this attribute on variants or tuple struct fields, but this will have no effect on generated schemas. + +Serde docs: [field](https://serde.rs/field-attrs.html#skip_deserializing) + +

+ +`#[serde(skip_deserializing)]` / `#[schemars(skip_deserializing)]` + +

+ +Set on a variant or field. When set on a field of a (non-tuple) struct, that field's schema will have the `readOnly` property set. When set on a variant or tuple struct field Schemars will treat this the same as a [`skip`](#skip) attribute. + +Serde docs: [variant](https://serde.rs/variant-attrs.html#skip_deserializing) / [field](https://serde.rs/field-attrs.html#skip_deserializing) + +

+ +`#[serde(flatten)]` / `#[schemars(flatten)]` + +

+ +Set on a field to include that field's contents as though they belonged to the field's container. + +Serde docs: [field](https://serde.rs/field-attrs.html#flatten) + +

+ +`#[serde(with = "Type")]` / `#[schemars(with = "Type")]` + +

+ +Set on a variant or field to generate its schema as the given type instead of its actual type. Serde allows the `with` attribute to refer to any module path, but Schemars requires this to be an actual type which implements `JsonSchema`. + +If the given type has any required generic type parameters, then they must all be explicitly specified in this attribute. Serde frequently allows you to omit them as it can make use of type inference, but unfortunately this is not possible with Schemars. For example, `with = "Vec::"` will work, but `with = "Vec"` and `with = "Vec::<_>"` will not. + +Serde docs: [variant](https://serde.rs/variant-attrs.html#with) / [field](https://serde.rs/field-attrs.html#with) + +

+ +`#[serde(deny_unknown_fields)]` / `#[schemars(deny_unknown_fields)]` + +

+ +Setting this on a container will set the `additionalProperties` keyword on generated schemas to `false` to show that any extra properties are explicitly disallowed. + +Serde docs: [container](https://serde.rs/container-attrs.html#deny_unknown_fields) + +

+ +`#[serde(transparent)]` / `#[schemars(transparent)]` + +

+ +Set on a newtype struct or a braced struct with one field to make the struct's generated schema exactly the same as that of the single field's. + +Serde docs: [container](https://serde.rs/container-attrs.html#transparent) + +

+ +`#[schemars(bound = "...")]` + +

+ +Where-clause for the JsonSchema impl. This replaces any trait bounds inferred by schemars. Schemars does **not** use trait bounds from `#[serde(bound)]` attributes. + +Serde docs: [container](https://serde.rs/container-attrs.html#bound) + +
+ +## Supported Validator Attributes + +
+ +

+ +`#[validate(email)]` / `#[schemars(email)]`
+`#[validate(phone)]` / `#[schemars(phone)]`
+`#[validate(url)]` / `#[schemars(url)]` + +

+ +Sets the schema's `format` to `email`/`phone`/`uri`, as appropriate. Only one of these attributes may be present on a single field. + +Validator docs: [email](https://github.com/Keats/validator#email) / [phone](https://github.com/Keats/validator#phone) / [url](https://github.com/Keats/validator#url) + +

+ +`#[validate(length(min = 1, max = 10))]` / `#[schemars(length(min = 1, max = 10))]`
+`#[validate(length(equal = 10))]` / `#[schemars(length(equal = 10))]` + +

+ +Sets the `minLength`/`maxLength` properties for string schemas, or the `minItems`/`maxItems` properties for array schemas. + +Validator docs: [length](https://github.com/Keats/validator#length) + +

+ +`#[validate(range(min = 1, max = 10))]` / `#[schemars(range(min = 1, max = 10))]` + +

+ +Sets the `minimum`/`maximum` properties for number schemas. + +Validator docs: [range](https://github.com/Keats/validator#range) + +

+ +`#[validate(regex = "path::to::regex")]` / `#[schemars(regex = "path::to::regex")]`
+`#[schemars(regex(pattern = r"^\d+$"))]` + +

+ +Sets the `pattern` property for string schemas. The `path::to::regex` will typically refer to a [`Regex`](https://docs.rs/regex/*/regex/struct.Regex.html) instance, but Schemars allows it to be any value with a `to_string()` method. + +Providing an inline regex pattern using `regex(pattern = ...)` is a Schemars extension, and not currently supported by the Validator crate. When using this form, you may want to use a `r"raw string literal"` so that `\\` characters in the regex pattern are not interpreted as escape sequences in the string. + +Validator docs: [regex](https://github.com/Keats/validator#regex) + +

+ +`#[validate(contains = "string")]` / `#[schemars(contains = "string")]` + +

+ +For string schemas, sets the `pattern` property to the given value, with any regex special characters escaped. For object schemas (e.g. when the attribute is set on a HashMap field), includes the value in the `required` property, indicating that the map must contain it as a key. + +Validator docs: [contains](https://github.com/Keats/validator#contains) + +

+ +`#[validate(required)]` / `#[schemars(required)]`
+`#[validate(required_nested)]` + +

+ +When set on an `Option` field, this will create a schemas as though the field were a `T`. + +Validator docs: [required](https://github.com/Keats/validator#required) / [required_nested](https://github.com/Keats/validator#required_nested) + +
+ +## Other Attributes + +

+ +`#[schemars(schema_with = "some::function")]` + +

+ +Set on a variant or field to generate this field's schema using the given function. This function must be callable as `fn(&mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema`. + +

+ +`#[schemars(title = "Some title", description = "Some description")]` + +

+ +Set on a container, variant or field to set the generated schema's `title` and/or `description`. If present, these will be used instead of values from any [`doc` comments/attributes](#doc). + +

+ +`#[schemars(example = "some::function")]` + +

+ +Set on a container, variant or field to include the result of the given function in the generated schema's `examples`. The function should take no parameters and can return any type that implements serde's `Serialize` trait - it does not need to return the same type as the attached struct/field. This attribute can be repeated to specify multiple examples. + +

+ +`#[deprecated]` + +

+ +Set the Rust built-in [`deprecated`](https://doc.rust-lang.org/edition-guide/rust-2018/the-compiler/an-attribute-for-deprecation.html) attribute on a struct, enum, field or variant to set the generated schema's `deprecated` keyword to `true`. + +

+ +`#[schemars(crate = "other_crate::schemars")]` + +

+ +Set the path to the schemars crate instance the generated code should depend on. This is mostly useful for other crates that depend on schemars in their macros. + +

+ +`#[schemars(inner(...))]` + +

+ +Sets properties specified by [validator attributes](#supported-validator-attributes) on items of an array schema. For example: + +```rs +struct Struct { + #[schemars(inner(url, regex(pattern = "^https://")))] + urls: Vec, +} +``` + +

+ +Doc Comments (`#[doc = "..."]`) + +

+ +If a struct, variant or field has any [doc comments](https://doc.rust-lang.org/stable/rust-by-example/meta/doc.html#doc-comments) (or [`doc` attributes](https://doc.rust-lang.org/rustdoc/the-doc-attribute.html)), then these will be used as the generated schema's `description`. If the first line is an ATX-style markdown heading (i.e. it begins with a # character), then it will be used as the schema's `title`, and the remaining lines will be the `description`. diff --git a/docs/_v0/2-implementing.md b/docs/_v0/2-implementing.md new file mode 100644 index 0000000..32f4edd --- /dev/null +++ b/docs/_v0/2-implementing.md @@ -0,0 +1,78 @@ +--- +title: Implementing JsonSchema +nav_order: 3 +permalink: /v0/implementing/ +--- + +# Implementing JsonSchema + +[Deriving `JsonSchema`]({{ site.baseurl }}{% link 1-deriving.md %}) is usually the easiest way to enable JSON schema generation for your types. But if you need more customisation, you can also implement `JsonSchema` manually. This trait has two associated functions which must be implemented, and one which can optionally be implemented: + +## schema_name + +```rust +fn schema_name() -> String; +``` + +This function returns the human-readable friendly name of the type's schema, which frequently is just the name of the type itself. The schema name is used as the title for root schemas, and the key within the root's `definitions` property for subschemas. + +NB in a future version of schemars, it's likely that this function will be changed to return a `Cow<'static, str>`. + +## schema_id + +```rust +fn schema_id() -> Cow<'static, str>; +``` + +This function returns a unique identifier of the type's schema - if two types return the same `schema_id`, then Schemars will consider them identical types. Because of this, if a type takes any generic type parameters, then its ID should depend on the type arguments. For example, the implementation of this function for `Vec where T: JsonSchema` is: + +```rust +fn schema_id() -> Cow<'static, str> { + Cow::Owned( + format!("[{}]", T::schema_id())) +} +``` + +`&mut Vec<&T>`, `LinkedList`, `Mutex>>`, and similar collection types also use that implementation, since they produce identical JSON schemas so they can be considered the same type. + +For a type with no generic type arguments, a reasonable implementation of this function would be to return the type name including module path (in case there is a type with the same name in another module/crate), e.g.: + +```rust +impl JsonSchema for NonGenericType { + fn schema_name() -> String { + // Exclude the module path to make the name in generated schemas clearer. + "NonGenericType".to_owned() + } + + fn schema_id() -> Cow<'static, str> { + // Include the module, in case a type with the same name is in another module/crate + Cow::Borrowed(concat!(module_path!(), "::NonGenericType")) + } + + fn json_schema(_gen: &mut SchemaGenerator) -> Schema { + todo!() + } +} +``` + +## json_schema + +```rust +fn json_schema(gen: &mut gen::SchemaGenerator) -> Schema; +``` + +This function creates the JSON schema itself. The `gen` argument can be used to check the schema generation settings, or to get schemas for other types. If you do need schemas for other types, you should call the `gen.subschema_for::()` method instead of `::json_schema(gen)`, as `subschema_for` can add `T`'s schema to the root schema's `definitions` so that it does not need to be duplicated when used more than once. + +`json_schema` should not return a `$ref` schema. + +## is_referenceable (optional) + +```rust +fn is_referenceable() -> bool; +``` + +If this function returns `true`, then Schemars can re-use the generate schema where possible by adding it to the root schema's `definitions` and having other schemas reference it using the `$ref` keyword. This can greatly simplify schemas that include a particular type multiple times, especially if that type's schema is fairly complex. + +Generally, this should return `false` for types with simple schemas (such as primitives). For more complex types, it should return `true`. For recursive types, this **must** return `true` to prevent infinite cycles when generating schemas. + +The default implementation of this function returns `true` to reduce the chance of someone inadvertently causing infinite cycles with recursive types. diff --git a/docs/_v0/3-generating.md b/docs/_v0/3-generating.md new file mode 100644 index 0000000..ec56349 --- /dev/null +++ b/docs/_v0/3-generating.md @@ -0,0 +1,35 @@ +--- +title: Generating Schemas +nav_order: 4 +permalink: /v0/generating/ +--- + +# Generating Schemas + +The easiest way to generate a schema for a type that implements is to use the [`schema_for!` macro](https://docs.rs/schemars/latest/schemars/macro.schema_for.html), like so: + +```rust +let my_schema = schema_for!(MyStruct); +``` + +This will create a schema that conforms to [JSON Schema Draft 7](https://json-schema.org/specification-links.html#draft-7), but this is liable to change in a future version of Schemars if support for other JSON Schema versions is added. + +If you want more control over how the schema is generated, you can use the [`gen` module](https://docs.rs/schemars/latest/schemars/gen/). There are two main types in this module: + +- [`SchemaSettings`](https://docs.rs/schemars/latest/schemars/gen/struct.SchemaSettings.html), which defines what JSON Schema features should be used when generating schemas (for example, how `Option`s should be represented). +- [`SchemaGenerator`](https://docs.rs/schemars/latest/schemars/gen/struct.SchemaGenerator.html), which manages the generation of a schema document. + +See the API documentation for more info on how to use those types for custom schema generation. + +## Schema from Example Value + +If you want a schema for a type that can't/doesn't implement `JsonSchema`, but does implement `serde::Serialize`, then you can generate a JSON schema from a value of that type using the [`schema_for_value!` macro](https://docs.rs/schemars/latest/schemars/macro.schema_for_value.html). However, this schema will generally be less precise than if the type implemented `JsonSchema` - particularly when it involves enums, since schemars will not make any assumptions about the structure of an enum based on a single variant. + +```rust +let value = MyStruct { foo = 123 }; +let my_schema = schema_for_value!(value); +``` + + diff --git a/docs/_v0/4-features.md b/docs/_v0/4-features.md new file mode 100644 index 0000000..4932fbe --- /dev/null +++ b/docs/_v0/4-features.md @@ -0,0 +1,39 @@ +--- +title: Feature Flags +nav_order: 5 +permalink: /v0/features/ +--- + +# Feature Flags and Optional Dependencies + +- `derive` (enabled by default) - provides `#[derive(JsonSchema)]` macro +- `impl_json_schema` - implements `JsonSchema` for Schemars types themselves +- `preserve_order` - keep the order of struct fields in `Schema` and `SchemaObject` +- `raw_value` - implements `JsonSchema` for `serde_json::value::RawValue` (enables the serde_json `raw_value` feature) + +Schemars can implement `JsonSchema` on types from several popular crates, enabled via feature flags (dependency versions are shown in brackets): + +- `chrono` - [chrono](https://crates.io/crates/chrono) (^0.4) +- `indexmap1` - [indexmap](https://crates.io/crates/indexmap) (^1.2) +- `indexmap2` - [indexmap](https://crates.io/crates/indexmap) (^2.0) +- `either` - [either](https://crates.io/crates/either) (^1.3) +- `uuid08` - [uuid](https://crates.io/crates/uuid) (^0.8) +- `uuid1` - [uuid](https://crates.io/crates/uuid) (^1.0) +- `smallvec` - [smallvec](https://crates.io/crates/smallvec) (^1.0) +- `arrayvec05` - [arrayvec](https://crates.io/crates/arrayvec) (^0.5) +- `arrayvec07` - [arrayvec](https://crates.io/crates/arrayvec) (^0.7) +- `url` - [url](https://crates.io/crates/url) (^2.0) +- `bytes` - [bytes](https://crates.io/crates/bytes) (^1.0) +- `enumset` - [enumset](https://crates.io/crates/enumset) (^1.0) +- `rust_decimal` - [rust_decimal](https://crates.io/crates/rust_decimal) (^1.0) +- `bigdecimal03` - [bigdecimal](https://crates.io/crates/bigdecimal) (^0.3) +- `bigdecimal04` - [bigdecimal](https://crates.io/crates/bigdecimal) (^0.4) +- `smol_str` - [smol_str](https://crates.io/crates/smol_str) (^0.1.17) +- `semver` - [semver](https://crates.io/crates/semver) (^1.0.9) + +For example, to implement `JsonSchema` on types from `chrono`, enable it as a feature in the `schemars` dependency in your `Cargo.toml` like so: + +```toml +[dependencies] +schemars = { version = "0.8", features = ["chrono"] } +``` diff --git a/docs/_v0/5-examples.md b/docs/_v0/5-examples.md new file mode 100644 index 0000000..0df7e3d --- /dev/null +++ b/docs/_v0/5-examples.md @@ -0,0 +1,8 @@ +--- +title: Examples +nav_order: 6 +has_children: true +permalink: /v0/examples/ +--- + +# Examples diff --git a/docs/_v0/examples/1-derive_jsonschema.md b/docs/_v0/examples/1-derive_jsonschema.md new file mode 100644 index 0000000..590b799 --- /dev/null +++ b/docs/_v0/examples/1-derive_jsonschema.md @@ -0,0 +1,12 @@ +--- +title: Deriving JsonSchema +parent: Examples +nav_order: 1 +summary: Deriving JsonSchema on a struct and enum. +--- + +# Deriving JsonSchema + +This is the simplest usage of Schemars. Both types are made to derive `JsonSchema`, and the `schema_for!` macro is used to generate the schema itself. + +{% include example_v0.md name="main" %} diff --git a/docs/_v0/examples/2-serde_attrs.md b/docs/_v0/examples/2-serde_attrs.md new file mode 100644 index 0000000..b665319 --- /dev/null +++ b/docs/_v0/examples/2-serde_attrs.md @@ -0,0 +1,14 @@ +--- +title: Using Serde Attributes +parent: Examples +nav_order: 2 +summary: "Deriving JsonSchema on types that use #[serde] attributes to customise serialization behaviour." +--- + +# Using Serde Attributes + +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. + +The list of supported `#[serde]` attributes are [documented here]({{ site.baseurl }}{% link 1.1-attributes.md %}#supported-serde-attributes). + +{% include example_v0.md name="serde_attrs" %} diff --git a/docs/_v0/examples/3-schemars_attrs.md b/docs/_v0/examples/3-schemars_attrs.md new file mode 100644 index 0000000..2a5de96 --- /dev/null +++ b/docs/_v0/examples/3-schemars_attrs.md @@ -0,0 +1,12 @@ +--- +title: Using Schemars Attributes +parent: Examples +nav_order: 3 +summary: "Deriving JsonSchema on types that use #[schemars] attributes to customise serialization behaviour." +--- + +# Using Serde Attributes + +`#[serde(...)]` attributes can be overriden (or replaced) with `#[schemars(...)]` attributes, which behave identically. You may find this useful if you want to change the generated schema without affecting Serde's behaviour, or if you're just not using Serde. + +{% include example_v0.md name="schemars_attrs" %} diff --git a/docs/_v0/examples/4-custom_settings.md b/docs/_v0/examples/4-custom_settings.md new file mode 100644 index 0000000..e439f50 --- /dev/null +++ b/docs/_v0/examples/4-custom_settings.md @@ -0,0 +1,12 @@ +--- +title: Custom Schema Settings +parent: Examples +nav_order: 4 +summary: Generating a schema using custom settings which changes how Option is handled. +--- + +# Custom Schema Settings + +The `gen` module allows you to customise how schemas are generated. For example, the default behaviour for `Option` is to include `null` in the schema's `type`s, but we can instead add a `nullable` property to its schema: + +{% include example_v0.md name="custom_settings" %} diff --git a/docs/_v0/examples/5-remote_derive.md b/docs/_v0/examples/5-remote_derive.md new file mode 100644 index 0000000..ee0adb6 --- /dev/null +++ b/docs/_v0/examples/5-remote_derive.md @@ -0,0 +1,16 @@ +--- +title: Derive for Remote Crate +parent: Examples +nav_order: 5 +summary: Deriving JsonSchema implementations for a type in somebody else's crate. +--- + +# Deriving JsonSchema for a Type in a Different Crate + +Rust's [orphan rule](https://doc.rust-lang.org/book/traits.html#rules-for-implementing-traits) requires that either the trait or the type for which you are implementing the trait must be defined in the same crate as the impl, so it is not possible to implement `JsonSchema` for a type in a different crate directly. + +To work around this, Schemars provides a way of deriving `JsonSchema` implementations for types in other people's crates. The only catch is that you have to provide a definition of the type for Schemars's derive to process. + +This is the same way that Serde allows remote deriving, which is why this page reads so similarly to [Serde's documentation](https://serde.rs/remote-derive.html)! + +{% include example_v0.md name="remote_derive" %} diff --git a/docs/_v0/examples/6-doc_comments.md b/docs/_v0/examples/6-doc_comments.md new file mode 100644 index 0000000..24de317 --- /dev/null +++ b/docs/_v0/examples/6-doc_comments.md @@ -0,0 +1,12 @@ +--- +title: Doc Comments +parent: Examples +nav_order: 6 +summary: Giving schemas a custom title and/or description using doc comments. +--- + +# Setting a Custom Title and/or Description Using Doc Comments + +If a struct, variant or field has any [doc comments](https://doc.rust-lang.org/stable/rust-by-example/meta/doc.html#doc-comments) (or [`doc` attributes](https://doc.rust-lang.org/rustdoc/the-doc-attribute.html)), then these will be used as the generated schema's `description`. If the first line is an ATX-style markdown heading (i.e. it begins with a # character), then it will be used as the schema's `title`, and the remaining lines will be the `description`. + +{% include example_v0.md name="doc_comments" %} diff --git a/docs/_v0/examples/7-custom_serialization.md b/docs/_v0/examples/7-custom_serialization.md new file mode 100644 index 0000000..499e670 --- /dev/null +++ b/docs/_v0/examples/7-custom_serialization.md @@ -0,0 +1,19 @@ +--- +title: Custom Serialization +parent: Examples +nav_order: 7 +summary: >- + If a field has a #[serde(with = "path")] attribute where "path" is not a type that implements JsonSchema, + then in order to derive JsonSchema on the type, it must also have a #[schemars(with = "Type")] attribute, + where "Type" implements JsonSchema. +--- + +# Deriving JsonSchema with Fields Using Custom Serialization + +Serde allows you to change how a field is (de)serialized by setting a [`#[serde(with = "path")]`](https://serde.rs/field-attrs.html#with) attribute, where `$path::serialize` and `$path::deserialize` must be functions with the correct signature. Schemars supports the same attribute, but `path` must be a type implementing `JsonSchema`. + +In order to derive `JsonSchema` on a type which includes a `#[serde(with = "path")]` attribute where `path` is not a type implementing `JsonSchema`, you'll need to override it with a suitable `#[schemars(with = "Type")]` or `#[schemars(schema_with = "path")]` attribute. + +{% include example_v0.md name="custom_serialization" %} + +Note that the `default` values in the schema are serialized as strings where appropriate. diff --git a/docs/_v0/examples/8-enum_repr.md b/docs/_v0/examples/8-enum_repr.md new file mode 100644 index 0000000..1cfcf81 --- /dev/null +++ b/docs/_v0/examples/8-enum_repr.md @@ -0,0 +1,13 @@ +--- +title: Serialize Enum as Number (serde_repr) +parent: Examples +nav_order: 8 +summary: >- + Generating a schema for with a C-like enum compatible with serde_repr. +--- + +# Serialize Enum as Number (serde_repr Compatibility) + +If you use the `#[repr(...)]` attribute on an enum to give it a C-like representation, then you may also want to use the [serde_repr](https://github.com/dtolnay/serde-repr) crate to serialize the enum values as numbers. In this case, you should use the corresponding `JsonSchema_repr` derive to ensure the schema for your type reflects how serde formats your type. + +{% include example_v0.md name="enum_repr" %} diff --git a/docs/_v0/examples/9-from_value.md b/docs/_v0/examples/9-from_value.md new file mode 100644 index 0000000..45198ff --- /dev/null +++ b/docs/_v0/examples/9-from_value.md @@ -0,0 +1,15 @@ +--- +title: Generate Schema from Example Value +parent: Examples +nav_order: 9 +summary: >- + Generating a schema for a serializable value. +--- + +# Generate Schema from Example Value + +If you want a schema for a type that can't/doesn't implement `JsonSchema`, but does implement [`serde::Serialize`](https://docs.serde.rs/serde/trait.Serialize.html), then you can generate a JSON schema from a value of that type. However, this schema will generally be less precise than if the type implemented `JsonSchema` - particularly when it involves enums, since schemars will not make any assumptions about the structure of an enum based on a single variant. + +{% include example_v0.md name="from_value" %} + +Note that the schema for the enum is not very useful in this case, since schemars doesn't know anything about the second variant. diff --git a/docs/_v0/index.md b/docs/_v0/index.md new file mode 100644 index 0000000..3b10e0d --- /dev/null +++ b/docs/_v0/index.md @@ -0,0 +1,20 @@ +--- +title: Overview +has_children: true +nav_order: 1 +permalink: /v0/ +--- + +# Schemars + +Schemars is a library to generate JSON Schema documents from Rust data structures. + +This is built on Rust's trait system - any type which implements the [`JsonSchema`](https://docs.rs/schemars/latest/schemars/trait.JsonSchema.html) trait can have a JSON Schema generated describing that type. Schemars implements this on many standard library types, and provides a derive macro to automatically implement it on custom types. + +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. + +## Basic Usage + +If you don't really care about the specifics, the easiest way to generate a JSON schema for your types is to `#[derive(JsonSchema)]` and use the `schema_for!` macro. All fields of the type must also implement `JsonSchema` - Schemars implements this for many standard library types. + +{% include example.md name="main" %} diff --git a/docs/docker-compose.yml b/docs/docker-compose.yml new file mode 100644 index 0000000..0afa5eb --- /dev/null +++ b/docs/docker-compose.yml @@ -0,0 +1,10 @@ +--- +services: + jekyll: + build: + context: . + volumes: + - ".:/docs" + working_dir: "/docs" + ports: + - 4000:4000 \ No newline at end of file diff --git a/docs/examples/1-derive_jsonschema.md b/docs/examples/1-derive_jsonschema.md index 3bf4548..efe245c 100644 --- a/docs/examples/1-derive_jsonschema.md +++ b/docs/examples/1-derive_jsonschema.md @@ -1,5 +1,4 @@ --- -layout: default title: Deriving JsonSchema parent: Examples nav_order: 1 diff --git a/docs/examples/2-serde_attrs.md b/docs/examples/2-serde_attrs.md index 0044a6e..79536e2 100644 --- a/docs/examples/2-serde_attrs.md +++ b/docs/examples/2-serde_attrs.md @@ -1,14 +1,13 @@ --- -layout: default title: Using Serde Attributes parent: Examples nav_order: 2 -summary: 'Deriving JsonSchema on types that use #[serde] attributes to customise serialization behaviour.' +summary: "Deriving JsonSchema on types that use #[serde] attributes to customise serialization behaviour." --- # Using Serde Attributes -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. +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. The list of supported `#[serde]` attributes are [documented here]({{ site.baseurl }}{% link 1.1-attributes.md %}#supported-serde-attributes). diff --git a/docs/examples/3-schemars_attrs.md b/docs/examples/3-schemars_attrs.md index 5956a25..d3a2c08 100644 --- a/docs/examples/3-schemars_attrs.md +++ b/docs/examples/3-schemars_attrs.md @@ -1,9 +1,8 @@ --- -layout: default title: Using Schemars Attributes parent: Examples nav_order: 3 -summary: 'Deriving JsonSchema on types that use #[schemars] attributes to customise serialization behaviour.' +summary: "Deriving JsonSchema on types that use #[schemars] attributes to customise serialization behaviour." --- # Using Serde Attributes diff --git a/docs/examples/4-custom_settings.md b/docs/examples/4-custom_settings.md index e9482cc..7b85f65 100644 --- a/docs/examples/4-custom_settings.md +++ b/docs/examples/4-custom_settings.md @@ -1,5 +1,4 @@ --- -layout: default title: Custom Schema Settings parent: Examples nav_order: 4 diff --git a/docs/examples/5-remote_derive.md b/docs/examples/5-remote_derive.md index 93e99c1..fd7f74f 100644 --- a/docs/examples/5-remote_derive.md +++ b/docs/examples/5-remote_derive.md @@ -1,5 +1,4 @@ --- -layout: default title: Derive for Remote Crate parent: Examples nav_order: 5 diff --git a/docs/examples/6-doc_comments.md b/docs/examples/6-doc_comments.md index 9a5cdf9..66f4a49 100644 --- a/docs/examples/6-doc_comments.md +++ b/docs/examples/6-doc_comments.md @@ -1,5 +1,4 @@ --- -layout: default title: Doc Comments parent: Examples nav_order: 6 diff --git a/docs/examples/7-custom_serialization.md b/docs/examples/7-custom_serialization.md index 8caa930..b124ae7 100644 --- a/docs/examples/7-custom_serialization.md +++ b/docs/examples/7-custom_serialization.md @@ -1,5 +1,4 @@ --- -layout: default title: Custom Serialization parent: Examples nav_order: 7 @@ -13,7 +12,7 @@ summary: >- Serde allows you to change how a field is (de)serialized by setting a [`#[serde(with = "path")]`](https://serde.rs/field-attrs.html#with) attribute, where `$path::serialize` and `$path::deserialize` must be functions with the correct signature. Schemars supports the same attribute, but `path` must be a type implementing `JsonSchema`. -In order to derive `JsonSchema` on a type which includes a `#[serde(with = "path")]` attribute where `path` is not a type implementing `JsonSchema`, you'll need to override it with a suitable `#[schemars(with = "Type")]` or `#[schemars(schema_with = "path")]` attribute. +In order to derive `JsonSchema` on a type which includes a `#[serde(with = "path")]` attribute where `path` is not a type implementing `JsonSchema`, you'll need to override it with a suitable `#[schemars(with = "Type")]` or `#[schemars(schema_with = "path")]` attribute. {% include example.md name="custom_serialization" %} diff --git a/docs/examples/8-enum_repr.md b/docs/examples/8-enum_repr.md index 0312c29..9533cba 100644 --- a/docs/examples/8-enum_repr.md +++ b/docs/examples/8-enum_repr.md @@ -1,5 +1,4 @@ --- -layout: default title: Serialize Enum as Number (serde_repr) parent: Examples nav_order: 8 diff --git a/docs/examples/9-from_value.md b/docs/examples/9-from_value.md index b711be0..8922925 100644 --- a/docs/examples/9-from_value.md +++ b/docs/examples/9-from_value.md @@ -1,5 +1,4 @@ --- -layout: default title: Generate Schema from Example Value parent: Examples nav_order: 9 diff --git a/docs/index.md b/docs/index.md index 2868342..ad71e78 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,5 +1,4 @@ --- -layout: default title: Overview nav_order: 1 --- @@ -10,7 +9,7 @@ Schemars is a library to generate JSON Schema documents from Rust data structure This is built on Rust's trait system - any type which implements the [`JsonSchema`](https://docs.rs/schemars/latest/schemars/trait.JsonSchema.html) trait can have a JSON Schema generated describing that type. Schemars implements this on many standard library types, and provides a derive macro to automatically implement it on custom types. -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. +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. ## Basic Usage From ad5baa9a642ab7de614d5231ddfe9d9ee71e70c2 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Sun, 9 Jun 2024 19:26:40 +0100 Subject: [PATCH 25/31] Add `extend` attribute to docs --- docs/1.1-attributes.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/1.1-attributes.md b/docs/1.1-attributes.md index 66e5736..2e6aa66 100644 --- a/docs/1.1-attributes.md +++ b/docs/1.1-attributes.md @@ -48,6 +48,7 @@ TABLE OF CONTENTS - [`example`](#example) - [`deprecated`](#deprecated) - [`crate`](#crate) + - [`extend`](#extend) - [Doc Comments (`doc`)](#doc) @@ -314,6 +315,21 @@ struct Struct { } ``` +

+ +`#[schemars(extend("key" = value))]` + +

+ +Set on a container, variant or field to add properties (or replace existing properties) in a generated schema. This can contain multiple key/value pairs and/or be specified multiple times, as long as each key is unique. + +The key must be a quoted string, and the value can be any expression that produces a type implementing `serde::Serialize`. The value can also be a JSON literal which can interpolate other values. + +```plaintext +#[schemars(extend("simple" = "string value", "complex" = {"array": [1, 2, 3]}))] +struct Struct; +``` +

Doc Comments (`#[doc = "..."]`) From 1683ece32e9d50e19f8111527956f3fc46081148 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Sun, 9 Jun 2024 19:48:35 +0100 Subject: [PATCH 26/31] Update docs for v1 --- docs/1.1-attributes.md | 2 +- docs/2-implementing.md | 17 +++++++++++------ docs/3-generating.md | 9 ++++++++- docs/4-features.md | 32 +++++++++++++------------------- schemars/src/gen.rs | 2 +- 5 files changed, 34 insertions(+), 28 deletions(-) diff --git a/docs/1.1-attributes.md b/docs/1.1-attributes.md index 2e6aa66..317c51b 100644 --- a/docs/1.1-attributes.md +++ b/docs/1.1-attributes.md @@ -63,7 +63,7 @@ TABLE OF CONTENTS

-Set on a struct, enum, field or variant to use the given name in the generated schema instead of the Rust name. When used on a struct or enum, the given name will be used as the title for root schemas, and the key within the root's `definitions` property for subschemas. +Set on a struct, enum, field or variant to use the given name in the generated schema instead of the Rust name. When used on a struct or enum, the given name will be used as the title for root schemas, and the key within the root's `$defs` property for subschemas. If set on a struct or enum with generic type parameters, then the given name may contain them enclosed in curly braces (e.g. `{T}`) and they will be replaced with the concrete type names when the schema is generated. diff --git a/docs/2-implementing.md b/docs/2-implementing.md index 6ee924e..c106fa8 100644 --- a/docs/2-implementing.md +++ b/docs/2-implementing.md @@ -6,7 +6,7 @@ permalink: /implementing/ # Implementing JsonSchema -[Deriving `JsonSchema`]({{ site.baseurl }}{% link 1-deriving.md %}) is usually the easiest way to enable JSON schema generation for your types. But if you need more customisation, you can also implement `JsonSchema` manually. This trait has two associated functions which must be implemented, and one which can optionally be implemented: +[Deriving `JsonSchema`]({{ site.baseurl }}{% link 1-deriving.md %}) is usually the easiest way to enable JSON schema generation for your types. But if you need more customisation, you can also implement `JsonSchema` manually. This trait has two associated functions which must be implemented, one which usually _should_ be implemented, and one which can optionally be implemented: ## schema_name @@ -14,9 +14,9 @@ permalink: /implementing/ fn schema_name() -> Cow<'static, str>; ``` -This function returns the human-readable friendly name of the type's schema, which frequently is just the name of the type itself. The schema name is used as the title for root schemas, and the key within the root's `definitions` property for subschemas. +This function returns the human-readable friendly name of the type's schema, which frequently is just the name of the type itself. The schema name is used as the title for root schemas, and the key within the root's `$defs` property for subschemas. -## schema_id +## schema_id (optional but recommended) ```rust fn schema_id() -> Cow<'static, str>; @@ -47,18 +47,23 @@ impl JsonSchema for NonGenericType { } fn json_schema(_gen: &mut SchemaGenerator) -> Schema { - todo!() + json_schema!({ + "type": "object", + "foo": "bar" + }) } } ``` +The default implementation of this function returns `Self::schema_name()`. + ## json_schema ```rust fn json_schema(gen: &mut gen::SchemaGenerator) -> Schema; ``` -This function creates the JSON schema itself. The `gen` argument can be used to check the schema generation settings, or to get schemas for other types. If you do need schemas for other types, you should call the `gen.subschema_for::()` method instead of `::json_schema(gen)`, as `subschema_for` can add `T`'s schema to the root schema's `definitions` so that it does not need to be duplicated when used more than once. +This function creates the JSON schema itself. The `gen` argument can be used to check the schema generation settings, or to get schemas for other types. If you do need schemas for other types, you should call the `gen.subschema_for::()` method instead of `::json_schema(gen)`, as `subschema_for` can add `T`'s schema to the root schema's `$defs` so that it does not need to be duplicated when used more than once. `json_schema` should not return a `$ref` schema. @@ -68,7 +73,7 @@ This function creates the JSON schema itself. The `gen` argument can be used to fn always_inline_schema() -> bool; ``` -If this function returns `false`, then Schemars can re-use the generate schema where possible by adding it to the root schema's `definitions` and having other schemas reference it using the `$ref` keyword. This can greatly simplify schemas that include a particular type multiple times, especially if that type's schema is fairly complex. +If this function returns `false`, then Schemars can re-use the generate schema where possible by adding it to the root schema's `$defs` and having other schemas reference it using the `$ref` keyword. This can greatly simplify schemas that include a particular type multiple times, especially if that type's schema is fairly complex. Generally, this should return `true` for types with simple schemas (such as primitives). For more complex types, it should return `false`. For recursive types, this **must** return `false` to prevent infinite cycles when generating schemas. diff --git a/docs/3-generating.md b/docs/3-generating.md index aebc7cc..eb9f862 100644 --- a/docs/3-generating.md +++ b/docs/3-generating.md @@ -12,13 +12,20 @@ The easiest way to generate a schema for a type that implements is to use the [` let my_schema = schema_for!(MyStruct); ``` -This will create a schema that conforms to [JSON Schema Draft 7](https://json-schema.org/specification-links.html#draft-7), but this is liable to change in a future version of Schemars if support for other JSON Schema versions is added. +This will create a schema that conforms to [JSON Schema 2020-12](https://json-schema.org/specification-links#2020-12), but this is liable to change in a future version of Schemars if support for other JSON Schema versions is added. If you want more control over how the schema is generated, you can use the [`gen` module](https://docs.rs/schemars/latest/schemars/gen/). There are two main types in this module: - [`SchemaSettings`](https://docs.rs/schemars/latest/schemars/gen/struct.SchemaSettings.html), which defines what JSON Schema features should be used when generating schemas (for example, how `Option`s should be represented). - [`SchemaGenerator`](https://docs.rs/schemars/latest/schemars/gen/struct.SchemaGenerator.html), which manages the generation of a schema document. +For example, to generate a schema that conforms to [JSON Schema Draft 7](https://json-schema.org/specification-links.html#draft-7): + +```rust +let generator = SchemaSettings::draft07().into_generator(); +let my_schema = generator.into_root_schema_for::(); +``` + See the API documentation for more info on how to use those types for custom schema generation. ## Schema from Example Value diff --git a/docs/4-features.md b/docs/4-features.md index ec1d5dc..d7b4577 100644 --- a/docs/4-features.md +++ b/docs/4-features.md @@ -7,34 +7,28 @@ permalink: /features/ # Feature Flags and Optional Dependencies - `derive` (enabled by default) - provides `#[derive(JsonSchema)]` macro -- `impl_json_schema` - implements `JsonSchema` for Schemars types themselves -- `preserve_order` - keep the order of struct fields in `Schema` and `SchemaObject` +- `preserve_order` - keep the order of struct fields in `Schema` properties - `raw_value` - implements `JsonSchema` for `serde_json::value::RawValue` (enables the serde_json `raw_value` feature) Schemars can implement `JsonSchema` on types from several popular crates, enabled via feature flags (dependency versions are shown in brackets): -- `chrono` - [chrono](https://crates.io/crates/chrono) (^0.4) -- `indexmap1` - [indexmap](https://crates.io/crates/indexmap) (^1.2) -- `indexmap2` - [indexmap](https://crates.io/crates/indexmap) (^2.0) -- `ipnetwork` - [ipnetwork](https://crates.io/crates/ipnetwork) (^0.20) -- `either` - [either](https://crates.io/crates/either) (^1.3) -- `uuid08` - [uuid](https://crates.io/crates/uuid) (^0.8) -- `uuid1` - [uuid](https://crates.io/crates/uuid) (^1.0) -- `smallvec` - [smallvec](https://crates.io/crates/smallvec) (^1.0) -- `arrayvec05` - [arrayvec](https://crates.io/crates/arrayvec) (^0.5) - `arrayvec07` - [arrayvec](https://crates.io/crates/arrayvec) (^0.7) -- `url` - [url](https://crates.io/crates/url) (^2.0) -- `bytes` - [bytes](https://crates.io/crates/bytes) (^1.0) -- `enumset` - [enumset](https://crates.io/crates/enumset) (^1.0) -- `rust_decimal` - [rust_decimal](https://crates.io/crates/rust_decimal) (^1.0) -- `bigdecimal03` - [bigdecimal](https://crates.io/crates/bigdecimal) (^0.3) - `bigdecimal04` - [bigdecimal](https://crates.io/crates/bigdecimal) (^0.4) -- `smol_str` - [smol_str](https://crates.io/crates/smol_str) (^0.1.17) -- `semver` - [semver](https://crates.io/crates/semver) (^1.0.9) +- `bytes1` - [bytes](https://crates.io/crates/bytes) (^1.0) +- `chrono04` - [chrono](https://crates.io/crates/chrono) (^0.4) +- `either1` - [either](https://crates.io/crates/either) (^1.3) +- `enumset1` - [enumset](https://crates.io/crates/enumset) (^1.0) +- `indexmap2` - [indexmap](https://crates.io/crates/indexmap) (^2.0) +- `rust_decimal1` - [rust_decimal](https://crates.io/crates/rust_decimal) (^1.0) +- `semver1` - [semver](https://crates.io/crates/semver) (^1.0.9) +- `smallvec1` - [smallvec](https://crates.io/crates/smallvec) (^1.0) +- `smol_str02` - [smol_str](https://crates.io/crates/smol_str) (^0.2.1) +- `url2` - [url](https://crates.io/crates/url) (^2.0) +- `uuid1` - [uuid](https://crates.io/crates/uuid) (^1.0) For example, to implement `JsonSchema` on types from `chrono`, enable it as a feature in the `schemars` dependency in your `Cargo.toml` like so: ```toml [dependencies] -schemars = { version = "0.8", features = ["chrono"] } +schemars = { version = "1.0.0-alpha.2", features = ["chrono04"] } ``` diff --git a/schemars/src/gen.rs b/schemars/src/gen.rs index b6f12dc..911763f 100644 --- a/schemars/src/gen.rs +++ b/schemars/src/gen.rs @@ -465,7 +465,7 @@ impl SchemaGenerator { } let pointer = self.definitions_path_stripped(); - // `$defs`` and `definitions` are both handled internally by `Visitor::visit_schema`. + // `$defs` and `definitions` are both handled internally by `Visitor::visit_schema`. // If the definitions are in any other location, explicitly visit them here to ensure // they're run against any referenced subschemas. if pointer != "/$defs" && pointer != "/definitions" { From cf88f19c5e2ae2b9ede32c9d490d5f2480508deb Mon Sep 17 00:00:00 2001 From: Romain Lebran Date: Tue, 25 Jun 2024 14:22:11 +0200 Subject: [PATCH 27/31] Fix multiple enum flatten --- schemars/src/_private.rs | 17 +++++ schemars/tests/enum_flatten.rs | 33 ++++++++++ schemars/tests/expected/enum_flatten.json | 75 +++++++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 schemars/tests/enum_flatten.rs create mode 100644 schemars/tests/expected/enum_flatten.json diff --git a/schemars/src/_private.rs b/schemars/src/_private.rs index cdaec3a..62cd693 100644 --- a/schemars/src/_private.rs +++ b/schemars/src/_private.rs @@ -174,13 +174,16 @@ pub fn apply_inner_validation(schema: &mut Schema, f: fn(&mut Schema) -> ()) { pub fn flatten(schema: &mut Schema, other: Schema) { if let Value::Object(obj2) = other.to_value() { let obj1 = schema.ensure_object(); + eprintln!("obj1: {:?}, obj2: {:?}", obj1, obj2); for (key, value2) in obj2 { + eprintln!("key: {:?}, value2: {:?}", key, value2); match obj1.entry(key) { Entry::Vacant(vacant) => { vacant.insert(value2); } Entry::Occupied(mut occupied) => { + eprintln!("====> occupied"); match occupied.key().as_str() { // This special "type" handling can probably be removed once the enum variant `with`/`schema_with` behaviour is fixed "type" => match (occupied.get_mut(), value2) { @@ -220,6 +223,20 @@ pub fn flatten(schema: &mut Schema, other: Schema) { } } } + "oneOf" => { + if let a1 @ Value::Array(_) = occupied.get().clone() { + occupied.remove(); + let all_of = Value::Array(vec![ + json!({ + "oneOf": a1 + }), + json!({ + "oneOf": value2 + }), + ]); + obj1.insert("allOf".to_string(), all_of); + } + } _ => { // leave the original value as it is (don't modify `schema`) } diff --git a/schemars/tests/enum_flatten.rs b/schemars/tests/enum_flatten.rs new file mode 100644 index 0000000..7fd3bb8 --- /dev/null +++ b/schemars/tests/enum_flatten.rs @@ -0,0 +1,33 @@ +mod util; +use schemars::JsonSchema; +use util::*; + +#[allow(dead_code)] +#[derive(JsonSchema)] +#[schemars(rename = "Flat")] +struct Flat { + f: f32, + #[schemars(flatten)] + e1: Enum1, + #[schemars(flatten)] + e2: Enum2, +} + +#[allow(dead_code)] +#[derive(JsonSchema)] +enum Enum1 { + B(bool), + S(String), +} + +#[allow(dead_code)] +#[derive(JsonSchema)] +enum Enum2 { + U(u32), + F(f64), +} + +#[test] +fn test_flat_schema() -> TestResult { + test_default_generated_schema::("enum_flatten") +} diff --git a/schemars/tests/expected/enum_flatten.json b/schemars/tests/expected/enum_flatten.json new file mode 100644 index 0000000..1bb0474 --- /dev/null +++ b/schemars/tests/expected/enum_flatten.json @@ -0,0 +1,75 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Flat", + "type": "object", + "properties": { + "f": { + "type": "number", + "format": "float" + } + }, + "required": [ + "f" + ], + "allOf": [ + { + "oneOf": [ + { + "type": "object", + "properties": { + "B": { + "type": "boolean" + } + }, + "required": [ + "B" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "S": { + "type": "string" + } + }, + "required": [ + "S" + ], + "additionalProperties": false + } + ] + }, + { + "oneOf": [ + { + "type": "object", + "properties": { + "U": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + }, + "required": [ + "U" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "F": { + "type": "number", + "format": "double" + } + }, + "required": [ + "F" + ], + "additionalProperties": false + } + ] + } + ] +} \ No newline at end of file From d7a3c9f28a9623daaf33533b73315a54fa39d8d7 Mon Sep 17 00:00:00 2001 From: Romain Lebran Date: Tue, 25 Jun 2024 16:09:34 +0200 Subject: [PATCH 28/31] Fix post rebase --- Cargo.lock | 4 +- schemars/tests/enum_flatten.rs | 1 + schemars/tests/expected/default.json | 4 + .../tests/expected/multiple_enum_flatten.json | 75 ------------------- schemars/tests/extend.rs | 1 + schemars/tests/multiple_enum_flatten.rs | 34 --------- 6 files changed, 8 insertions(+), 111 deletions(-) delete mode 100644 schemars/tests/expected/multiple_enum_flatten.json delete mode 100644 schemars/tests/multiple_enum_flatten.rs diff --git a/Cargo.lock b/Cargo.lock index cff5b5a..f2ade80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "apistos-schemars" -version = "0.8.21" +version = "1.0.0-alpha.2" dependencies = [ "apistos-schemars_derive", "arrayvec", @@ -30,7 +30,7 @@ dependencies = [ [[package]] name = "apistos-schemars_derive" -version = "0.8.21" +version = "1.0.0-alpha.2" dependencies = [ "pretty_assertions", "proc-macro2", diff --git a/schemars/tests/enum_flatten.rs b/schemars/tests/enum_flatten.rs index 7fd3bb8..de50251 100644 --- a/schemars/tests/enum_flatten.rs +++ b/schemars/tests/enum_flatten.rs @@ -1,4 +1,5 @@ mod util; +extern crate apistos_schemars as schemars; use schemars::JsonSchema; use util::*; diff --git a/schemars/tests/expected/default.json b/schemars/tests/expected/default.json index 73640a3..72e580d 100644 --- a/schemars/tests/expected/default.json +++ b/schemars/tests/expected/default.json @@ -8,6 +8,10 @@ "format": "int32", "default": 0 }, + "my_bool": { + "type": "boolean", + "default": false + }, "my_optional_string": { "type": [ "string", diff --git a/schemars/tests/expected/multiple_enum_flatten.json b/schemars/tests/expected/multiple_enum_flatten.json deleted file mode 100644 index acb8d19..0000000 --- a/schemars/tests/expected/multiple_enum_flatten.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Flat", - "allOf": [ - { - "type": "object", - "oneOf": [ - { - "type": "object", - "required": [ - "B" - ], - "properties": { - "B": { - "type": "boolean" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "S" - ], - "properties": { - "S": { - "type": "string" - } - }, - "additionalProperties": false - } - ], - "required": [ - "f" - ], - "properties": { - "f": { - "type": "number", - "format": "float" - } - } - }, - { - "oneOf": [ - { - "type": "object", - "required": [ - "U" - ], - "properties": { - "U": { - "type": "integer", - "format": "uint32", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "F" - ], - "properties": { - "F": { - "type": "number", - "format": "double" - } - }, - "additionalProperties": false - } - ] - } - ] -} \ No newline at end of file diff --git a/schemars/tests/extend.rs b/schemars/tests/extend.rs index 08f42fa..0136a78 100644 --- a/schemars/tests/extend.rs +++ b/schemars/tests/extend.rs @@ -1,4 +1,5 @@ mod util; +extern crate apistos_schemars as schemars; use schemars::JsonSchema; use serde_json::Value; use util::*; diff --git a/schemars/tests/multiple_enum_flatten.rs b/schemars/tests/multiple_enum_flatten.rs deleted file mode 100644 index 34d1c1e..0000000 --- a/schemars/tests/multiple_enum_flatten.rs +++ /dev/null @@ -1,34 +0,0 @@ -mod util; -extern crate apistos_schemars as schemars; -use schemars::JsonSchema; -use util::*; - -#[allow(dead_code)] -#[derive(JsonSchema)] -#[schemars(rename = "Flat")] -struct Flat { - f: f32, - #[schemars(flatten)] - e1: Enum1, - #[schemars(flatten)] - e2: Enum2, -} - -#[allow(dead_code)] -#[derive(JsonSchema)] -enum Enum1 { - B(bool), - S(String), -} - -#[allow(dead_code)] -#[derive(JsonSchema)] -enum Enum2 { - U(u32), - F(f64) -} - -#[test] -fn test_flat_schema() -> TestResult { - test_default_generated_schema::("multiple_enum_flatten") -} From 96330262950240cdeab241dee07a80986eeec0e3 Mon Sep 17 00:00:00 2001 From: Romain Lebran Date: Tue, 25 Jun 2024 14:22:11 +0200 Subject: [PATCH 29/31] Fix tests --- schemars/src/_private.rs | 3 --- schemars/src/macros.rs | 1 + schemars/src/schema.rs | 3 +++ 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/schemars/src/_private.rs b/schemars/src/_private.rs index 62cd693..1e94531 100644 --- a/schemars/src/_private.rs +++ b/schemars/src/_private.rs @@ -174,16 +174,13 @@ pub fn apply_inner_validation(schema: &mut Schema, f: fn(&mut Schema) -> ()) { pub fn flatten(schema: &mut Schema, other: Schema) { if let Value::Object(obj2) = other.to_value() { let obj1 = schema.ensure_object(); - eprintln!("obj1: {:?}, obj2: {:?}", obj1, obj2); for (key, value2) in obj2 { - eprintln!("key: {:?}, value2: {:?}", key, value2); match obj1.entry(key) { Entry::Vacant(vacant) => { vacant.insert(value2); } Entry::Occupied(mut occupied) => { - eprintln!("====> occupied"); match occupied.key().as_str() { // This special "type" handling can probably be removed once the enum variant `with`/`schema_with` behaviour is fixed "type" => match (occupied.get_mut(), value2) { diff --git a/schemars/src/macros.rs b/schemars/src/macros.rs index aea504d..2b2db0b 100644 --- a/schemars/src/macros.rs +++ b/schemars/src/macros.rs @@ -88,6 +88,7 @@ macro_rules! schema_for_value { /// /// # Example /// ``` +/// extern crate apistos_schemars as schemars; /// use schemars::{Schema, json_schema}; /// /// let desc = "A helpful description."; diff --git a/schemars/src/schema.rs b/schemars/src/schema.rs index dd9e866..41f5766 100644 --- a/schemars/src/schema.rs +++ b/schemars/src/schema.rs @@ -12,6 +12,7 @@ use serde_json::{Map, Value}; /// /// A custom JSON schema can be created using the [`json_schema!`](crate::json_schema) macro: /// ``` +/// extern crate apistos_schemars as schemars; /// use schemars::{Schema, json_schema}; /// /// let my_schema: Schema = json_schema!({ @@ -22,6 +23,7 @@ use serde_json::{Map, Value}; /// Because a `Schema` is a thin wrapper around a `Value`, you can also use [`TryFrom::try_from`]/[`TryInto::try_into`] to create a `Schema` from an existing `Value`. /// This operation is fallible, because only [objects](Value::Object) and [bools](Value::Bool) can be converted in this way. /// ``` +/// extern crate apistos_schemars as schemars; /// use schemars::{Schema, json_schema}; /// use serde_json::json; /// @@ -46,6 +48,7 @@ use serde_json::{Map, Value}; /// /// Similarly, you can use [`From`]/[`Into`] to (infallibly) create a `Schema` from an existing [`Map`] or [`bool`]. /// ``` +/// extern crate apistos_schemars as schemars; /// use schemars::{Schema, json_schema}; /// use serde_json::{Map, json}; /// From d59983ee0b5fe43ea6d1d41728b48d07212ed706 Mon Sep 17 00:00:00 2001 From: Romain Lebran Date: Tue, 25 Jun 2024 16:25:27 +0200 Subject: [PATCH 30/31] Add ipnetwork support --- Cargo.lock | 10 +++ README.md | 1 + docs/_v0/4-features.md | 1 + schemars/Cargo.toml | 1 + schemars/src/json_schema_impls/ipnetwork.rs | 97 +++++++++++---------- schemars/src/json_schema_impls/mod.rs | 3 + schemars/tests/enum.rs | 2 +- schemars/tests/enum_deny_unknown_fields.rs | 2 +- schemars/tests/expected/ipnetwork.json | 2 +- schemars/tests/expected/ipv4network.json | 2 +- schemars/tests/expected/ipv6network.json | 2 +- 11 files changed, 74 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f2ade80..3f0b5c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,6 +15,7 @@ dependencies = [ "either", "enumset", "indexmap", + "ipnetwork", "pretty_assertions", "ref-cast", "rust_decimal", @@ -220,6 +221,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "ipnetwork" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" +dependencies = [ + "serde", +] + [[package]] name = "itoa" version = "1.0.9" diff --git a/README.md b/README.md index 7181db0..99d322a 100644 --- a/README.md +++ b/README.md @@ -272,6 +272,7 @@ Schemars can implement `JsonSchema` on types from several popular crates, enable - `either1` - [either](https://crates.io/crates/either) (^1.3) - `enumset1` - [enumset](https://crates.io/crates/enumset) (^1.0) - `indexmap2` - [indexmap](https://crates.io/crates/indexmap) (^2.0) +- `ipnetwork` - [ipnetwork](https://crates.io/crates/ipnetwork) (^0.20) - `rust_decimal1` - [rust_decimal](https://crates.io/crates/rust_decimal) (^1.0) - `semver1` - [semver](https://crates.io/crates/semver) (^1.0.9) - `smallvec1` - [smallvec](https://crates.io/crates/smallvec) (^1.0) diff --git a/docs/_v0/4-features.md b/docs/_v0/4-features.md index 4932fbe..7ff233c 100644 --- a/docs/_v0/4-features.md +++ b/docs/_v0/4-features.md @@ -16,6 +16,7 @@ Schemars can implement `JsonSchema` on types from several popular crates, enable - `chrono` - [chrono](https://crates.io/crates/chrono) (^0.4) - `indexmap1` - [indexmap](https://crates.io/crates/indexmap) (^1.2) - `indexmap2` - [indexmap](https://crates.io/crates/indexmap) (^2.0) +- `ipnetwork` - [ipnetwork](https://crates.io/crates/ipnetwork) (^0.20) - `either` - [either](https://crates.io/crates/either) (^1.3) - `uuid08` - [uuid](https://crates.io/crates/uuid) (^0.8) - `uuid1` - [uuid](https://crates.io/crates/uuid) (^1.0) diff --git a/schemars/Cargo.toml b/schemars/Cargo.toml index efe6fb2..4d1d324 100644 --- a/schemars/Cargo.toml +++ b/schemars/Cargo.toml @@ -26,6 +26,7 @@ chrono04 = { version = "0.4", default-features = false, optional = true, package either1 = { version = "1.3", default-features = false, optional = true, package = "either" } enumset1 = { version = "1.0", default-features = false, optional = true, package = "enumset" } indexmap2 = { version = "2.0", default-features = false, optional = true, package = "indexmap" } +ipnetwork = { version = "0.20", optional = true } rust_decimal1 = { version = "1", default-features = false, optional = true, package = "rust_decimal"} semver1 = { version = "1.0.9", default-features = false, optional = true, package = "semver" } smallvec1 = { version = "1.0", default-features = false, optional = true, package = "smallvec" } diff --git a/schemars/src/json_schema_impls/ipnetwork.rs b/schemars/src/json_schema_impls/ipnetwork.rs index 0335d57..918a26c 100644 --- a/schemars/src/json_schema_impls/ipnetwork.rs +++ b/schemars/src/json_schema_impls/ipnetwork.rs @@ -1,60 +1,69 @@ -use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network}; use crate::gen::SchemaGenerator; -use crate::JsonSchema; -use crate::schema::{InstanceType, Schema, SchemaObject, SubschemaValidation}; +use crate::{json_schema, JsonSchema, Schema}; +use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network}; +use std::borrow::Cow; impl JsonSchema for IpNetwork { - no_ref_schema!(); - - fn schema_name() -> String { - "Ip".to_string() - } - - fn json_schema(gen: &mut SchemaGenerator) -> Schema { - SchemaObject { - subschemas: Some(Box::new(SubschemaValidation { - one_of: Some(vec![ - Ipv4Network::json_schema(gen), - Ipv6Network::json_schema(gen), - ]), - ..Default::default() - })), - ..Default::default() - } - .into() - } + always_inline!(); + + fn schema_name() -> Cow<'static, str> { + "Ip".into() + } + + fn schema_id() -> Cow<'static, str> { + "ipnetwork::Ip".into() + } + + fn json_schema(_: &mut SchemaGenerator) -> Schema { + json_schema!({ + "oneOf": [ + { + "type": "string", + "format": "ipv4" + } + ,{ + "type": "string", + "format": "ipv6" + } + ] + }) + } } impl JsonSchema for Ipv4Network { - no_ref_schema!(); + always_inline!(); + + fn schema_name() -> Cow<'static, str> { + "IpV4".into() + } - fn schema_name() -> String { - "IpV4".to_string() - } + fn schema_id() -> Cow<'static, str> { + "ipnetwork::IpV4".into() + } - fn json_schema(_: &mut SchemaGenerator) -> Schema { - SchemaObject { - instance_type: Some(InstanceType::String.into()), - format: Some("ipv4".to_string()), - ..Default::default() + fn json_schema(_: &mut SchemaGenerator) -> Schema { + json_schema!({ + "type": "string", + "format": "ipv4" + }) } - .into() - } } impl JsonSchema for Ipv6Network { - no_ref_schema!(); + always_inline!(); - fn schema_name() -> String { - "IpV6".to_string() - } + fn schema_name() -> Cow<'static, str> { + "IpV6".into() + } + + fn schema_id() -> Cow<'static, str> { + "ipnetwork::Ipv6Network".into() + } - fn json_schema(_: &mut SchemaGenerator) -> Schema { - SchemaObject { - instance_type: Some(InstanceType::String.into()), - format: Some("ipv6".to_string()), - ..Default::default() + fn json_schema(_: &mut SchemaGenerator) -> Schema { + json_schema!({ + "type": "string", + "format": "ipv6" + }) } - .into() - } } diff --git a/schemars/src/json_schema_impls/mod.rs b/schemars/src/json_schema_impls/mod.rs index ca9ff28..06b1b46 100644 --- a/schemars/src/json_schema_impls/mod.rs +++ b/schemars/src/json_schema_impls/mod.rs @@ -79,6 +79,9 @@ forward_impl!(( crate::JsonSchema #[cfg(feature = "indexmap2")] mod indexmap2; +#[cfg(feature = "ipnetwork")] +mod ipnetwork; + #[cfg(feature = "semver1")] mod semver1; diff --git a/schemars/tests/enum.rs b/schemars/tests/enum.rs index 244307d..2e8bd97 100644 --- a/schemars/tests/enum.rs +++ b/schemars/tests/enum.rs @@ -1,7 +1,7 @@ mod util; extern crate apistos_schemars as schemars; -use std::collections::BTreeMap; use schemars::JsonSchema; +use std::collections::BTreeMap; use util::*; // Ensure that schemars_derive uses the full path to std::string::String diff --git a/schemars/tests/enum_deny_unknown_fields.rs b/schemars/tests/enum_deny_unknown_fields.rs index 7ab2eef..d0e7dc9 100644 --- a/schemars/tests/enum_deny_unknown_fields.rs +++ b/schemars/tests/enum_deny_unknown_fields.rs @@ -1,7 +1,7 @@ mod util; extern crate apistos_schemars as schemars; -use std::collections::BTreeMap; use schemars::JsonSchema; +use std::collections::BTreeMap; use util::*; // Ensure that schemars_derive uses the full path to std::string::String diff --git a/schemars/tests/expected/ipnetwork.json b/schemars/tests/expected/ipnetwork.json index e446228..71b6e87 100644 --- a/schemars/tests/expected/ipnetwork.json +++ b/schemars/tests/expected/ipnetwork.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Ip", "oneOf": [ { diff --git a/schemars/tests/expected/ipv4network.json b/schemars/tests/expected/ipv4network.json index 8cd1ec7..6a7849e 100644 --- a/schemars/tests/expected/ipv4network.json +++ b/schemars/tests/expected/ipv4network.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "IpV4", "type": "string", "format": "ipv4" diff --git a/schemars/tests/expected/ipv6network.json b/schemars/tests/expected/ipv6network.json index 1c3b373..5ea170e 100644 --- a/schemars/tests/expected/ipv6network.json +++ b/schemars/tests/expected/ipv6network.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "IpV6", "type": "string", "format": "ipv6" From fb5b869a181c1500b3aa4cb62b3eea2827dacccd Mon Sep 17 00:00:00 2001 From: Romain Lebran Date: Thu, 27 Jun 2024 10:28:27 +0200 Subject: [PATCH 31/31] Publish alpha2 --- .github/workflows/draft.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/draft.yaml b/.github/workflows/draft.yaml index cd62ed3..ddaa9ca 100644 --- a/.github/workflows/draft.yaml +++ b/.github/workflows/draft.yaml @@ -4,6 +4,7 @@ on: push: branches: - master + - v1 env: CARGO_TERM_COLOR: always