Define Schema
as a newtype around serde_json::Value
(#289)
This commit is contained in:
parent
7f6a7b7e32
commit
342cd5fd09
79 changed files with 1410 additions and 2394 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
||||||
- nightly
|
- nightly
|
||||||
include:
|
include:
|
||||||
- rust: 1.60.0
|
- rust: 1.60.0
|
||||||
test_features: "--features impl_json_schema"
|
test_features: ""
|
||||||
allow_failure: false
|
allow_failure: false
|
||||||
- rust: stable
|
- rust: stable
|
||||||
test_features: "--all-features"
|
test_features: "--all-features"
|
||||||
|
|
100
Cargo.lock
generated
100
Cargo.lock
generated
|
@ -2,12 +2,6 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "arrayvec"
|
|
||||||
version = "0.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.7.4"
|
version = "0.7.4"
|
||||||
|
@ -29,17 +23,6 @@ dependencies = [
|
||||||
"serde",
|
"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]]
|
[[package]]
|
||||||
name = "bigdecimal"
|
name = "bigdecimal"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
|
@ -168,12 +151,6 @@ version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hashbrown"
|
|
||||||
version = "0.12.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.2"
|
version = "0.14.2"
|
||||||
|
@ -196,17 +173,6 @@ dependencies = [
|
||||||
"unicode-normalization",
|
"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]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.0.2"
|
version = "2.0.2"
|
||||||
|
@ -214,8 +180,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897"
|
checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown 0.14.2",
|
"hashbrown",
|
||||||
"serde",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -284,29 +249,49 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.69"
|
version = "1.0.81"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.33"
|
version = "1.0.36"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ref-cast"
|
||||||
|
version = "1.0.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4846d4c50d1721b1a3bef8af76924eef20d5e723647333798c1b519b3a9473f"
|
||||||
|
dependencies = [
|
||||||
|
"ref-cast-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ref-cast-impl"
|
||||||
|
version = "1.0.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust_decimal"
|
name = "rust_decimal"
|
||||||
version = "1.32.0"
|
version = "1.32.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4c4216490d5a413bc6d10fa4742bd7d4955941d062c0ef873141d6b0e7b30fd"
|
checksum = "a4c4216490d5a413bc6d10fa4742bd7d4955941d062c0ef873141d6b0e7b30fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec 0.7.4",
|
"arrayvec",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -320,18 +305,16 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
||||||
name = "schemars"
|
name = "schemars"
|
||||||
version = "0.8.19"
|
version = "0.8.19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec 0.5.2",
|
"arrayvec",
|
||||||
"arrayvec 0.7.4",
|
"bigdecimal",
|
||||||
"bigdecimal 0.3.1",
|
|
||||||
"bigdecimal 0.4.2",
|
|
||||||
"bytes",
|
"bytes",
|
||||||
"chrono",
|
"chrono",
|
||||||
"dyn-clone",
|
"dyn-clone",
|
||||||
"either",
|
"either",
|
||||||
"enumset",
|
"enumset",
|
||||||
"indexmap 1.9.3",
|
"indexmap",
|
||||||
"indexmap 2.0.2",
|
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
|
"ref-cast",
|
||||||
"rust_decimal",
|
"rust_decimal",
|
||||||
"schemars_derive",
|
"schemars_derive",
|
||||||
"semver",
|
"semver",
|
||||||
|
@ -341,8 +324,7 @@ dependencies = [
|
||||||
"smol_str",
|
"smol_str",
|
||||||
"trybuild",
|
"trybuild",
|
||||||
"url",
|
"url",
|
||||||
"uuid 0.8.2",
|
"uuid",
|
||||||
"uuid 1.5.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -361,9 +343,6 @@ name = "semver"
|
||||||
version = "1.0.20"
|
version = "1.0.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
|
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
|
@ -415,18 +394,15 @@ checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smol_str"
|
name = "smol_str"
|
||||||
version = "0.1.24"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fad6c857cbab2627dcf01ec85a623ca4e7dcb5691cbaa3d7fb7653671f0d09c9"
|
checksum = "e6845563ada680337a52d43bb0b29f396f2d911616f6573012645b9e3d048a49"
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.38"
|
version = "2.0.60"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
|
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -504,12 +480,6 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "uuid"
|
|
||||||
version = "0.8.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
|
|
@ -15,117 +15,91 @@ rust-version = "1.60"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
schemars_derive = { version = "=0.8.19", optional = true, path = "../schemars_derive" }
|
schemars_derive = { version = "=0.8.19", optional = true, path = "../schemars_derive" }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = "1.0"
|
||||||
serde_json = "1.0.25"
|
serde_json = "1.0.25"
|
||||||
dyn-clone = "1.0"
|
dyn-clone = "1.0"
|
||||||
|
ref-cast = "1.0.22"
|
||||||
|
|
||||||
chrono = { version = "0.4", default-features = false, optional = true }
|
# optional dependencies
|
||||||
indexmap = { version = "1.2", features = ["serde-1"], optional = true }
|
chrono04 = { version = "0.4", default-features = false, optional = true, package = "chrono" }
|
||||||
indexmap2 = { version = "2.0", features = ["serde"], optional = true, package = "indexmap" }
|
indexmap2 = { version = "2.0", default-features = false, optional = true, package = "indexmap" }
|
||||||
either = { version = "1.3", default-features = false, optional = true }
|
either1 = { version = "1.3", default-features = false, optional = true, package = "either" }
|
||||||
uuid08 = { version = "0.8", default-features = false, optional = true, package = "uuid" }
|
|
||||||
uuid1 = { version = "1.0", default-features = false, optional = true, package = "uuid" }
|
uuid1 = { version = "1.0", default-features = false, optional = true, package = "uuid" }
|
||||||
smallvec = { version = "1.0", optional = true }
|
smallvec1 = { version = "1.0", default-features = false, optional = true, package = "smallvec" }
|
||||||
arrayvec05 = { version = "0.5", default-features = false, optional = true, package = "arrayvec" }
|
|
||||||
arrayvec07 = { version = "0.7", default-features = false, optional = true, package = "arrayvec" }
|
arrayvec07 = { version = "0.7", default-features = false, optional = true, package = "arrayvec" }
|
||||||
url = { version = "2.0", default-features = false, optional = true }
|
url2 = { version = "2.0", default-features = false, optional = true, package = "url" }
|
||||||
bytes = { version = "1.0", optional = true }
|
bytes1 = { version = "1.0", default-features = false, optional = true, package = "bytes" }
|
||||||
rust_decimal = { version = "1", default-features = false, optional = true }
|
rust_decimal1 = { version = "1", default-features = false, optional = true, package = "rust_decimal"}
|
||||||
bigdecimal03 = { version = "0.3", default-features = false, optional = true, package = "bigdecimal" }
|
|
||||||
bigdecimal04 = { version = "0.4", default-features = false, optional = true, package = "bigdecimal" }
|
bigdecimal04 = { version = "0.4", default-features = false, optional = true, package = "bigdecimal" }
|
||||||
enumset = { version = "1.0", optional = true }
|
enumset1 = { version = "1.0", default-features = false, optional = true, package = "enumset" }
|
||||||
smol_str = { version = "0.1.17", optional = true }
|
smol_str02 = { version = "0.2.1", default-features = false, optional = true, package = "smol_str" }
|
||||||
semver = { version = "1.0.9", features = ["serde"], optional = true }
|
semver1 = { version = "1.0.9", default-features = false, optional = true, package = "semver" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1.2.1"
|
pretty_assertions = "1.2.1"
|
||||||
trybuild = "1.0"
|
trybuild = "1.0"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["derive"]
|
default = ["derive"]
|
||||||
|
|
||||||
derive = ["schemars_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"]
|
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 = []
|
ui_test = []
|
||||||
|
|
||||||
[[test]]
|
|
||||||
name = "chrono"
|
|
||||||
required-features = ["chrono"]
|
|
||||||
|
|
||||||
[[test]]
|
|
||||||
name = "indexmap"
|
|
||||||
required-features = ["indexmap"]
|
|
||||||
|
|
||||||
[[test]]
|
|
||||||
name = "indexmap2"
|
|
||||||
required-features = ["indexmap2"]
|
|
||||||
|
|
||||||
[[test]]
|
|
||||||
name = "either"
|
|
||||||
required-features = ["either"]
|
|
||||||
|
|
||||||
[[test]]
|
|
||||||
name = "uuid"
|
|
||||||
required-features = ["uuid08", "uuid1"]
|
|
||||||
|
|
||||||
[[test]]
|
|
||||||
name = "smallvec"
|
|
||||||
required-features = ["smallvec"]
|
|
||||||
|
|
||||||
[[test]]
|
|
||||||
name = "bytes"
|
|
||||||
required-features = ["bytes"]
|
|
||||||
|
|
||||||
[[test]]
|
|
||||||
name = "arrayvec"
|
|
||||||
required-features = ["arrayvec05", "arrayvec07"]
|
|
||||||
|
|
||||||
[[test]]
|
|
||||||
name = "schema_for_schema"
|
|
||||||
required-features = ["impl_json_schema"]
|
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "ui"
|
name = "ui"
|
||||||
required-features = ["ui_test"]
|
required-features = ["ui_test"]
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "chrono"
|
||||||
|
required-features = ["chrono04"]
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "indexmap"
|
||||||
|
required-features = ["indexmap2"]
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "either"
|
||||||
|
required-features = ["either1"]
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "uuid"
|
||||||
|
required-features = ["uuid1"]
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "smallvec"
|
||||||
|
required-features = ["smallvec1"]
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "bytes"
|
||||||
|
required-features = ["bytes1"]
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "arrayvec"
|
||||||
|
required-features = ["arrayvec07"]
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "url"
|
name = "url"
|
||||||
required-features = ["url"]
|
required-features = ["url2"]
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "enumset"
|
name = "enumset"
|
||||||
required-features = ["enumset"]
|
required-features = ["enumset1"]
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "smol_str"
|
name = "smol_str"
|
||||||
required-features = ["smol_str"]
|
required-features = ["smol_str02"]
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
required-features = ["semver"]
|
required-features = ["semver1"]
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "decimal"
|
name = "decimal"
|
||||||
required-features = ["rust_decimal", "bigdecimal03", "bigdecimal04"]
|
required-features = ["rust_decimal1", "bigdecimal04"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use schemars::schema::{Schema, SchemaObject};
|
use schemars::Schema;
|
||||||
use schemars::{gen::SchemaGenerator, schema_for, JsonSchema};
|
use schemars::{gen::SchemaGenerator, schema_for, JsonSchema};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -21,9 +21,11 @@ pub struct MyStruct {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_custom_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn make_custom_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||||
let mut schema: SchemaObject = <String>::json_schema(gen).into();
|
let mut schema = String::json_schema(gen);
|
||||||
schema.format = Some("boolean".to_owned());
|
schema
|
||||||
schema.into()
|
.ensure_object()
|
||||||
|
.insert("format".into(), "boolean".into());
|
||||||
|
schema
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eight() -> i32 {
|
fn eight() -> i32 {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::gen::SchemaGenerator;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::schema::{InstanceType, ObjectValidation, Schema, SchemaObject};
|
use crate::JsonSchema;
|
||||||
use crate::{JsonSchema, Map, Set};
|
use crate::Schema;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use serde_json::Map;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
// Helper for generating schemas for flattened `Option` fields.
|
// Helper for generating schemas for flattened `Option` fields.
|
||||||
|
@ -12,12 +13,8 @@ pub fn json_schema_for_flatten<T: ?Sized + JsonSchema>(
|
||||||
let mut schema = T::_schemars_private_non_optional_json_schema(gen);
|
let mut schema = T::_schemars_private_non_optional_json_schema(gen);
|
||||||
|
|
||||||
if T::_schemars_private_is_option() && !required {
|
if T::_schemars_private_is_option() && !required {
|
||||||
if let Schema::Object(SchemaObject {
|
if let Some(object) = schema.as_object_mut() {
|
||||||
object: Some(ref mut object_validation),
|
object.remove("required");
|
||||||
..
|
|
||||||
}) = schema
|
|
||||||
{
|
|
||||||
object_validation.required.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,38 +54,22 @@ impl<T: Serialize> MaybeSerializeWrapper<T> {
|
||||||
|
|
||||||
/// Create a schema for a unit enum
|
/// Create a schema for a unit enum
|
||||||
pub fn new_unit_enum(variant: &str) -> Schema {
|
pub fn new_unit_enum(variant: &str) -> Schema {
|
||||||
Schema::Object(SchemaObject {
|
// TODO switch from single-valued "enum" to "const"
|
||||||
instance_type: Some(InstanceType::String.into()),
|
json_schema!({
|
||||||
enum_values: Some(vec![variant.into()]),
|
"type": "string",
|
||||||
..SchemaObject::default()
|
"enum": [variant],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a schema for an externally tagged enum
|
/// Create a schema for an externally tagged enum
|
||||||
pub fn new_externally_tagged_enum(variant: &str, sub_schema: Schema) -> Schema {
|
pub fn new_externally_tagged_enum(variant: &str, sub_schema: Schema) -> Schema {
|
||||||
Schema::Object(SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::Object.into()),
|
"type": "object",
|
||||||
object: Some(Box::new(ObjectValidation {
|
"properties": {
|
||||||
properties: {
|
variant: sub_schema
|
||||||
let mut props = Map::new();
|
|
||||||
props.insert(variant.to_owned(), sub_schema);
|
|
||||||
props
|
|
||||||
},
|
},
|
||||||
required: {
|
"required": [variant],
|
||||||
let mut required = Set::new();
|
"additionalProperties": false,
|
||||||
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()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,74 +79,87 @@ pub fn new_internally_tagged_enum(
|
||||||
variant: &str,
|
variant: &str,
|
||||||
deny_unknown_fields: bool,
|
deny_unknown_fields: bool,
|
||||||
) -> Schema {
|
) -> Schema {
|
||||||
let tag_schema = Schema::Object(SchemaObject {
|
// TODO switch from single-valued "enum" to "const"
|
||||||
instance_type: Some(InstanceType::String.into()),
|
let mut schema = json_schema!({
|
||||||
enum_values: Some(vec![variant.into()]),
|
"type": "object",
|
||||||
..Default::default()
|
"properties": {
|
||||||
|
tag_name: {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [variant],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [tag_name],
|
||||||
});
|
});
|
||||||
Schema::Object(SchemaObject {
|
|
||||||
instance_type: Some(InstanceType::Object.into()),
|
if deny_unknown_fields {
|
||||||
object: Some(Box::new(ObjectValidation {
|
schema
|
||||||
properties: {
|
.as_object_mut()
|
||||||
let mut props = Map::new();
|
.unwrap()
|
||||||
props.insert(tag_name.to_owned(), tag_schema);
|
.insert("additionalProperties".into(), false.into());
|
||||||
props
|
}
|
||||||
},
|
|
||||||
required: {
|
schema
|
||||||
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()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_object_property<T: ?Sized + JsonSchema>(
|
pub fn insert_object_property<T: ?Sized + JsonSchema>(
|
||||||
obj: &mut ObjectValidation,
|
schema: &mut Schema,
|
||||||
key: &str,
|
key: &str,
|
||||||
has_default: bool,
|
has_default: bool,
|
||||||
required: 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 required || !(has_default || T::_schemars_private_is_option()) {
|
if required || !(has_default || 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 {
|
pub fn insert_validation_property(
|
||||||
use crate::Schema;
|
schema: &mut Schema,
|
||||||
use serde_json::Value;
|
required_type: &str,
|
||||||
|
key: &str,
|
||||||
macro_rules! add_metadata_fn {
|
value: impl Into<Value>,
|
||||||
($method:ident, $name:ident, $ty:ty) => {
|
) {
|
||||||
pub fn $method(schema: Schema, $name: impl Into<$ty>) -> Schema {
|
if schema.has_type(required_type) || (required_type == "number" && schema.has_type("integer")) {
|
||||||
let value = $name.into();
|
schema.ensure_object().insert(key.to_owned(), value.into());
|
||||||
if value == <$ty>::default() {
|
|
||||||
schema
|
|
||||||
} else {
|
|
||||||
let mut schema_obj = schema.into_object();
|
|
||||||
schema_obj.metadata().$name = value.into();
|
|
||||||
Schema::Object(schema_obj)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
add_metadata_fn!(add_description, description, String);
|
pub fn append_required(schema: &mut Schema, key: &str) {
|
||||||
add_metadata_fn!(add_id, id, String);
|
if schema.has_type("object") {
|
||||||
add_metadata_fn!(add_title, title, String);
|
if let Value::Array(array) = schema
|
||||||
add_metadata_fn!(add_deprecated, deprecated, bool);
|
.ensure_object()
|
||||||
add_metadata_fn!(add_read_only, read_only, bool);
|
.entry("required")
|
||||||
add_metadata_fn!(add_write_only, write_only, bool);
|
.or_insert(Value::Array(Vec::new()))
|
||||||
add_metadata_fn!(add_default, default, Value);
|
{
|
||||||
|
let value = Value::from(key);
|
||||||
|
if !array.contains(&value) {
|
||||||
|
array.push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_examples<I: IntoIterator<Item = Value>>(schema: Schema, examples: I) -> Schema {
|
pub fn apply_inner_validation(schema: &mut Schema, f: fn(&mut Schema) -> ()) {
|
||||||
let mut schema_obj = schema.into_object();
|
if let Some(inner_schema) = schema
|
||||||
schema_obj.metadata().examples.extend(examples);
|
.as_object_mut()
|
||||||
Schema::Object(schema_obj)
|
.and_then(|o| o.get_mut("items"))
|
||||||
|
.and_then(|i| <&mut Schema>::try_from(i).ok())
|
||||||
|
{
|
||||||
|
f(inner_schema);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,180 +1,111 @@
|
||||||
use crate::schema::*;
|
use serde_json::map::Entry;
|
||||||
use crate::{Map, Set};
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use crate::Schema;
|
||||||
|
|
||||||
impl Schema {
|
impl Schema {
|
||||||
/// This function is only public for use by schemars_derive.
|
/// This function is only public for use by schemars_derive.
|
||||||
///
|
///
|
||||||
/// It should not be considered part of the public API.
|
/// It should not be considered part of the public API.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn flatten(self, other: Self) -> Schema {
|
pub fn flatten(mut self, other: Self) -> Schema {
|
||||||
if is_null_type(&self) {
|
// This special null-type-schema handling is here for backward-compatibility, but needs reviewing.
|
||||||
return other;
|
// I think it's only needed to make internally-tagged enum unit variants behave correctly, but that
|
||||||
} else if is_null_type(&other) {
|
// should be handled entirely within schemars_derive.
|
||||||
return self;
|
if other
|
||||||
}
|
.as_object()
|
||||||
let s1: SchemaObject = self.into();
|
.and_then(|o| o.get("type"))
|
||||||
let s2: SchemaObject = other.into();
|
.and_then(|t| t.as_str())
|
||||||
Schema::Object(s1.merge(s2))
|
== Some("null")
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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),)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($ty:ident { or: $($or_field:ident)*, }) => {
|
|
||||||
impl_merge!( $ty { merge: , or: $($or_field)*, });
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// For ObjectValidation::additional_properties.
|
|
||||||
impl Merge for Option<Box<Schema>> {
|
|
||||||
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())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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<T: Merge> Merge for Option<T> {
|
|
||||||
fn merge(self, other: Self) -> Self {
|
|
||||||
match (self, other) {
|
|
||||||
(Some(x), Some(y)) => Some(x.merge(y)),
|
|
||||||
(None, y) => y,
|
|
||||||
(x, None) => x,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Merge> Merge for Box<T> {
|
|
||||||
fn merge(mut self, other: Self) -> Self {
|
|
||||||
*self = (*self).merge(*other);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Merge for Vec<T> {
|
|
||||||
fn merge(mut self, other: Self) -> Self {
|
|
||||||
self.extend(other);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> Merge for Map<K, V>
|
|
||||||
where
|
|
||||||
K: std::hash::Hash + Eq + Ord,
|
|
||||||
{
|
{
|
||||||
fn merge(mut self, other: Self) -> Self {
|
|
||||||
self.extend(other);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Ord> Merge for Set<T> {
|
|
||||||
fn merge(mut self, other: Self) -> Self {
|
|
||||||
self.extend(other);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Merge for SingleOrVec<InstanceType> {
|
|
||||||
fn merge(self, other: Self) -> Self {
|
|
||||||
if self == other {
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
let mut vec = match (self, other) {
|
|
||||||
(SingleOrVec::Vec(v1), SingleOrVec::Vec(v2)) => v1.merge(v2),
|
if let Value::Object(mut obj2) = other.to_value() {
|
||||||
(SingleOrVec::Vec(mut v), SingleOrVec::Single(s))
|
let obj1 = self.ensure_object();
|
||||||
| (SingleOrVec::Single(s), SingleOrVec::Vec(mut v)) => {
|
|
||||||
v.push(*s);
|
let ap2 = obj2.remove("additionalProperties");
|
||||||
v
|
if let Entry::Occupied(mut ap1) = obj1.entry("additionalProperties") {
|
||||||
|
match ap2 {
|
||||||
|
Some(ap2) => {
|
||||||
|
flatten_additional_properties(ap1.get_mut(), ap2);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
ap1.remove();
|
||||||
}
|
}
|
||||||
(SingleOrVec::Single(s1), SingleOrVec::Single(s2)) => vec![*s1, *s2],
|
|
||||||
};
|
|
||||||
vec.sort();
|
|
||||||
vec.dedup();
|
|
||||||
SingleOrVec::Vec(vec)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_null_type(schema: &Schema) -> bool {
|
for (key, value2) in obj2 {
|
||||||
let s = match schema {
|
match obj1.entry(key) {
|
||||||
Schema::Object(s) => s,
|
Entry::Vacant(vacant) => {
|
||||||
_ => return false,
|
vacant.insert(value2);
|
||||||
};
|
}
|
||||||
let instance_type = match &s.instance_type {
|
Entry::Occupied(mut occupied) => {
|
||||||
Some(SingleOrVec::Single(t)) => t,
|
match occupied.key().as_str() {
|
||||||
_ => return false,
|
// 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)) => {
|
||||||
**instance_type == InstanceType::Null
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,12 @@ There are two main types in this module:
|
||||||
* [`SchemaGenerator`], which manages the generation of a schema document.
|
* [`SchemaGenerator`], which manages the generation of a schema document.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::schema::*;
|
use crate::Schema;
|
||||||
use crate::{visit::*, JsonSchema, Map};
|
use crate::{visit::*, JsonSchema};
|
||||||
use dyn_clone::DynClone;
|
use dyn_clone::DynClone;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
use std::collections::{BTreeMap, HashMap};
|
||||||
use std::{any::Any, collections::HashSet, fmt::Debug};
|
use std::{any::Any, collections::HashSet, fmt::Debug};
|
||||||
|
|
||||||
/// Settings to customize how Schemas are generated.
|
/// Settings to customize how Schemas are generated.
|
||||||
|
@ -76,7 +76,7 @@ impl SchemaSettings {
|
||||||
option_add_null_type: true,
|
option_add_null_type: true,
|
||||||
definitions_path: "#/definitions/".to_owned(),
|
definitions_path: "#/definitions/".to_owned(),
|
||||||
meta_schema: Some("https://json-schema.org/draft/2019-09/schema".to_owned()),
|
meta_schema: Some("https://json-schema.org/draft/2019-09/schema".to_owned()),
|
||||||
visitors: Vec::default(),
|
visitors: Vec::new(),
|
||||||
inline_subschemas: false,
|
inline_subschemas: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,9 +96,7 @@ impl SchemaSettings {
|
||||||
Box::new(ReplaceBoolSchemas {
|
Box::new(ReplaceBoolSchemas {
|
||||||
skip_additional_properties: true,
|
skip_additional_properties: true,
|
||||||
}),
|
}),
|
||||||
Box::new(SetSingleExample {
|
Box::new(SetSingleExample),
|
||||||
retain_examples: false,
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
inline_subschemas: false,
|
inline_subschemas: false,
|
||||||
}
|
}
|
||||||
|
@ -150,7 +148,7 @@ impl SchemaSettings {
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct SchemaGenerator {
|
pub struct SchemaGenerator {
|
||||||
settings: SchemaSettings,
|
settings: SchemaSettings,
|
||||||
definitions: Map<String, Schema>,
|
definitions: BTreeMap<String, Schema>,
|
||||||
pending_schema_ids: HashSet<Cow<'static, str>>,
|
pending_schema_ids: HashSet<Cow<'static, str>>,
|
||||||
schema_id_to_name: HashMap<Cow<'static, str>, String>,
|
schema_id_to_name: HashMap<Cow<'static, str>, String>,
|
||||||
used_schema_names: HashSet<String>,
|
used_schema_names: HashSet<String>,
|
||||||
|
@ -198,19 +196,6 @@ impl SchemaGenerator {
|
||||||
&self.settings
|
&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.
|
/// 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 [referenceable](JsonSchema::is_referenceable), this will add `T`'s schema to this generator's definitions, and
|
||||||
|
@ -262,7 +247,7 @@ impl SchemaGenerator {
|
||||||
name: String,
|
name: String,
|
||||||
id: Cow<'static, str>,
|
id: Cow<'static, str>,
|
||||||
) {
|
) {
|
||||||
let dummy = Schema::Bool(false);
|
let dummy = false.into();
|
||||||
// insert into definitions BEFORE calling json_schema to avoid infinite recursion
|
// insert into definitions BEFORE calling json_schema to avoid infinite recursion
|
||||||
self.definitions.insert(name.clone(), dummy);
|
self.definitions.insert(name.clone(), dummy);
|
||||||
|
|
||||||
|
@ -273,26 +258,26 @@ impl SchemaGenerator {
|
||||||
|
|
||||||
/// Borrows the collection of all [referenceable](JsonSchema::is_referenceable) schemas that have been generated.
|
/// 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.
|
/// themselves.
|
||||||
pub fn definitions(&self) -> &Map<String, Schema> {
|
pub fn definitions(&self) -> &BTreeMap<String, Schema> {
|
||||||
&self.definitions
|
&self.definitions
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutably borrows the collection of all [referenceable](JsonSchema::is_referenceable) schemas that have been generated.
|
/// 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.
|
/// themselves.
|
||||||
pub fn definitions_mut(&mut self) -> &mut Map<String, Schema> {
|
pub fn definitions_mut(&mut self) -> &mut BTreeMap<String, Schema> {
|
||||||
&mut self.definitions
|
&mut self.definitions
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the collection of all [referenceable](JsonSchema::is_referenceable) schemas that have been generated,
|
/// Returns the collection of all [referenceable](JsonSchema::is_referenceable) 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
|
/// The keys of the returned `BTreeMap` are the [schema names](JsonSchema::schema_name), and the values are the schemas
|
||||||
/// themselves.
|
/// themselves.
|
||||||
pub fn take_definitions(&mut self) -> Map<String, Schema> {
|
pub fn take_definitions(&mut self) -> BTreeMap<String, Schema> {
|
||||||
std::mem::take(&mut self.definitions)
|
std::mem::take(&mut self.definitions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,40 +291,72 @@ impl SchemaGenerator {
|
||||||
/// If `T`'s schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will
|
/// If `T`'s schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will
|
||||||
/// add them to the `SchemaGenerator`'s schema definitions and include them in the returned `SchemaObject`'s
|
/// add them to the `SchemaGenerator`'s schema definitions and include them in the returned `SchemaObject`'s
|
||||||
/// [`definitions`](../schema/struct.Metadata.html#structfield.definitions)
|
/// [`definitions`](../schema/struct.Metadata.html#structfield.definitions)
|
||||||
pub fn root_schema_for<T: ?Sized + JsonSchema>(&mut self) -> RootSchema {
|
pub fn root_schema_for<T: ?Sized + JsonSchema>(&mut self) -> Schema {
|
||||||
let mut schema = self.json_schema_internal::<T>(T::schema_id()).into_object();
|
let mut schema = self.json_schema_internal::<T>(T::schema_id());
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
for visitor in &mut self.settings.visitors {
|
let object = schema.ensure_object();
|
||||||
visitor.visit_root_schema(&mut root)
|
|
||||||
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
root
|
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_schema(&mut schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
schema
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consumes `self` and generates a root JSON Schema for the type `T`.
|
/// Consumes `self` and generates a root JSON Schema for the type `T`.
|
||||||
///
|
///
|
||||||
/// If `T`'s schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will
|
/// If `T`'s schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will
|
||||||
/// include them in the returned `SchemaObject`'s [`definitions`](../schema/struct.Metadata.html#structfield.definitions)
|
/// include them in the returned `SchemaObject`'s [`definitions`](../schema/struct.Metadata.html#structfield.definitions)
|
||||||
pub fn into_root_schema_for<T: ?Sized + JsonSchema>(mut self) -> RootSchema {
|
pub fn into_root_schema_for<T: ?Sized + JsonSchema>(mut self) -> Schema {
|
||||||
let mut schema = self.json_schema_internal::<T>(T::schema_id()).into_object();
|
let mut schema = self.json_schema_internal::<T>(T::schema_id());
|
||||||
schema.metadata().title.get_or_insert_with(T::schema_name);
|
|
||||||
let mut root = RootSchema {
|
|
||||||
meta_schema: self.settings.meta_schema,
|
|
||||||
definitions: self.definitions,
|
|
||||||
schema,
|
|
||||||
};
|
|
||||||
|
|
||||||
for visitor in &mut self.settings.visitors {
|
let object = schema.ensure_object();
|
||||||
visitor.visit_root_schema(&mut root)
|
|
||||||
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
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(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for visitor in &mut self.settings.visitors {
|
||||||
|
visitor.visit_schema(&mut schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
schema
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a root JSON Schema for the given example value.
|
/// Generates a root JSON Schema for the given example value.
|
||||||
|
@ -349,29 +366,39 @@ impl SchemaGenerator {
|
||||||
pub fn root_schema_for_value<T: ?Sized + Serialize>(
|
pub fn root_schema_for_value<T: ?Sized + Serialize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
value: &T,
|
value: &T,
|
||||||
) -> Result<RootSchema, serde_json::Error> {
|
) -> Result<Schema, serde_json::Error> {
|
||||||
let mut schema = value
|
let mut schema = value.serialize(crate::ser::Serializer {
|
||||||
.serialize(crate::ser::Serializer {
|
|
||||||
gen: self,
|
gen: self,
|
||||||
include_title: true,
|
include_title: true,
|
||||||
})?
|
})?;
|
||||||
.into_object();
|
|
||||||
|
let object = schema.ensure_object();
|
||||||
|
|
||||||
if let Ok(example) = serde_json::to_value(value) {
|
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 {
|
if let Some(meta_schema) = self.settings.meta_schema.as_deref() {
|
||||||
meta_schema: self.settings.meta_schema.clone(),
|
object.insert("$schema".into(), meta_schema.into());
|
||||||
definitions: self.definitions.clone(),
|
}
|
||||||
schema,
|
|
||||||
};
|
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 {
|
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.
|
/// Consumes `self` and generates a root JSON Schema for the given example value.
|
||||||
|
@ -381,72 +408,39 @@ impl SchemaGenerator {
|
||||||
pub fn into_root_schema_for_value<T: ?Sized + Serialize>(
|
pub fn into_root_schema_for_value<T: ?Sized + Serialize>(
|
||||||
mut self,
|
mut self,
|
||||||
value: &T,
|
value: &T,
|
||||||
) -> Result<RootSchema, serde_json::Error> {
|
) -> Result<Schema, serde_json::Error> {
|
||||||
let mut schema = value
|
let mut schema = value.serialize(crate::ser::Serializer {
|
||||||
.serialize(crate::ser::Serializer {
|
|
||||||
gen: &mut self,
|
gen: &mut self,
|
||||||
include_title: true,
|
include_title: true,
|
||||||
})?
|
})?;
|
||||||
.into_object();
|
|
||||||
|
let object = schema.ensure_object();
|
||||||
|
|
||||||
if let Ok(example) = serde_json::to_value(value) {
|
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 {
|
if let Some(meta_schema) = self.settings.meta_schema {
|
||||||
meta_schema: self.settings.meta_schema,
|
object.insert("$schema".into(), meta_schema.into());
|
||||||
definitions: self.definitions,
|
}
|
||||||
schema,
|
|
||||||
};
|
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 {
|
for visitor in &mut self.settings.visitors {
|
||||||
visitor.visit_root_schema(&mut root)
|
visitor.visit_schema(&mut schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(root)
|
Ok(schema)
|
||||||
}
|
|
||||||
|
|
||||||
/// 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
|
|
||||||
/// ```
|
|
||||||
/// use schemars::{JsonSchema, gen::SchemaGenerator};
|
|
||||||
///
|
|
||||||
/// #[derive(JsonSchema)]
|
|
||||||
/// struct MyStruct {
|
|
||||||
/// foo: i32,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// let mut gen = SchemaGenerator::default();
|
|
||||||
/// let ref_schema = gen.subschema_for::<MyStruct>();
|
|
||||||
///
|
|
||||||
/// 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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema_internal<T: ?Sized + JsonSchema>(&mut self, id: Cow<'static, str>) -> Schema {
|
fn json_schema_internal<T: ?Sized + JsonSchema>(&mut self, id: Cow<'static, str>) -> Schema {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::gen::SchemaGenerator;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::schema::*;
|
use crate::{json_schema, JsonSchema, Schema};
|
||||||
use crate::JsonSchema;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
// Does not require T: JsonSchema.
|
// Does not require T: JsonSchema.
|
||||||
|
@ -16,15 +15,10 @@ impl<T> JsonSchema for [T; 0] {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||||
SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::Array.into()),
|
"type": "array",
|
||||||
array: Some(Box::new(ArrayValidation {
|
"maxItems": 0,
|
||||||
max_items: Some(0),
|
})
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,17 +38,12 @@ macro_rules! array_impls {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||||
SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::Array.into()),
|
"type": "array",
|
||||||
array: Some(Box::new(ArrayValidation {
|
"items": serde_json::Value::from(gen.subschema_for::<T>()),
|
||||||
items: Some(gen.subschema_for::<T>().into()),
|
"minItems": $len,
|
||||||
max_items: Some($len),
|
"maxItems": $len,
|
||||||
min_items: Some($len),
|
})
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)+
|
)+
|
||||||
|
@ -67,40 +56,3 @@ array_impls! {
|
||||||
21 22 23 24 25 26 27 28 29 30
|
21 22 23 24 25 26 27 28 29 30
|
||||||
31 32
|
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::<i32>()))
|
|
||||||
);
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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!((<A> JsonSchema for ArrayString<A> where A: Array<Item = u8> + Copy) => String);
|
|
||||||
|
|
||||||
impl<A: Array> JsonSchema for ArrayVec<A>
|
|
||||||
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::<A::Item>().into()),
|
|
||||||
max_items: A::CAPACITY.try_into().ok(),
|
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,6 @@
|
||||||
use crate::gen::SchemaGenerator;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::schema::*;
|
use crate::{json_schema, JsonSchema, Schema};
|
||||||
use crate::JsonSchema;
|
|
||||||
use arrayvec07::{ArrayString, ArrayVec};
|
use arrayvec07::{ArrayString, ArrayVec};
|
||||||
use std::convert::TryInto;
|
|
||||||
|
|
||||||
// Do not set maxLength on the schema as that describes length in characters, but we only
|
// Do not set maxLength on the schema as that describes length in characters, but we only
|
||||||
// know max length in bytes.
|
// know max length in bytes.
|
||||||
|
@ -19,15 +17,10 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||||
SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::Array.into()),
|
"type": "array",
|
||||||
array: Some(Box::new(ArrayValidation {
|
"items": gen.subschema_for::<T>(),
|
||||||
items: Some(gen.subschema_for::<T>().into()),
|
"maxItems": CAP
|
||||||
max_items: CAP.try_into().ok(),
|
})
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
use crate::gen::SchemaGenerator;
|
|
||||||
use crate::schema::*;
|
|
||||||
use crate::JsonSchema;
|
|
||||||
use std::sync::atomic::*;
|
use std::sync::atomic::*;
|
||||||
|
|
||||||
forward_impl!(AtomicBool => bool);
|
forward_impl!(AtomicBool => bool);
|
||||||
|
@ -22,12 +19,12 @@ forward_impl!(AtomicUsize => usize);
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::tests::schema_object_for;
|
use crate::schema_for;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn schema_for_atomics() {
|
fn schema_for_atomics() {
|
||||||
let atomic_schema = schema_object_for::<(
|
let atomic_schema = schema_for!((
|
||||||
AtomicBool,
|
AtomicBool,
|
||||||
AtomicI8,
|
AtomicI8,
|
||||||
AtomicI16,
|
AtomicI16,
|
||||||
|
@ -39,9 +36,8 @@ mod tests {
|
||||||
AtomicU32,
|
AtomicU32,
|
||||||
AtomicU64,
|
AtomicU64,
|
||||||
AtomicUsize,
|
AtomicUsize,
|
||||||
)>();
|
));
|
||||||
let basic_schema =
|
let basic_schema = schema_for!((bool, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize));
|
||||||
schema_object_for::<(bool, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize)>();
|
|
||||||
assert_eq!(atomic_schema, basic_schema);
|
assert_eq!(atomic_schema, basic_schema);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
use crate::gen::SchemaGenerator;
|
|
||||||
use crate::schema::*;
|
|
||||||
use crate::JsonSchema;
|
|
||||||
use bytes::{Bytes, BytesMut};
|
|
||||||
|
|
||||||
forward_impl!((JsonSchema for Bytes) => Vec<u8>);
|
|
||||||
forward_impl!((JsonSchema for BytesMut) => Vec<u8>);
|
|
|
@ -1,8 +1,6 @@
|
||||||
use crate::gen::SchemaGenerator;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::schema::*;
|
use crate::{json_schema, JsonSchema, Schema};
|
||||||
use crate::JsonSchema;
|
use chrono04::prelude::*;
|
||||||
use chrono::prelude::*;
|
|
||||||
use serde_json::json;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
impl JsonSchema for Weekday {
|
impl JsonSchema for Weekday {
|
||||||
|
@ -17,20 +15,18 @@ impl JsonSchema for Weekday {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||||
SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::String.into()),
|
"type": "string",
|
||||||
enum_values: Some(vec![
|
"enum": [
|
||||||
json!("Mon"),
|
"Mon",
|
||||||
json!("Tue"),
|
"Tue",
|
||||||
json!("Wed"),
|
"Wed",
|
||||||
json!("Thu"),
|
"Thu",
|
||||||
json!("Fri"),
|
"Fri",
|
||||||
json!("Sat"),
|
"Sat",
|
||||||
json!("Sun"),
|
"Sun",
|
||||||
]),
|
]
|
||||||
..Default::default()
|
})
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,12 +47,10 @@ macro_rules! formatted_string_impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||||
SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::String.into()),
|
"type": "string",
|
||||||
format: Some($format.to_owned()),
|
"format": $format
|
||||||
..Default::default()
|
})
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::gen::SchemaGenerator;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::schema::*;
|
use crate::{json_schema, JsonSchema, Schema};
|
||||||
use crate::JsonSchema;
|
use serde_json::Value;
|
||||||
use serde_json::json;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::ops::{Bound, Range, RangeInclusive};
|
use std::ops::{Bound, Range, RangeInclusive};
|
||||||
|
|
||||||
|
@ -18,35 +17,45 @@ impl<T: JsonSchema> JsonSchema for Option<T> {
|
||||||
|
|
||||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||||
let mut schema = gen.subschema_for::<T>();
|
let mut schema = gen.subschema_for::<T>();
|
||||||
|
|
||||||
if gen.settings().option_add_null_type {
|
if gen.settings().option_add_null_type {
|
||||||
schema = match schema {
|
schema = match schema.try_to_object() {
|
||||||
Schema::Bool(true) => Schema::Bool(true),
|
Ok(mut obj) => {
|
||||||
Schema::Bool(false) => <()>::json_schema(gen),
|
let instance_type = obj.get_mut("type");
|
||||||
Schema::Object(SchemaObject {
|
match instance_type {
|
||||||
instance_type: Some(ref mut instance_type),
|
Some(Value::Array(array)) => {
|
||||||
..
|
let null = Value::from("null");
|
||||||
}) => {
|
if !array.contains(&null) {
|
||||||
add_null_type(instance_type);
|
array.push(null);
|
||||||
schema
|
|
||||||
}
|
}
|
||||||
schema => SchemaObject {
|
obj.into()
|
||||||
// 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()
|
|
||||||
}
|
}
|
||||||
.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)
|
||||||
|
]
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Err(true) => true.into(),
|
||||||
|
Err(false) => <()>::json_schema(gen),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if gen.settings().option_nullable {
|
if gen.settings().option_nullable {
|
||||||
let mut schema_obj = schema.into_object();
|
schema
|
||||||
schema_obj
|
.ensure_object()
|
||||||
.extensions
|
.insert("nullable".into(), true.into());
|
||||||
.insert("nullable".to_owned(), json!(true));
|
|
||||||
schema = Schema::Object(schema_obj);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
schema
|
schema
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,16 +68,6 @@ impl<T: JsonSchema> JsonSchema for Option<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_null_type(instance_type: &mut SingleOrVec<InstanceType>) {
|
|
||||||
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<T: JsonSchema, E: JsonSchema> JsonSchema for Result<T, E> {
|
impl<T: JsonSchema, E: JsonSchema> JsonSchema for Result<T, E> {
|
||||||
fn schema_name() -> String {
|
fn schema_name() -> String {
|
||||||
format!("Result_of_{}_or_{}", T::schema_name(), E::schema_name())
|
format!("Result_of_{}_or_{}", T::schema_name(), E::schema_name())
|
||||||
|
@ -79,27 +78,24 @@ impl<T: JsonSchema, E: JsonSchema> JsonSchema for Result<T, E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||||
let mut ok_schema = SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::Object.into()),
|
"oneOf": [
|
||||||
..Default::default()
|
{
|
||||||
};
|
"type": "object",
|
||||||
let obj = ok_schema.object();
|
"properties": {
|
||||||
obj.required.insert("Ok".to_owned());
|
"Ok": gen.subschema_for::<T>()
|
||||||
obj.properties
|
},
|
||||||
.insert("Ok".to_owned(), gen.subschema_for::<T>());
|
"required": ["Ok"]
|
||||||
|
},
|
||||||
let mut err_schema = SchemaObject {
|
{
|
||||||
instance_type: Some(InstanceType::Object.into()),
|
"type": "object",
|
||||||
..Default::default()
|
"properties": {
|
||||||
};
|
"Err": gen.subschema_for::<E>()
|
||||||
let obj = err_schema.object();
|
},
|
||||||
obj.required.insert("Err".to_owned());
|
"required": ["Err"]
|
||||||
obj.properties
|
}
|
||||||
.insert("Err".to_owned(), gen.subschema_for::<E>());
|
]
|
||||||
|
})
|
||||||
let mut schema = SchemaObject::default();
|
|
||||||
schema.subschemas().one_of = Some(vec![ok_schema.into(), err_schema.into()]);
|
|
||||||
schema.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,37 +109,28 @@ impl<T: JsonSchema> JsonSchema for Bound<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||||
let mut included_schema = SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::Object.into()),
|
"oneOf": [
|
||||||
..Default::default()
|
{
|
||||||
};
|
"type": "object",
|
||||||
let obj = included_schema.object();
|
"properties": {
|
||||||
obj.required.insert("Included".to_owned());
|
"Included": gen.subschema_for::<T>()
|
||||||
obj.properties
|
},
|
||||||
.insert("Included".to_owned(), gen.subschema_for::<T>());
|
"required": ["Included"]
|
||||||
|
},
|
||||||
let mut excluded_schema = SchemaObject {
|
{
|
||||||
instance_type: Some(InstanceType::Object.into()),
|
"type": "object",
|
||||||
..Default::default()
|
"properties": {
|
||||||
};
|
"Excluded": gen.subschema_for::<T>()
|
||||||
let obj = excluded_schema.object();
|
},
|
||||||
obj.required.insert("Excluded".to_owned());
|
"required": ["Excluded"]
|
||||||
obj.properties
|
},
|
||||||
.insert("Excluded".to_owned(), gen.subschema_for::<T>());
|
{
|
||||||
|
"type": "string",
|
||||||
let unbounded_schema = SchemaObject {
|
"const": "Unbounded"
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,18 +144,15 @@ impl<T: JsonSchema> JsonSchema for Range<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||||
let mut schema = SchemaObject {
|
let subschema = gen.subschema_for::<T>();
|
||||||
instance_type: Some(InstanceType::Object.into()),
|
json_schema!({
|
||||||
..Default::default()
|
"type": "object",
|
||||||
};
|
"properties": {
|
||||||
let obj = schema.object();
|
"start": subschema,
|
||||||
obj.required.insert("start".to_owned());
|
"end": subschema
|
||||||
obj.required.insert("end".to_owned());
|
},
|
||||||
obj.properties
|
"required": ["start", "end"]
|
||||||
.insert("start".to_owned(), gen.subschema_for::<T>());
|
})
|
||||||
obj.properties
|
|
||||||
.insert("end".to_owned(), gen.subschema_for::<T>());
|
|
||||||
schema.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,54 +161,3 @@ forward_impl!((<T: JsonSchema> JsonSchema for RangeInclusive<T>) => Range<T>);
|
||||||
forward_impl!((<T: ?Sized> JsonSchema for std::marker::PhantomData<T>) => ());
|
forward_impl!((<T: ?Sized> JsonSchema for std::marker::PhantomData<T>) => ());
|
||||||
|
|
||||||
forward_impl!((<'a> JsonSchema for std::fmt::Arguments<'a>) => String);
|
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::<Option<i32>>();
|
|
||||||
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::<Option<Foo>>();
|
|
||||||
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::<Result<bool, String>>();
|
|
||||||
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::<bool>());
|
|
||||||
|
|
||||||
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::<String>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::gen::SchemaGenerator;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::schema::*;
|
use crate::{json_schema, JsonSchema, Schema};
|
||||||
use crate::JsonSchema;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
macro_rules! decimal_impl {
|
macro_rules! decimal_impl {
|
||||||
|
@ -17,23 +16,16 @@ macro_rules! decimal_impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||||
SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::String.into()),
|
"type": "string",
|
||||||
string: Some(Box::new(StringValidation {
|
"pattern": r"^-?[0-9]+(\.[0-9]+)?$",
|
||||||
pattern: Some(r"^-?[0-9]+(\.[0-9]+)?$".to_owned()),
|
})
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rust_decimal")]
|
#[cfg(feature = "rust_decimal1")]
|
||||||
decimal_impl!(rust_decimal::Decimal);
|
decimal_impl!(rust_decimal1::Decimal);
|
||||||
#[cfg(feature = "bigdecimal03")]
|
|
||||||
decimal_impl!(bigdecimal03::BigDecimal);
|
|
||||||
#[cfg(feature = "bigdecimal04")]
|
#[cfg(feature = "bigdecimal04")]
|
||||||
decimal_impl!(bigdecimal04::BigDecimal);
|
decimal_impl!(bigdecimal04::BigDecimal);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::gen::SchemaGenerator;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::schema::*;
|
use crate::{json_schema, JsonSchema, Schema};
|
||||||
use crate::JsonSchema;
|
use either1::Either;
|
||||||
use either::Either;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
impl<L: JsonSchema, R: JsonSchema> JsonSchema for Either<L, R> {
|
impl<L: JsonSchema, R: JsonSchema> JsonSchema for Either<L, R> {
|
||||||
|
@ -20,8 +19,8 @@ impl<L: JsonSchema, R: JsonSchema> JsonSchema for Either<L, R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||||
let mut schema = SchemaObject::default();
|
json_schema!({
|
||||||
schema.subschemas().any_of = Some(vec![gen.subschema_for::<L>(), gen.subschema_for::<R>()]);
|
"anyOf": [gen.subschema_for::<L>(), gen.subschema_for::<R>()],
|
||||||
schema.into()
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +0,0 @@
|
||||||
use crate::gen::SchemaGenerator;
|
|
||||||
use crate::schema::*;
|
|
||||||
use crate::JsonSchema;
|
|
||||||
use enumset::{EnumSet, EnumSetType};
|
|
||||||
|
|
||||||
forward_impl!((<T> JsonSchema for EnumSet<T> where T: EnumSetType + JsonSchema) => std::collections::BTreeSet<T>);
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::gen::SchemaGenerator;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::schema::*;
|
use crate::{json_schema, JsonSchema, Schema};
|
||||||
use crate::JsonSchema;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::ffi::{CStr, CString, OsStr, OsString};
|
use std::ffi::{CStr, CString, OsStr, OsString};
|
||||||
|
|
||||||
|
@ -14,27 +13,24 @@ impl JsonSchema for OsString {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||||
let mut unix_schema = SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::Object.into()),
|
"oneOf": [
|
||||||
..Default::default()
|
{
|
||||||
};
|
"type": "object",
|
||||||
let obj = unix_schema.object();
|
"properties": {
|
||||||
obj.required.insert("Unix".to_owned());
|
"Unix": <Vec<u8>>::json_schema(gen)
|
||||||
obj.properties
|
},
|
||||||
.insert("Unix".to_owned(), <Vec<u8>>::json_schema(gen));
|
"required": ["Unix"]
|
||||||
|
},
|
||||||
let mut win_schema = SchemaObject {
|
{
|
||||||
instance_type: Some(InstanceType::Object.into()),
|
"type": "object",
|
||||||
..Default::default()
|
"properties": {
|
||||||
};
|
"Windows": <Vec<u16>>::json_schema(gen)
|
||||||
let obj = win_schema.object();
|
},
|
||||||
obj.required.insert("Windows".to_owned());
|
"required": ["Windows"]
|
||||||
obj.properties
|
},
|
||||||
.insert("Windows".to_owned(), <Vec<u16>>::json_schema(gen));
|
]
|
||||||
|
})
|
||||||
let mut schema = SchemaObject::default();
|
|
||||||
schema.subschemas().one_of = Some(vec![unix_schema.into(), win_schema.into()]);
|
|
||||||
schema.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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!((<K, V: JsonSchema, H> JsonSchema for IndexMap<K, V, H>) => HashMap<K, V, H>);
|
|
||||||
forward_impl!((<T: JsonSchema, H> JsonSchema for IndexSet<T, H>) => HashSet<T, H>);
|
|
|
@ -1,5 +1,3 @@
|
||||||
use crate::gen::SchemaGenerator;
|
|
||||||
use crate::schema::*;
|
|
||||||
use crate::JsonSchema;
|
use crate::JsonSchema;
|
||||||
use indexmap2::{IndexMap, IndexSet};
|
use indexmap2::{IndexMap, IndexSet};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::gen::SchemaGenerator;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::schema::*;
|
use crate::{json_schema, JsonSchema, Schema};
|
||||||
use crate::JsonSchema;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
macro_rules! map_impl {
|
macro_rules! map_impl {
|
||||||
|
@ -20,16 +19,10 @@ macro_rules! map_impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||||
let subschema = gen.subschema_for::<V>();
|
json_schema!({
|
||||||
SchemaObject {
|
"type": "object",
|
||||||
instance_type: Some(InstanceType::Object.into()),
|
"additionalProperties": gen.subschema_for::<V>(),
|
||||||
object: Some(Box::new(ObjectValidation {
|
})
|
||||||
additional_properties: Some(Box::new(subschema)),
|
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,11 +21,11 @@ macro_rules! forward_impl {
|
||||||
<$target>::schema_id()
|
<$target>::schema_id()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn json_schema(gen: &mut $crate::gen::SchemaGenerator) -> $crate::Schema {
|
||||||
<$target>::json_schema(gen)
|
<$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)
|
<$target>::_schemars_private_non_optional_json_schema(gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,55 +35,61 @@ macro_rules! forward_impl {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($ty:ty => $target:ty) => {
|
($ty:ty => $target:ty) => {
|
||||||
forward_impl!((JsonSchema for $ty) => $target);
|
forward_impl!(($crate::JsonSchema for $ty) => $target);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
mod array;
|
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;
|
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;
|
mod ffi;
|
||||||
#[cfg(feature = "indexmap")]
|
|
||||||
mod indexmap;
|
|
||||||
#[cfg(feature = "indexmap2")]
|
|
||||||
mod indexmap2;
|
|
||||||
mod maps;
|
mod maps;
|
||||||
mod nonzero_signed;
|
mod nonzero_signed;
|
||||||
mod nonzero_unsigned;
|
mod nonzero_unsigned;
|
||||||
mod primitives;
|
mod primitives;
|
||||||
#[cfg(feature = "semver")]
|
|
||||||
mod semver;
|
|
||||||
mod sequences;
|
mod sequences;
|
||||||
mod serdejson;
|
mod serdejson;
|
||||||
#[cfg(feature = "smallvec")]
|
|
||||||
mod smallvec;
|
|
||||||
#[cfg(feature = "smol_str")]
|
|
||||||
mod smol_str;
|
|
||||||
mod time;
|
mod time;
|
||||||
mod tuple;
|
mod tuple;
|
||||||
#[cfg(feature = "url")]
|
mod wrapper;
|
||||||
mod url;
|
|
||||||
#[cfg(feature = "uuid08")]
|
#[cfg(std_atomic)]
|
||||||
mod uuid08;
|
mod atomic;
|
||||||
|
|
||||||
|
#[cfg(feature = "arrayvec07")]
|
||||||
|
mod arrayvec07;
|
||||||
|
|
||||||
|
#[cfg(feature = "bytes1")]
|
||||||
|
mod bytes1 {
|
||||||
|
forward_impl!(bytes1::Bytes => Vec<u8>);
|
||||||
|
forward_impl!(bytes1::BytesMut => Vec<u8>);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "chrono04")]
|
||||||
|
mod chrono04;
|
||||||
|
|
||||||
|
#[cfg(any(feature = "rust_decimal1", feature = "bigdecimal04"))]
|
||||||
|
mod decimal;
|
||||||
|
|
||||||
|
#[cfg(feature = "either1")]
|
||||||
|
mod either1;
|
||||||
|
|
||||||
|
#[cfg(feature = "enumset1")]
|
||||||
|
forward_impl!((<T: enumset1::EnumSetType + crate::JsonSchema> crate::JsonSchema for enumset1::EnumSet<T>) => std::collections::BTreeSet<T>);
|
||||||
|
|
||||||
|
#[cfg(feature = "indexmap2")]
|
||||||
|
mod indexmap2;
|
||||||
|
|
||||||
|
#[cfg(feature = "semver1")]
|
||||||
|
mod semver1;
|
||||||
|
|
||||||
|
#[cfg(feature = "smallvec1")]
|
||||||
|
forward_impl!((<A: smallvec1::Array> crate::JsonSchema for smallvec1::SmallVec<A> where A::Item: crate::JsonSchema) => Vec<A::Item>);
|
||||||
|
|
||||||
|
#[cfg(feature = "smol_str02")]
|
||||||
|
forward_impl!(smol_str02::SmolStr => String);
|
||||||
|
|
||||||
|
#[cfg(feature = "url2")]
|
||||||
|
mod url2;
|
||||||
|
|
||||||
#[cfg(feature = "uuid1")]
|
#[cfg(feature = "uuid1")]
|
||||||
mod uuid1;
|
mod uuid1;
|
||||||
mod wrapper;
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::gen::SchemaGenerator;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::schema::*;
|
use crate::{JsonSchema, Schema};
|
||||||
use crate::JsonSchema;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::num::*;
|
use std::num::*;
|
||||||
|
|
||||||
|
@ -18,14 +17,12 @@ macro_rules! nonzero_unsigned_impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||||
let zero_schema: Schema = SchemaObject {
|
let mut schema = <$primitive>::json_schema(gen);
|
||||||
const_value: Some(0.into()),
|
let object = schema.ensure_object();
|
||||||
..Default::default()
|
object.insert("not".to_owned(), serde_json::json!({
|
||||||
}
|
"const": 0
|
||||||
.into();
|
}));
|
||||||
let mut schema: SchemaObject = <$primitive>::json_schema(gen).into();
|
schema
|
||||||
schema.subschemas().not = Some(Box::from(zero_schema));
|
|
||||||
schema.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::gen::SchemaGenerator;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::schema::*;
|
use crate::Schema;
|
||||||
use crate::JsonSchema;
|
use crate::JsonSchema;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::num::*;
|
use std::num::*;
|
||||||
|
@ -18,9 +18,10 @@ macro_rules! nonzero_unsigned_impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||||
let mut schema: SchemaObject = <$primitive>::json_schema(gen).into();
|
let mut schema = <$primitive>::json_schema(gen);
|
||||||
schema.number().minimum = Some(1.0);
|
let object = schema.ensure_object();
|
||||||
schema.into()
|
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!(NonZeroU64 => u64);
|
||||||
nonzero_unsigned_impl!(NonZeroU128 => u128);
|
nonzero_unsigned_impl!(NonZeroU128 => u128);
|
||||||
nonzero_unsigned_impl!(NonZeroUsize => usize);
|
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::<NonZeroU32>();
|
|
||||||
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()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,67 +1,30 @@
|
||||||
use crate::gen::SchemaGenerator;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::schema::*;
|
use crate::{json_schema, JsonSchema, Schema};
|
||||||
use crate::JsonSchema;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
macro_rules! simple_impl {
|
macro_rules! simple_impl {
|
||||||
($type:ty => $instance_type:ident) => {
|
($type:ty => $instance_type:literal) => {
|
||||||
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:ident, $name:expr, $format:expr) => {
|
|
||||||
impl JsonSchema for $type {
|
impl JsonSchema for $type {
|
||||||
no_ref_schema!();
|
no_ref_schema!();
|
||||||
|
|
||||||
fn schema_name() -> String {
|
fn schema_name() -> String {
|
||||||
$name.to_owned()
|
$instance_type.to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn schema_id() -> Cow<'static, str> {
|
fn schema_id() -> Cow<'static, str> {
|
||||||
Cow::Borrowed($name)
|
Cow::Borrowed($instance_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||||
SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::$instance_type.into()),
|
"type": $instance_type
|
||||||
format: $format,
|
})
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
($type:ty => $instance_type:literal, $format:literal) => {
|
||||||
|
|
||||||
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) => {
|
|
||||||
impl JsonSchema for $type {
|
impl JsonSchema for $type {
|
||||||
no_ref_schema!();
|
no_ref_schema!();
|
||||||
|
|
||||||
|
@ -74,24 +37,69 @@ macro_rules! unsigned_impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||||
let mut schema = SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::$instance_type.into()),
|
"type": $instance_type,
|
||||||
format: Some($format.to_owned()),
|
"format": $format
|
||||||
..Default::default()
|
})
|
||||||
};
|
|
||||||
schema.number().minimum = Some(0.0);
|
|
||||||
schema.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned_impl!(u8 => Integer, "uint8");
|
simple_impl!(str => "string");
|
||||||
unsigned_impl!(u16 => Integer, "uint16");
|
simple_impl!(String => "string");
|
||||||
unsigned_impl!(u32 => Integer, "uint32");
|
simple_impl!(bool => "boolean");
|
||||||
unsigned_impl!(u64 => Integer, "uint64");
|
simple_impl!(f32 => "number", "float");
|
||||||
unsigned_impl!(u128 => Integer, "uint128");
|
simple_impl!(f64 => "number", "double");
|
||||||
unsigned_impl!(usize => Integer, "uint");
|
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:literal, $format:literal) => {
|
||||||
|
impl JsonSchema for $type {
|
||||||
|
no_ref_schema!();
|
||||||
|
|
||||||
|
fn schema_name() -> String {
|
||||||
|
$format.to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn schema_id() -> Cow<'static, str> {
|
||||||
|
Cow::Borrowed($format)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||||
|
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");
|
||||||
|
|
||||||
impl JsonSchema for char {
|
impl JsonSchema for char {
|
||||||
no_ref_schema!();
|
no_ref_schema!();
|
||||||
|
@ -105,15 +113,10 @@ impl JsonSchema for char {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||||
SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::String.into()),
|
"type": "string",
|
||||||
string: Some(Box::new(StringValidation {
|
"minLength": 1,
|
||||||
min_length: Some(1),
|
"maxLength": 1,
|
||||||
max_length: Some(1),
|
})
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
24
schemars/src/json_schema_impls/semver1.rs
Normal file
24
schemars/src/json_schema_impls/semver1.rs
Normal file
|
@ -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-]+)*))?$"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::gen::SchemaGenerator;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::schema::*;
|
use crate::{json_schema, JsonSchema, Schema};
|
||||||
use crate::JsonSchema;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
macro_rules! seq_impl {
|
macro_rules! seq_impl {
|
||||||
|
@ -21,15 +20,10 @@ macro_rules! seq_impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||||
SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::Array.into()),
|
"type": "array",
|
||||||
array: Some(Box::new(ArrayValidation {
|
"items": gen.subschema_for::<T>(),
|
||||||
items: Some(gen.subschema_for::<T>().into()),
|
})
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -53,16 +47,11 @@ macro_rules! set_impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||||
SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::Array.into()),
|
"type": "array",
|
||||||
array: Some(Box::new(ArrayValidation {
|
"uniqueItems": true,
|
||||||
unique_items: Some(true),
|
"items": gen.subschema_for::<T>(),
|
||||||
items: Some(gen.subschema_for::<T>().into()),
|
})
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::gen::SchemaGenerator;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::schema::*;
|
use crate::{json_schema, JsonSchema, Schema};
|
||||||
use crate::JsonSchema;
|
|
||||||
use serde_json::{Map, Number, Value};
|
use serde_json::{Map, Number, Value};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
@ -17,7 +16,7 @@ impl JsonSchema for Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
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 {
|
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||||
SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::Number.into()),
|
"type": "number"
|
||||||
..Default::default()
|
})
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
use crate::gen::SchemaGenerator;
|
|
||||||
use crate::schema::*;
|
|
||||||
use crate::JsonSchema;
|
|
||||||
use smallvec::{Array, SmallVec};
|
|
||||||
|
|
||||||
forward_impl!((<A: Array> JsonSchema for SmallVec<A> where A::Item: JsonSchema) => Vec<A::Item>);
|
|
|
@ -1,6 +0,0 @@
|
||||||
use crate::gen::SchemaGenerator;
|
|
||||||
use crate::schema::*;
|
|
||||||
use crate::JsonSchema;
|
|
||||||
use smol_str::SmolStr;
|
|
||||||
|
|
||||||
forward_impl!(SmolStr => String);
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::gen::SchemaGenerator;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::schema::*;
|
use crate::{json_schema, JsonSchema, Schema};
|
||||||
use crate::JsonSchema;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
|
@ -14,18 +13,14 @@ impl JsonSchema for Duration {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||||
let mut schema = SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::Object.into()),
|
"type": "object",
|
||||||
..Default::default()
|
"required": ["secs", "nanos"],
|
||||||
};
|
"properties": {
|
||||||
let obj = schema.object();
|
"secs": u64::json_schema(gen),
|
||||||
obj.required.insert("secs".to_owned());
|
"nanos": u32::json_schema(gen),
|
||||||
obj.required.insert("nanos".to_owned());
|
}
|
||||||
obj.properties
|
})
|
||||||
.insert("secs".to_owned(), <u64>::json_schema(gen));
|
|
||||||
obj.properties
|
|
||||||
.insert("nanos".to_owned(), <u32>::json_schema(gen));
|
|
||||||
schema.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,17 +34,13 @@ impl JsonSchema for SystemTime {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||||
let mut schema = SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::Object.into()),
|
"type": "object",
|
||||||
..Default::default()
|
"required": ["secs_since_epoch", "nanos_since_epoch"],
|
||||||
};
|
"properties": {
|
||||||
let obj = schema.object();
|
"secs_since_epoch": u64::json_schema(gen),
|
||||||
obj.required.insert("secs_since_epoch".to_owned());
|
"nanos_since_epoch": u32::json_schema(gen),
|
||||||
obj.required.insert("nanos_since_epoch".to_owned());
|
}
|
||||||
obj.properties
|
})
|
||||||
.insert("secs_since_epoch".to_owned(), <u64>::json_schema(gen));
|
|
||||||
obj.properties
|
|
||||||
.insert("nanos_since_epoch".to_owned(), <u32>::json_schema(gen));
|
|
||||||
schema.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::gen::SchemaGenerator;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::schema::*;
|
use crate::{json_schema, JsonSchema, Schema};
|
||||||
use crate::JsonSchema;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
macro_rules! tuple_impls {
|
macro_rules! tuple_impls {
|
||||||
|
@ -24,20 +23,14 @@ macro_rules! tuple_impls {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||||
let items = vec![
|
json_schema!({
|
||||||
|
"type": "array",
|
||||||
|
"items": [
|
||||||
$(gen.subschema_for::<$name>()),+
|
$(gen.subschema_for::<$name>()),+
|
||||||
];
|
],
|
||||||
SchemaObject {
|
"minItems": $len,
|
||||||
instance_type: Some(InstanceType::Array.into()),
|
"maxItems": $len,
|
||||||
array: Some(Box::new(ArrayValidation {
|
})
|
||||||
items: Some(items.into()),
|
|
||||||
max_items: Some($len),
|
|
||||||
min_items: Some($len),
|
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)+
|
)+
|
||||||
|
@ -62,29 +55,3 @@ tuple_impls! {
|
||||||
15 => (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14)
|
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)
|
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::<i32>(),
|
|
||||||
schema_for::<bool>()
|
|
||||||
]))
|
|
||||||
);
|
|
||||||
assert_eq!(array_validation.max_items, Some(2));
|
|
||||||
assert_eq!(array_validation.min_items, Some(2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use crate::gen::SchemaGenerator;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::schema::*;
|
use crate::{json_schema, JsonSchema, Schema};
|
||||||
use crate::JsonSchema;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use url::Url;
|
use url2::Url;
|
||||||
|
|
||||||
impl JsonSchema for Url {
|
impl JsonSchema for Url {
|
||||||
no_ref_schema!();
|
no_ref_schema!();
|
||||||
|
@ -16,11 +15,9 @@ impl JsonSchema for Url {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||||
SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::String.into()),
|
"type": "string",
|
||||||
format: Some("uri".to_owned()),
|
"format": "uri",
|
||||||
..Default::default()
|
})
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::gen::SchemaGenerator;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::schema::*;
|
use crate::{json_schema, JsonSchema, Schema};
|
||||||
use crate::JsonSchema;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use uuid1::Uuid;
|
use uuid1::Uuid;
|
||||||
|
|
||||||
|
@ -16,11 +15,9 @@ impl JsonSchema for Uuid {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||||
SchemaObject {
|
json_schema!({
|
||||||
instance_type: Some(InstanceType::String.into()),
|
"type": "string",
|
||||||
format: Some("uuid".to_string()),
|
"format": "uuid",
|
||||||
..Default::default()
|
})
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use crate::gen::SchemaGenerator;
|
|
||||||
use crate::schema::Schema;
|
|
||||||
use crate::JsonSchema;
|
use crate::JsonSchema;
|
||||||
|
|
||||||
macro_rules! wrapper_impl {
|
macro_rules! wrapper_impl {
|
||||||
|
|
|
@ -1,32 +1,9 @@
|
||||||
#![forbid(unsafe_code)]
|
#![deny(unsafe_code)]
|
||||||
#![doc = include_str!("../README.md")]
|
#![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<K, V> = std::collections::BTreeMap<K, V>;
|
|
||||||
#[cfg(feature = "preserve_order")]
|
|
||||||
pub type Map<K, V> = indexmap::IndexMap<K, V>;
|
|
||||||
/// 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<T> = std::collections::BTreeSet<T>;
|
|
||||||
|
|
||||||
/// 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 flatten;
|
||||||
mod json_schema_impls;
|
mod json_schema_impls;
|
||||||
|
mod schema;
|
||||||
mod ser;
|
mod ser;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
|
@ -36,7 +13,6 @@ mod macros;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod _private;
|
pub mod _private;
|
||||||
pub mod gen;
|
pub mod gen;
|
||||||
pub mod schema;
|
|
||||||
pub mod visit;
|
pub mod visit;
|
||||||
|
|
||||||
#[cfg(feature = "schemars_derive")]
|
#[cfg(feature = "schemars_derive")]
|
||||||
|
@ -50,7 +26,7 @@ pub use schemars_derive::*;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub use serde_json as _serde_json;
|
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.
|
/// A type which can be described as a JSON Schema document.
|
||||||
///
|
///
|
||||||
|
@ -75,7 +51,7 @@ use schema::Schema;
|
||||||
/// you will need to determine an appropriate name and ID for the type.
|
/// you will need to determine an appropriate name and ID for the type.
|
||||||
/// For non-generic types, the type name/path are suitable for this:
|
/// For non-generic types, the type name/path are suitable for this:
|
||||||
/// ```
|
/// ```
|
||||||
/// use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema};
|
/// use schemars::{gen::SchemaGenerator, Schema, JsonSchema};
|
||||||
/// use std::borrow::Cow;
|
/// use std::borrow::Cow;
|
||||||
///
|
///
|
||||||
/// struct NonGenericType;
|
/// struct NonGenericType;
|
||||||
|
@ -101,7 +77,7 @@ use schema::Schema;
|
||||||
///
|
///
|
||||||
/// But generic type parameters which may affect the generated schema should typically be included in the name/ID:
|
/// But generic type parameters which may affect the generated schema should typically be included in the name/ID:
|
||||||
/// ```
|
/// ```
|
||||||
/// use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema};
|
/// use schemars::{gen::SchemaGenerator, Schema, JsonSchema};
|
||||||
/// use std::{borrow::Cow, marker::PhantomData};
|
/// use std::{borrow::Cow, marker::PhantomData};
|
||||||
///
|
///
|
||||||
/// struct GenericType<T>(PhantomData<T>);
|
/// struct GenericType<T>(PhantomData<T>);
|
||||||
|
@ -175,24 +151,3 @@ pub trait JsonSchema {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub fn schema_object_for<T: JsonSchema>() -> schema::SchemaObject {
|
|
||||||
schema_object(schema_for::<T>())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn schema_for<T: JsonSchema>() -> 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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -76,3 +76,19 @@ macro_rules! schema_for_value {
|
||||||
.unwrap()
|
.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)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -2,550 +2,229 @@
|
||||||
JSON Schema types.
|
JSON Schema types.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#[cfg(feature = "impl_json_schema")]
|
use ref_cast::ref_cast_custom;
|
||||||
use crate as schemars;
|
use ref_cast::RefCastCustom;
|
||||||
#[cfg(feature = "impl_json_schema")]
|
|
||||||
use crate::JsonSchema;
|
|
||||||
use crate::{Map, Set};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::{Map, Value};
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
/// A JSON Schema.
|
/// A JSON Schema.
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[derive(Debug, Clone, PartialEq, RefCastCustom)]
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[repr(transparent)]
|
||||||
#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
|
pub struct Schema(Value);
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum Schema {
|
impl<'de> Deserialize<'de> for Schema {
|
||||||
/// A trivial boolean JSON Schema.
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
///
|
where
|
||||||
/// The schema `true` matches everything (always passes validation), whereas the schema `false`
|
D: serde::Deserializer<'de>,
|
||||||
/// matches nothing (always fails validation).
|
{
|
||||||
Bool(bool),
|
let value = Value::deserialize(deserializer)?;
|
||||||
/// A JSON Schema object.
|
Schema::validate(&value)?;
|
||||||
Object(SchemaObject),
|
Ok(Schema(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Schema {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
ser::OrderedKeywordWrapper(&self.0).serialize(serializer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Schema {
|
impl Schema {
|
||||||
/// Creates a new `$ref` schema.
|
pub fn new() -> Self {
|
||||||
///
|
Self(Value::Object(Map::new()))
|
||||||
/// 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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if `self` is a `$ref` schema.
|
pub fn new_ref(reference: String) -> Self {
|
||||||
///
|
let mut map = Map::new();
|
||||||
/// If `self` is a [`SchemaObject`] with `Some` [`reference`](struct.SchemaObject.html#structfield.reference) set, this returns `true`.
|
map.insert("$ref".to_owned(), Value::String(reference));
|
||||||
/// Otherwise, returns `false`.
|
Self(Value::Object(map))
|
||||||
pub fn is_ref(&self) -> bool {
|
}
|
||||||
match self {
|
|
||||||
Schema::Object(o) => o.is_ref(),
|
pub fn as_value(&self) -> &Value {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_bool(&self) -> Option<bool> {
|
||||||
|
self.0.as_bool()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_object(&self) -> Option<&Map<String, Value>> {
|
||||||
|
self.0.as_object()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_object_mut(&mut self) -> Option<&mut Map<String, Value>> {
|
||||||
|
self.0.as_object_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn try_to_object(self) -> Result<Map<String, Value>, bool> {
|
||||||
|
match self.0 {
|
||||||
|
Value::Object(m) => Ok(m),
|
||||||
|
Value::Bool(b) => Err(b),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_value(self) -> Value {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ensure_object(&mut self) -> &mut Map<String, Value> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.as_object_mut()
|
||||||
|
.expect("Schema value should be of type Object.")
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts the given schema (if it is a boolean schema) into an equivalent schema object.
|
fn validate<E: serde::de::Error>(value: &Value) -> Result<(), E> {
|
||||||
///
|
use serde::de::Unexpected;
|
||||||
/// If the given schema is already a schema object, this has no effect.
|
let unexpected = match value {
|
||||||
///
|
Value::Bool(_) | Value::Object(_) => return Ok(()),
|
||||||
/// # Example
|
Value::Null => Unexpected::Unit,
|
||||||
/// ```
|
Value::Number(n) => {
|
||||||
/// use schemars::schema::{Schema, SchemaObject};
|
if let Some(u) = n.as_u64() {
|
||||||
///
|
Unexpected::Unsigned(u)
|
||||||
/// let bool_schema = Schema::Bool(true);
|
} else if let Some(i) = n.as_i64() {
|
||||||
///
|
Unexpected::Signed(i)
|
||||||
/// assert_eq!(bool_schema.into_object(), SchemaObject::default());
|
} else if let Some(f) = n.as_f64() {
|
||||||
/// ```
|
Unexpected::Float(f)
|
||||||
pub fn into_object(self) -> SchemaObject {
|
} else {
|
||||||
match self {
|
unreachable!()
|
||||||
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()
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Value::String(s) => Unexpected::Str(s),
|
||||||
|
Value::Array(_) => Unexpected::Seq,
|
||||||
|
};
|
||||||
|
|
||||||
|
Err(E::invalid_type(unexpected, &"object or boolean"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
#[ref_cast_custom]
|
||||||
|
fn ref_cast(value: &Value) -> &Self;
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
#[ref_cast_custom]
|
||||||
|
fn ref_cast_mut(value: &mut Value) -> &mut Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Schema> for Value {
|
||||||
|
fn from(v: Schema) -> Value {
|
||||||
|
v.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SchemaObject> for Schema {
|
impl std::convert::TryFrom<Value> for Schema {
|
||||||
fn from(o: SchemaObject) -> Self {
|
type Error = serde_json::Error;
|
||||||
Schema::Object(o)
|
|
||||||
|
fn try_from(value: Value) -> serde_json::Result<Schema> {
|
||||||
|
Schema::validate(&value)?;
|
||||||
|
Ok(Schema(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> std::convert::TryFrom<&'a Value> for &'a Schema {
|
||||||
|
type Error = serde_json::Error;
|
||||||
|
|
||||||
|
fn try_from(value: &Value) -> serde_json::Result<&Schema> {
|
||||||
|
Schema::validate(value)?;
|
||||||
|
Ok(Schema::ref_cast(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> std::convert::TryFrom<&'a mut Value> for &'a mut Schema {
|
||||||
|
type Error = serde_json::Error;
|
||||||
|
|
||||||
|
fn try_from(value: &mut Value) -> serde_json::Result<&mut Schema> {
|
||||||
|
Schema::validate(value)?;
|
||||||
|
Ok(Schema::ref_cast_mut(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Schema {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(Value::Object(Map::new()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Map<String, Value>> for Schema {
|
||||||
|
fn from(o: Map<String, Value>) -> Self {
|
||||||
|
Schema(Value::Object(o))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<bool> for Schema {
|
impl From<bool> for Schema {
|
||||||
fn from(b: bool) -> Self {
|
fn from(b: bool) -> Self {
|
||||||
Schema::Bool(b)
|
Schema(Value::Bool(b))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The root object of a JSON Schema document.
|
mod ser {
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
|
use serde::ser::{Serialize, SerializeMap, SerializeSeq};
|
||||||
#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
|
use serde_json::Value;
|
||||||
#[serde(rename_all = "camelCase", default)]
|
|
||||||
pub struct RootSchema {
|
|
||||||
/// The `$schema` keyword.
|
|
||||||
///
|
|
||||||
/// See [JSON Schema 8.1.1. The "$schema" Keyword](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.1.1).
|
|
||||||
#[serde(rename = "$schema", skip_serializing_if = "Option::is_none")]
|
|
||||||
pub meta_schema: Option<String>,
|
|
||||||
/// The root schema itself.
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub schema: SchemaObject,
|
|
||||||
/// The `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<String, Schema>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A JSON Schema object.
|
const ORDERED_KEYWORDS_START: [&str; 6] =
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
|
["$id", "$schema", "title", "description", "type", "format"];
|
||||||
#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
|
const ORDERED_KEYWORDS_END: [&str; 2] = ["$defs", "definitions"];
|
||||||
#[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<Box<Metadata>>,
|
|
||||||
/// 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<SingleOrVec<InstanceType>>,
|
|
||||||
/// 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<String>,
|
|
||||||
/// 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<Vec<Value>>,
|
|
||||||
/// 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<Value>,
|
|
||||||
/// Properties of the [`SchemaObject`] which define validation assertions in terms of other schemas.
|
|
||||||
#[serde(flatten, deserialize_with = "skip_if_default")]
|
|
||||||
pub subschemas: Option<Box<SubschemaValidation>>,
|
|
||||||
/// Properties of the [`SchemaObject`] which define validation assertions for numbers.
|
|
||||||
#[serde(flatten, deserialize_with = "skip_if_default")]
|
|
||||||
pub number: Option<Box<NumberValidation>>,
|
|
||||||
/// Properties of the [`SchemaObject`] which define validation assertions for strings.
|
|
||||||
#[serde(flatten, deserialize_with = "skip_if_default")]
|
|
||||||
pub string: Option<Box<StringValidation>>,
|
|
||||||
/// Properties of the [`SchemaObject`] which define validation assertions for arrays.
|
|
||||||
#[serde(flatten, deserialize_with = "skip_if_default")]
|
|
||||||
pub array: Option<Box<ArrayValidation>>,
|
|
||||||
/// Properties of the [`SchemaObject`] which define validation assertions for objects.
|
|
||||||
#[serde(flatten, deserialize_with = "skip_if_default")]
|
|
||||||
pub object: Option<Box<ObjectValidation>>,
|
|
||||||
/// 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<String>,
|
|
||||||
/// Arbitrary extra properties which are not part of the JSON Schema specification, or which `schemars` does not support.
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub extensions: Map<String, Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deserializing "null" to `Option<Value>` directly results in `None`,
|
pub(super) struct OrderedKeywordWrapper<'a>(pub &'a Value);
|
||||||
// this function instead makes it deserialize to `Some(Value::Null)`.
|
|
||||||
fn allow_null<'de, D>(de: D) -> Result<Option<Value>, D::Error>
|
impl Serialize for OrderedKeywordWrapper<'_> {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
D: serde::Deserializer<'de>,
|
S: serde::Serializer,
|
||||||
{
|
{
|
||||||
Value::deserialize(de).map(Option::Some)
|
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))?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn skip_if_default<'de, D, T>(deserializer: D) -> Result<Option<Box<T>>, D::Error>
|
for (key, value) in object {
|
||||||
where
|
if !ORDERED_KEYWORDS_START.contains(&key.as_str())
|
||||||
D: serde::Deserializer<'de>,
|
&& !ORDERED_KEYWORDS_END.contains(&key.as_str())
|
||||||
T: Deserialize<'de> + Default + PartialEq,
|
|
||||||
{
|
{
|
||||||
let value = T::deserialize(deserializer)?;
|
map.serialize_entry(key, &OrderedKeywordWrapper(value))?;
|
||||||
if value == T::default() {
|
|
||||||
Ok(None)
|
|
||||||
} else {
|
|
||||||
Ok(Some(Box::new(value)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! get_or_insert_default_fn {
|
for key in ORDERED_KEYWORDS_END {
|
||||||
($name:ident, $ret:ty) => {
|
if let Some(value) = object.get(key) {
|
||||||
get_or_insert_default_fn!(
|
map.serialize_entry(key, &OrderedKeywordWrapper(value))?;
|
||||||
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)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if `self` is a `$ref` schema.
|
map.end()
|
||||||
///
|
}
|
||||||
/// If `self` has `Some` [`reference`](struct.SchemaObject.html#structfield.reference) set, this returns `true`.
|
_ => self.0.serialize(serializer),
|
||||||
/// Otherwise, returns `false`.
|
|
||||||
pub fn is_ref(&self) -> bool {
|
|
||||||
self.reference.is_some()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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))
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Schema> for SchemaObject {
|
|
||||||
fn from(schema: Schema) -> Self {
|
|
||||||
schema.into_object()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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<String>,
|
|
||||||
/// The `title` keyword.
|
|
||||||
///
|
|
||||||
/// See [JSON Schema Validation 9.1. "title" and "description"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub title: Option<String>,
|
|
||||||
/// The `description` keyword.
|
|
||||||
///
|
|
||||||
/// See [JSON Schema Validation 9.1. "title" and "description"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub description: Option<String>,
|
|
||||||
/// The `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<Value>,
|
|
||||||
/// 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<Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
|
||||||
fn is_false(b: &bool) -> bool {
|
|
||||||
!b
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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<Vec<Schema>>,
|
|
||||||
/// 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<Vec<Schema>>,
|
|
||||||
/// 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<Vec<Schema>>,
|
|
||||||
/// 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<Box<Schema>>,
|
|
||||||
/// 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<Box<Schema>>,
|
|
||||||
/// 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<Box<Schema>>,
|
|
||||||
/// 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<Box<Schema>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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<f64>,
|
|
||||||
/// 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<f64>,
|
|
||||||
/// 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<f64>,
|
|
||||||
/// 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<f64>,
|
|
||||||
/// 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<f64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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<u32>,
|
|
||||||
/// 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<u32>,
|
|
||||||
/// 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<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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<SingleOrVec<Schema>>,
|
|
||||||
/// 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<Box<Schema>>,
|
|
||||||
/// 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<u32>,
|
|
||||||
/// 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<u32>,
|
|
||||||
/// 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<bool>,
|
|
||||||
/// 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<Box<Schema>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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<u32>,
|
|
||||||
/// 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<u32>,
|
|
||||||
/// 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<String>,
|
|
||||||
/// 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<String, Schema>,
|
|
||||||
/// 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<String, Schema>,
|
|
||||||
/// 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<Box<Schema>>,
|
|
||||||
/// 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<Box<Schema>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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<T> {
|
|
||||||
Single(Box<T>),
|
|
||||||
Vec(Vec<T>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> From<T> for SingleOrVec<T> {
|
|
||||||
fn from(single: T) -> Self {
|
|
||||||
SingleOrVec::Single(Box::new(single))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> From<Vec<T>> for SingleOrVec<T> {
|
|
||||||
fn from(vec: Vec<T>) -> Self {
|
|
||||||
SingleOrVec::Vec(vec)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: PartialEq> SingleOrVec<T> {
|
|
||||||
/// Returns `true` if `self` is either a `Single` equal to `x`, or a `Vec` containing `x`.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// 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),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use crate::schema::*;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::JsonSchema;
|
use crate::{json_schema, JsonSchema, Schema};
|
||||||
use crate::{gen::SchemaGenerator, Map};
|
use serde_json::{Error, Map, Value};
|
||||||
use serde_json::{Error, Value};
|
use std::fmt::Display;
|
||||||
use std::{convert::TryInto, fmt::Display};
|
|
||||||
|
|
||||||
pub(crate) struct Serializer<'a> {
|
pub(crate) struct Serializer<'a> {
|
||||||
pub(crate) gen: &'a mut SchemaGenerator,
|
pub(crate) gen: &'a mut SchemaGenerator,
|
||||||
|
@ -22,7 +21,7 @@ pub(crate) struct SerializeTuple<'a> {
|
||||||
|
|
||||||
pub(crate) struct SerializeMap<'a> {
|
pub(crate) struct SerializeMap<'a> {
|
||||||
gen: &'a mut SchemaGenerator,
|
gen: &'a mut SchemaGenerator,
|
||||||
properties: Map<String, Schema>,
|
properties: Map<String, Value>,
|
||||||
current_key: Option<String>,
|
current_key: Option<String>,
|
||||||
title: &'static str,
|
title: &'static str,
|
||||||
}
|
}
|
||||||
|
@ -36,13 +35,11 @@ macro_rules! forward_to_subschema_for {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! return_instance_type {
|
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<Self::Ok, Self::Error> {
|
fn $fn(self, _value: $ty) -> Result<Self::Ok, Self::Error> {
|
||||||
Ok(SchemaObject {
|
Ok(json_schema!({
|
||||||
instance_type: Some(InstanceType::$instance_type.into()),
|
"type": $instance_type
|
||||||
..Default::default()
|
}))
|
||||||
}
|
|
||||||
.into())
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -59,18 +56,18 @@ impl<'a> serde::Serializer for Serializer<'a> {
|
||||||
type SerializeStruct = SerializeMap<'a>;
|
type SerializeStruct = SerializeMap<'a>;
|
||||||
type SerializeStructVariant = Self;
|
type SerializeStructVariant = Self;
|
||||||
|
|
||||||
return_instance_type!(serialize_i8, i8, Integer);
|
return_instance_type!(serialize_i8, i8, "integer");
|
||||||
return_instance_type!(serialize_i16, i16, Integer);
|
return_instance_type!(serialize_i16, i16, "integer");
|
||||||
return_instance_type!(serialize_i32, i32, Integer);
|
return_instance_type!(serialize_i32, i32, "integer");
|
||||||
return_instance_type!(serialize_i64, i64, Integer);
|
return_instance_type!(serialize_i64, i64, "integer");
|
||||||
return_instance_type!(serialize_i128, i128, Integer);
|
return_instance_type!(serialize_i128, i128, "integer");
|
||||||
return_instance_type!(serialize_u8, u8, Integer);
|
return_instance_type!(serialize_u8, u8, "integer");
|
||||||
return_instance_type!(serialize_u16, u16, Integer);
|
return_instance_type!(serialize_u16, u16, "integer");
|
||||||
return_instance_type!(serialize_u32, u32, Integer);
|
return_instance_type!(serialize_u32, u32, "integer");
|
||||||
return_instance_type!(serialize_u64, u64, Integer);
|
return_instance_type!(serialize_u64, u64, "integer");
|
||||||
return_instance_type!(serialize_u128, u128, Integer);
|
return_instance_type!(serialize_u128, u128, "integer");
|
||||||
return_instance_type!(serialize_f32, f32, Number);
|
return_instance_type!(serialize_f32, f32, "number");
|
||||||
return_instance_type!(serialize_f64, f64, Number);
|
return_instance_type!(serialize_f64, f64, "number");
|
||||||
|
|
||||||
forward_to_subschema_for!(serialize_bool, bool);
|
forward_to_subschema_for!(serialize_bool, bool);
|
||||||
forward_to_subschema_for!(serialize_char, char);
|
forward_to_subschema_for!(serialize_char, char);
|
||||||
|
@ -93,7 +90,7 @@ impl<'a> serde::Serializer for Serializer<'a> {
|
||||||
let value_schema = iter
|
let value_schema = iter
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.try_fold(None, |acc, (_, v)| {
|
.try_fold(None, |acc, (_, v)| {
|
||||||
if acc == Some(Schema::Bool(true)) {
|
if acc == Some(true.into()) {
|
||||||
return Ok(acc);
|
return Ok(acc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,21 +100,16 @@ impl<'a> serde::Serializer for Serializer<'a> {
|
||||||
})?;
|
})?;
|
||||||
Ok(match &acc {
|
Ok(match &acc {
|
||||||
None => Some(schema),
|
None => Some(schema),
|
||||||
Some(items) if items != &schema => Some(Schema::Bool(true)),
|
Some(items) if items != &schema => Some(true.into()),
|
||||||
_ => acc,
|
_ => acc,
|
||||||
})
|
})
|
||||||
})?
|
})?
|
||||||
.unwrap_or(Schema::Bool(true));
|
.unwrap_or(true.into());
|
||||||
|
|
||||||
Ok(SchemaObject {
|
Ok(json_schema!({
|
||||||
instance_type: Some(InstanceType::Object.into()),
|
"type": "object",
|
||||||
object: Some(Box::new(ObjectValidation {
|
"additionalProperties": value_schema,
|
||||||
additional_properties: Some(Box::new(value_schema)),
|
}))
|
||||||
..ObjectValidation::default()
|
|
||||||
})),
|
|
||||||
..SchemaObject::default()
|
|
||||||
}
|
|
||||||
.into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||||
|
@ -132,52 +124,47 @@ impl<'a> serde::Serializer for Serializer<'a> {
|
||||||
where
|
where
|
||||||
T: serde::Serialize,
|
T: serde::Serialize,
|
||||||
{
|
{
|
||||||
// FIXME nasty duplication of `impl JsonSchema for Option<T>`
|
|
||||||
fn add_null_type(instance_type: &mut SingleOrVec<InstanceType>) {
|
|
||||||
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 {
|
let mut schema = value.serialize(Serializer {
|
||||||
gen: self.gen,
|
gen: self.gen,
|
||||||
include_title: false,
|
include_title: false,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if self.gen.settings().option_add_null_type {
|
if self.gen.settings().option_add_null_type {
|
||||||
schema = match schema {
|
schema = match schema.try_to_object() {
|
||||||
Schema::Bool(true) => Schema::Bool(true),
|
Ok(mut obj) => {
|
||||||
Schema::Bool(false) => <()>::json_schema(self.gen),
|
let value = obj.get_mut("type");
|
||||||
Schema::Object(SchemaObject {
|
match value {
|
||||||
instance_type: Some(ref mut instance_type),
|
Some(Value::Array(array)) => {
|
||||||
..
|
let null = Value::from("null");
|
||||||
}) => {
|
if !array.contains(&null) {
|
||||||
add_null_type(instance_type);
|
array.push(null);
|
||||||
schema
|
|
||||||
}
|
}
|
||||||
schema => SchemaObject {
|
obj.into()
|
||||||
subschemas: Some(Box::new(SubschemaValidation {
|
|
||||||
any_of: Some(vec![schema, <()>::json_schema(self.gen)]),
|
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
..Default::default()
|
|
||||||
}
|
}
|
||||||
.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)
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(true) => true.into(),
|
||||||
|
Err(false) => <()>::json_schema(self.gen),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.gen.settings().option_nullable {
|
if self.gen.settings().option_nullable {
|
||||||
let mut schema_obj = schema.into_object();
|
schema
|
||||||
schema_obj
|
.ensure_object()
|
||||||
.extensions
|
.insert("nullable".into(), true.into());
|
||||||
.insert("nullable".to_owned(), serde_json::json!(true));
|
|
||||||
schema = Schema::Object(schema_obj);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(schema)
|
Ok(schema)
|
||||||
|
@ -193,7 +180,7 @@ impl<'a> serde::Serializer for Serializer<'a> {
|
||||||
_variant_index: u32,
|
_variant_index: u32,
|
||||||
_variant: &'static str,
|
_variant: &'static str,
|
||||||
) -> Result<Self::Ok, Self::Error> {
|
) -> Result<Self::Ok, Self::Error> {
|
||||||
Ok(Schema::Bool(true))
|
Ok(true.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_newtype_struct<T: ?Sized>(
|
fn serialize_newtype_struct<T: ?Sized>(
|
||||||
|
@ -205,15 +192,13 @@ impl<'a> serde::Serializer for Serializer<'a> {
|
||||||
T: serde::Serialize,
|
T: serde::Serialize,
|
||||||
{
|
{
|
||||||
let include_title = self.include_title;
|
let include_title = self.include_title;
|
||||||
let mut result = value.serialize(self);
|
let mut schema = value.serialize(self)?;
|
||||||
|
|
||||||
if include_title {
|
if include_title && !name.is_empty() {
|
||||||
if let Ok(Schema::Object(ref mut object)) = result {
|
schema.ensure_object().insert("title".into(), name.into());
|
||||||
object.metadata().title = Some(name.to_string());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
Ok(schema)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_newtype_variant<T: ?Sized>(
|
fn serialize_newtype_variant<T: ?Sized>(
|
||||||
|
@ -226,7 +211,7 @@ impl<'a> serde::Serializer for Serializer<'a> {
|
||||||
where
|
where
|
||||||
T: serde::Serialize,
|
T: serde::Serialize,
|
||||||
{
|
{
|
||||||
Ok(Schema::Bool(true))
|
Ok(true.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
||||||
|
@ -313,7 +298,7 @@ impl serde::ser::SerializeTupleVariant for Serializer<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||||
Ok(Schema::Bool(true))
|
Ok(true.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,7 +318,7 @@ impl serde::ser::SerializeStructVariant for Serializer<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||||
Ok(Schema::Bool(true))
|
Ok(true.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,7 +330,7 @@ impl serde::ser::SerializeSeq for SerializeSeq<'_> {
|
||||||
where
|
where
|
||||||
T: serde::Serialize,
|
T: serde::Serialize,
|
||||||
{
|
{
|
||||||
if self.items != Some(Schema::Bool(true)) {
|
if self.items != Some(true.into()) {
|
||||||
let schema = value.serialize(Serializer {
|
let schema = value.serialize(Serializer {
|
||||||
gen: self.gen,
|
gen: self.gen,
|
||||||
include_title: false,
|
include_title: false,
|
||||||
|
@ -354,7 +339,7 @@ impl serde::ser::SerializeSeq for SerializeSeq<'_> {
|
||||||
None => self.items = Some(schema),
|
None => self.items = Some(schema),
|
||||||
Some(items) => {
|
Some(items) => {
|
||||||
if items != &schema {
|
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<Self::Ok, Self::Error> {
|
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||||
let items = self.items.unwrap_or(Schema::Bool(true));
|
let items = self.items.unwrap_or(true.into());
|
||||||
Ok(SchemaObject {
|
|
||||||
instance_type: Some(InstanceType::Array.into()),
|
Ok(json_schema!({
|
||||||
array: Some(Box::new(ArrayValidation {
|
"type": "array",
|
||||||
items: Some(items.into()),
|
"items": items
|
||||||
..ArrayValidation::default()
|
}))
|
||||||
})),
|
|
||||||
..SchemaObject::default()
|
|
||||||
}
|
|
||||||
.into())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,23 +375,21 @@ impl serde::ser::SerializeTuple for SerializeTuple<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||||
let len = self.items.len().try_into().ok();
|
let len = self.items.len();
|
||||||
let mut schema = SchemaObject {
|
let mut schema = json_schema!({
|
||||||
instance_type: Some(InstanceType::Array.into()),
|
"type": "array",
|
||||||
array: Some(Box::new(ArrayValidation {
|
"items": self.items,
|
||||||
items: Some(SingleOrVec::Vec(self.items)),
|
"maxItems": len,
|
||||||
max_items: len,
|
"minItems": len,
|
||||||
min_items: len,
|
});
|
||||||
..ArrayValidation::default()
|
|
||||||
})),
|
|
||||||
..SchemaObject::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
if !self.title.is_empty() {
|
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,
|
gen: self.gen,
|
||||||
include_title: false,
|
include_title: false,
|
||||||
})?;
|
})?;
|
||||||
self.properties.insert(key, schema);
|
self.properties.insert(key, schema.into());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||||
let mut schema = SchemaObject {
|
let mut schema = json_schema!({
|
||||||
instance_type: Some(InstanceType::Object.into()),
|
"type": "object",
|
||||||
object: Some(Box::new(ObjectValidation {
|
"properties": self.properties,
|
||||||
properties: self.properties,
|
});
|
||||||
..ObjectValidation::default()
|
|
||||||
})),
|
|
||||||
..SchemaObject::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
if !self.title.is_empty() {
|
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,
|
gen: self.gen,
|
||||||
include_title: false,
|
include_title: false,
|
||||||
})?;
|
})?;
|
||||||
self.properties.insert(key.to_string(), prop_schema);
|
self.properties.insert(key.to_string(), prop_schema.into());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,116 +7,87 @@ 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.
|
When overriding one of these methods, you will *usually* want to still call this default implementation.
|
||||||
|
|
||||||
# Example
|
# Example
|
||||||
To add a custom property to all schemas:
|
To add a custom property to all object schemas:
|
||||||
```
|
```
|
||||||
use schemars::schema::SchemaObject;
|
use schemars::Schema;
|
||||||
use schemars::visit::{Visitor, visit_schema_object};
|
use schemars::visit::{Visitor, visit_schema};
|
||||||
|
|
||||||
pub struct MyVisitor;
|
pub struct MyVisitor;
|
||||||
|
|
||||||
impl Visitor for 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
|
// 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
|
// 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.
|
/// Trait used to recursively modify a constructed schema and its subschemas.
|
||||||
pub trait Visitor {
|
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.
|
/// 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.
|
/// 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) {
|
fn visit_schema(&mut self, schema: &mut Schema) {
|
||||||
visit_schema(self, 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: Visitor + ?Sized>(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`].
|
/// Visits all subschemas of the [`Schema`].
|
||||||
pub fn visit_schema<V: Visitor + ?Sized>(v: &mut V, schema: &mut Schema) {
|
pub fn visit_schema<V: Visitor + ?Sized>(v: &mut V, schema: &mut Schema) {
|
||||||
if let Schema::Object(schema) = schema {
|
if let Some(obj) = schema.as_object_mut() {
|
||||||
v.visit_schema_object(schema)
|
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" => {
|
||||||
/// Visits all subschemas of the [`SchemaObject`].
|
if let Some(array) = value.as_array_mut() {
|
||||||
pub fn visit_schema_object<V: Visitor + ?Sized>(v: &mut V, schema: &mut SchemaObject) {
|
for value in array {
|
||||||
if let Some(sub) = &mut schema.subschemas {
|
if let Ok(subschema) = value.try_into() {
|
||||||
visit_vec(v, &mut sub.all_of);
|
v.visit_schema(subschema)
|
||||||
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: Visitor + ?Sized>(v: &mut V, target: &mut Option<Box<Schema>>) {
|
|
||||||
if let Some(s) = target {
|
|
||||||
v.visit_schema(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_vec<V: Visitor + ?Sized>(v: &mut V, target: &mut Option<Vec<Schema>>) {
|
|
||||||
if let Some(vec) = target {
|
|
||||||
for s in vec {
|
|
||||||
v.visit_schema(s)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fn visit_map_values<V: Visitor + ?Sized>(v: &mut V, target: &mut crate::Map<String, Schema>) {
|
"items" => {
|
||||||
for s in target.values_mut() {
|
if let Some(array) = value.as_array_mut() {
|
||||||
v.visit_schema(s)
|
for value in array {
|
||||||
|
if let Ok(subschema) = value.try_into() {
|
||||||
|
v.visit_schema(subschema)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if let Ok(subschema) = value.try_into() {
|
||||||
fn visit_single_or_vec<V: Visitor + ?Sized>(v: &mut V, target: &mut Option<SingleOrVec<Schema>>) {
|
v.visit_schema(subschema)
|
||||||
match target {
|
}
|
||||||
None => {}
|
}
|
||||||
Some(SingleOrVec::Single(s)) => v.visit_schema(s),
|
"properties" | "patternProperties" | "definitions" | "$defs" => {
|
||||||
Some(SingleOrVec::Vec(vec)) => {
|
if let Some(obj) = value.as_object_mut() {
|
||||||
for s in vec {
|
for value in obj.values_mut() {
|
||||||
v.visit_schema(s)
|
if let Ok(subschema) = value.try_into() {
|
||||||
|
v.visit_schema(subschema)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,29 +104,23 @@ pub struct ReplaceBoolSchemas {
|
||||||
|
|
||||||
impl Visitor for ReplaceBoolSchemas {
|
impl Visitor for ReplaceBoolSchemas {
|
||||||
fn visit_schema(&mut self, schema: &mut Schema) {
|
fn visit_schema(&mut self, schema: &mut Schema) {
|
||||||
|
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);
|
visit_schema(self, schema);
|
||||||
|
|
||||||
if let Schema::Bool(b) = *schema {
|
if let Some(obj) = schema.as_object_mut() {
|
||||||
*schema = Schema::Bool(b).into_object().into()
|
obj.insert(ap_key, ap_value);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visit_schema_object(self, schema);
|
visit_schema(self, schema);
|
||||||
|
} else {
|
||||||
|
schema.ensure_object();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,18 +131,19 @@ impl Visitor for ReplaceBoolSchemas {
|
||||||
pub struct RemoveRefSiblings;
|
pub struct RemoveRefSiblings;
|
||||||
|
|
||||||
impl Visitor for RemoveRefSiblings {
|
impl Visitor for RemoveRefSiblings {
|
||||||
fn visit_schema_object(&mut self, schema: &mut SchemaObject) {
|
fn visit_schema(&mut self, schema: &mut Schema) {
|
||||||
visit_schema_object(self, schema);
|
visit_schema(self, schema);
|
||||||
|
|
||||||
if let Some(reference) = schema.reference.take() {
|
if let Some(obj) = schema.as_object_mut() {
|
||||||
if schema == &SchemaObject::default() {
|
if obj.len() > 1 {
|
||||||
schema.reference = Some(reference);
|
if let Some(ref_value) = obj.remove("$ref") {
|
||||||
} else {
|
if let Value::Array(all_of) =
|
||||||
let ref_schema = Schema::new_ref(reference);
|
obj.entry("allOf").or_insert(Value::Array(Vec::new()))
|
||||||
let all_of = &mut schema.subschemas().all_of;
|
{
|
||||||
match all_of {
|
all_of.push(json!({
|
||||||
Some(vec) => vec.push(ref_schema),
|
"$ref": ref_value
|
||||||
None => *all_of = Some(vec![ref_schema]),
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,25 +154,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.
|
/// This is useful for dialects of JSON Schema (e.g. OpenAPI 3.0) that do not support the `examples` property.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SetSingleExample {
|
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Visitor for SetSingleExample {
|
impl Visitor for SetSingleExample {
|
||||||
fn visit_schema_object(&mut self, schema: &mut SchemaObject) {
|
fn visit_schema(&mut self, schema: &mut Schema) {
|
||||||
visit_schema_object(self, schema);
|
visit_schema(self, schema);
|
||||||
|
|
||||||
let first_example = schema.metadata.as_mut().and_then(|m| {
|
if let Some(obj) = schema.as_object_mut() {
|
||||||
if self.retain_examples {
|
if let Some(Value::Array(examples)) = obj.remove("examples") {
|
||||||
m.examples.first().cloned()
|
if let Some(first_example) = examples.into_iter().next() {
|
||||||
} else {
|
obj.insert("example".into(), first_example);
|
||||||
m.examples.drain(..).next()
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(example) = first_example {
|
|
||||||
schema.extensions.insert("example".to_owned(), example);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,6 @@
|
||||||
mod util;
|
mod util;
|
||||||
use util::*;
|
use util::*;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn arrayvec05() -> TestResult {
|
|
||||||
test_default_generated_schema::<arrayvec05::ArrayVec<[i32; 16]>>("arrayvec")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn arrayvec05_string() -> TestResult {
|
|
||||||
test_default_generated_schema::<arrayvec05::ArrayString<[u8; 16]>>("arrayvec_string")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn arrayvec07() -> TestResult {
|
fn arrayvec07() -> TestResult {
|
||||||
test_default_generated_schema::<arrayvec07::ArrayVec<i32, 16>>("arrayvec")
|
test_default_generated_schema::<arrayvec07::ArrayVec<i32, 16>>("arrayvec")
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
mod util;
|
mod util;
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes1::{Bytes, BytesMut};
|
||||||
use util::*;
|
use util::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
mod util;
|
mod util;
|
||||||
use chrono::prelude::*;
|
use chrono04::prelude::*;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use util::*;
|
use util::*;
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,7 @@ use util::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rust_decimal() -> TestResult {
|
fn rust_decimal() -> TestResult {
|
||||||
test_default_generated_schema::<rust_decimal::Decimal>("rust_decimal")
|
test_default_generated_schema::<rust_decimal1::Decimal>("rust_decimal")
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn bigdecimal03() -> TestResult {
|
|
||||||
test_default_generated_schema::<bigdecimal03::BigDecimal>("bigdecimal03")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
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::<Struct>();
|
|
||||||
let struct_schema = gen.definitions().get(&<Struct>::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));
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
mod util;
|
mod util;
|
||||||
use either::Either;
|
use either1::Either;
|
||||||
use util::*;
|
use util::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
mod util;
|
mod util;
|
||||||
use schemars::{JsonSchema, Map};
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use schemars::JsonSchema;
|
||||||
use util::*;
|
use util::*;
|
||||||
|
|
||||||
// Ensure that schemars_derive uses the full path to std::string::String
|
// Ensure that schemars_derive uses the full path to std::string::String
|
||||||
|
@ -20,7 +22,7 @@ struct Struct {
|
||||||
#[schemars(rename_all = "camelCase")]
|
#[schemars(rename_all = "camelCase")]
|
||||||
enum External {
|
enum External {
|
||||||
UnitOne,
|
UnitOne,
|
||||||
StringMap(Map<&'static str, &'static str>),
|
StringMap(BTreeMap<&'static str, &'static str>),
|
||||||
UnitStructNewType(UnitStruct),
|
UnitStructNewType(UnitStruct),
|
||||||
StructNewType(Struct),
|
StructNewType(Struct),
|
||||||
Struct {
|
Struct {
|
||||||
|
@ -29,6 +31,7 @@ enum External {
|
||||||
},
|
},
|
||||||
UnitTwo,
|
UnitTwo,
|
||||||
Tuple(i32, bool),
|
Tuple(i32, bool),
|
||||||
|
// FIXME this should probably only replace the "payload" of the enum
|
||||||
#[schemars(with = "i32")]
|
#[schemars(with = "i32")]
|
||||||
WithInt,
|
WithInt,
|
||||||
}
|
}
|
||||||
|
@ -43,7 +46,7 @@ fn enum_external_tag() -> TestResult {
|
||||||
#[schemars(tag = "typeProperty")]
|
#[schemars(tag = "typeProperty")]
|
||||||
enum Internal {
|
enum Internal {
|
||||||
UnitOne,
|
UnitOne,
|
||||||
StringMap(Map<&'static str, &'static str>),
|
StringMap(BTreeMap<&'static str, &'static str>),
|
||||||
UnitStructNewType(UnitStruct),
|
UnitStructNewType(UnitStruct),
|
||||||
StructNewType(Struct),
|
StructNewType(Struct),
|
||||||
Struct {
|
Struct {
|
||||||
|
@ -51,6 +54,7 @@ enum Internal {
|
||||||
bar: bool,
|
bar: bool,
|
||||||
},
|
},
|
||||||
UnitTwo,
|
UnitTwo,
|
||||||
|
// FIXME this should probably only replace the "payload" of the enum
|
||||||
#[schemars(with = "i32")]
|
#[schemars(with = "i32")]
|
||||||
WithInt,
|
WithInt,
|
||||||
}
|
}
|
||||||
|
@ -65,7 +69,7 @@ fn enum_internal_tag() -> TestResult {
|
||||||
#[schemars(untagged)]
|
#[schemars(untagged)]
|
||||||
enum Untagged {
|
enum Untagged {
|
||||||
UnitOne,
|
UnitOne,
|
||||||
StringMap(Map<&'static str, &'static str>),
|
StringMap(BTreeMap<&'static str, &'static str>),
|
||||||
UnitStructNewType(UnitStruct),
|
UnitStructNewType(UnitStruct),
|
||||||
StructNewType(Struct),
|
StructNewType(Struct),
|
||||||
Struct {
|
Struct {
|
||||||
|
@ -73,6 +77,7 @@ enum Untagged {
|
||||||
bar: bool,
|
bar: bool,
|
||||||
},
|
},
|
||||||
Tuple(i32, bool),
|
Tuple(i32, bool),
|
||||||
|
// FIXME this should probably only replace the "payload" of the enum
|
||||||
#[schemars(with = "i32")]
|
#[schemars(with = "i32")]
|
||||||
WithInt,
|
WithInt,
|
||||||
}
|
}
|
||||||
|
@ -87,7 +92,7 @@ fn enum_untagged() -> TestResult {
|
||||||
#[schemars(tag = "t", content = "c")]
|
#[schemars(tag = "t", content = "c")]
|
||||||
enum Adjacent {
|
enum Adjacent {
|
||||||
UnitOne,
|
UnitOne,
|
||||||
StringMap(Map<&'static str, &'static str>),
|
StringMap(BTreeMap<&'static str, &'static str>),
|
||||||
UnitStructNewType(UnitStruct),
|
UnitStructNewType(UnitStruct),
|
||||||
StructNewType(Struct),
|
StructNewType(Struct),
|
||||||
Struct {
|
Struct {
|
||||||
|
@ -96,6 +101,7 @@ enum Adjacent {
|
||||||
},
|
},
|
||||||
Tuple(i32, bool),
|
Tuple(i32, bool),
|
||||||
UnitTwo,
|
UnitTwo,
|
||||||
|
// FIXME this should probably only replace the "payload" of the enum
|
||||||
#[schemars(with = "i32")]
|
#[schemars(with = "i32")]
|
||||||
WithInt,
|
WithInt,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
mod util;
|
mod util;
|
||||||
use schemars::{JsonSchema, Map};
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use schemars::JsonSchema;
|
||||||
use util::*;
|
use util::*;
|
||||||
|
|
||||||
// Ensure that schemars_derive uses the full path to std::string::String
|
// Ensure that schemars_derive uses the full path to std::string::String
|
||||||
|
@ -22,7 +24,7 @@ struct Struct {
|
||||||
#[schemars(rename_all = "camelCase", deny_unknown_fields)]
|
#[schemars(rename_all = "camelCase", deny_unknown_fields)]
|
||||||
enum External {
|
enum External {
|
||||||
UnitOne,
|
UnitOne,
|
||||||
StringMap(Map<&'static str, &'static str>),
|
StringMap(BTreeMap<&'static str, &'static str>),
|
||||||
UnitStructNewType(UnitStruct),
|
UnitStructNewType(UnitStruct),
|
||||||
StructNewType(Struct),
|
StructNewType(Struct),
|
||||||
Struct {
|
Struct {
|
||||||
|
@ -31,6 +33,7 @@ enum External {
|
||||||
},
|
},
|
||||||
UnitTwo,
|
UnitTwo,
|
||||||
Tuple(i32, bool),
|
Tuple(i32, bool),
|
||||||
|
// FIXME this should probably only replace the "payload" of the enum
|
||||||
#[schemars(with = "i32")]
|
#[schemars(with = "i32")]
|
||||||
WithInt,
|
WithInt,
|
||||||
}
|
}
|
||||||
|
@ -46,7 +49,7 @@ fn enum_external_tag() -> TestResult {
|
||||||
#[schemars(tag = "typeProperty", deny_unknown_fields)]
|
#[schemars(tag = "typeProperty", deny_unknown_fields)]
|
||||||
enum Internal {
|
enum Internal {
|
||||||
UnitOne,
|
UnitOne,
|
||||||
StringMap(Map<&'static str, &'static str>),
|
StringMap(BTreeMap<&'static str, &'static str>),
|
||||||
UnitStructNewType(UnitStruct),
|
UnitStructNewType(UnitStruct),
|
||||||
StructNewType(Struct),
|
StructNewType(Struct),
|
||||||
Struct {
|
Struct {
|
||||||
|
@ -54,6 +57,7 @@ enum Internal {
|
||||||
bar: bool,
|
bar: bool,
|
||||||
},
|
},
|
||||||
UnitTwo,
|
UnitTwo,
|
||||||
|
// FIXME this should only replace the "payload" of the enum (which doesn't even make sense for unit enums!)
|
||||||
#[schemars(with = "i32")]
|
#[schemars(with = "i32")]
|
||||||
WithInt,
|
WithInt,
|
||||||
}
|
}
|
||||||
|
@ -69,7 +73,7 @@ fn enum_internal_tag() -> TestResult {
|
||||||
#[schemars(untagged, deny_unknown_fields)]
|
#[schemars(untagged, deny_unknown_fields)]
|
||||||
enum Untagged {
|
enum Untagged {
|
||||||
UnitOne,
|
UnitOne,
|
||||||
StringMap(Map<&'static str, &'static str>),
|
StringMap(BTreeMap<&'static str, &'static str>),
|
||||||
UnitStructNewType(UnitStruct),
|
UnitStructNewType(UnitStruct),
|
||||||
StructNewType(Struct),
|
StructNewType(Struct),
|
||||||
Struct {
|
Struct {
|
||||||
|
@ -77,6 +81,7 @@ enum Untagged {
|
||||||
bar: bool,
|
bar: bool,
|
||||||
},
|
},
|
||||||
Tuple(i32, bool),
|
Tuple(i32, bool),
|
||||||
|
// FIXME this should probably only replace the "payload" of the enum
|
||||||
#[schemars(with = "i32")]
|
#[schemars(with = "i32")]
|
||||||
WithInt,
|
WithInt,
|
||||||
}
|
}
|
||||||
|
@ -92,7 +97,7 @@ fn enum_untagged() -> TestResult {
|
||||||
#[schemars(tag = "t", content = "c", deny_unknown_fields)]
|
#[schemars(tag = "t", content = "c", deny_unknown_fields)]
|
||||||
enum Adjacent {
|
enum Adjacent {
|
||||||
UnitOne,
|
UnitOne,
|
||||||
StringMap(Map<&'static str, &'static str>),
|
StringMap(BTreeMap<&'static str, &'static str>),
|
||||||
UnitStructNewType(UnitStruct),
|
UnitStructNewType(UnitStruct),
|
||||||
StructNewType(Struct),
|
StructNewType(Struct),
|
||||||
Struct {
|
Struct {
|
||||||
|
@ -101,6 +106,7 @@ enum Adjacent {
|
||||||
},
|
},
|
||||||
Tuple(i32, bool),
|
Tuple(i32, bool),
|
||||||
UnitTwo,
|
UnitTwo,
|
||||||
|
// FIXME this should probably only replace the "payload" of the enum
|
||||||
#[schemars(with = "i32")]
|
#[schemars(with = "i32")]
|
||||||
WithInt,
|
WithInt,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
mod util;
|
mod util;
|
||||||
use enumset::{EnumSet, EnumSetType};
|
use enumset1::{EnumSet, EnumSetType};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use util::*;
|
use util::*;
|
||||||
|
|
||||||
|
// needed to derive EnumSetType when using a crate alias
|
||||||
|
extern crate enumset1 as enumset;
|
||||||
|
|
||||||
#[derive(EnumSetType, JsonSchema)]
|
#[derive(EnumSetType, JsonSchema)]
|
||||||
enum Foo {
|
enum Foo {
|
||||||
Bar,
|
Bar,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
"title": "String",
|
"title": "string",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
||||||
"title": "Decimal",
|
|
||||||
"type": "string",
|
|
||||||
"pattern": "^-?[0-9]+(\\.[0-9]+)?$"
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
"$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": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
|
|
|
@ -127,8 +127,7 @@
|
||||||
"WithInt"
|
"WithInt"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"additionalProperties": false
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -15,7 +15,7 @@
|
||||||
"$ref": "#/definitions/Range_of_double"
|
"$ref": "#/definitions/Range_of_double"
|
||||||
},
|
},
|
||||||
"bound": {
|
"bound": {
|
||||||
"$ref": "#/definitions/Bound_of_String"
|
"$ref": "#/definitions/Bound_of_string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Bound_of_String": {
|
"Bound_of_string": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
|
@ -10,10 +10,10 @@
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"byte_or_bool2": {
|
"byte_or_bool2": {
|
||||||
"$ref": "#/definitions/Or_for_uint8_and_Boolean"
|
"$ref": "#/definitions/Or_for_uint8_and_boolean"
|
||||||
},
|
},
|
||||||
"unit_or_t2": {
|
"unit_or_t2": {
|
||||||
"$ref": "#/definitions/Or_for_Null_and_int32"
|
"$ref": "#/definitions/Or_for_null_and_int32"
|
||||||
},
|
},
|
||||||
"s": {
|
"s": {
|
||||||
"$ref": "#/definitions/Str"
|
"$ref": "#/definitions/Str"
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
"Or_for_uint8_and_Boolean": {
|
"Or_for_uint8_and_boolean": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Or_for_Null_and_int32": {
|
"Or_for_null_and_int32": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "null"
|
"type": "null"
|
||||||
|
|
|
@ -8,14 +8,14 @@
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"result1": {
|
"result1": {
|
||||||
"$ref": "#/definitions/Result_of_MyStruct_or_Array_of_String"
|
"$ref": "#/definitions/Result_of_MyStruct_or_Array_of_string"
|
||||||
},
|
},
|
||||||
"result2": {
|
"result2": {
|
||||||
"$ref": "#/definitions/Result_of_Boolean_or_Null"
|
"$ref": "#/definitions/Result_of_boolean_or_null"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
"Result_of_MyStruct_or_Array_of_String": {
|
"Result_of_MyStruct_or_Array_of_string": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
@ -56,7 +56,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Result_of_Boolean_or_Null": {
|
"Result_of_boolean_or_null": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
"$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",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"inner",
|
"inner",
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
"$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",
|
"type": "object",
|
||||||
"required": [
|
|
||||||
"inner",
|
|
||||||
"t",
|
|
||||||
"u",
|
|
||||||
"v",
|
|
||||||
"w"
|
|
||||||
],
|
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"inner": {
|
||||||
|
"$ref": "#/definitions/MySimpleStruct"
|
||||||
|
},
|
||||||
"t": {
|
"t": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"format": "int32"
|
"format": "int32"
|
||||||
|
@ -25,23 +21,27 @@
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"inner": {
|
|
||||||
"$ref": "#/definitions/MySimpleStruct"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"required": [
|
||||||
|
"inner",
|
||||||
|
"t",
|
||||||
|
"u",
|
||||||
|
"v",
|
||||||
|
"w"
|
||||||
|
],
|
||||||
"definitions": {
|
"definitions": {
|
||||||
"MySimpleStruct": {
|
"MySimpleStruct": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
|
||||||
"foo"
|
|
||||||
],
|
|
||||||
"properties": {
|
"properties": {
|
||||||
"foo": {
|
"foo": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"format": "int32"
|
"format": "int32"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"required": [
|
||||||
|
"foo"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
"$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",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"foo",
|
"foo",
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
"format": "int32"
|
"format": "int32"
|
||||||
},
|
},
|
||||||
"generic": {
|
"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": {
|
"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",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"inner",
|
"inner",
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": [
|
"type": [
|
||||||
"boolean",
|
"object",
|
||||||
"object"
|
"boolean"
|
||||||
],
|
],
|
||||||
"required": [
|
"required": [
|
||||||
"typeProperty"
|
"typeProperty"
|
||||||
|
@ -39,8 +39,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": [
|
"type": [
|
||||||
"boolean",
|
"object",
|
||||||
"object"
|
"boolean"
|
||||||
],
|
],
|
||||||
"required": [
|
"required": [
|
||||||
"typeProperty"
|
"typeProperty"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
"title": "Array_of_String",
|
"title": "Array_of_string",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
"title": "String",
|
"title": "string",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
|
@ -1,13 +1,15 @@
|
||||||
mod util;
|
mod util;
|
||||||
use indexmap::{IndexMap, IndexSet};
|
use std::hash::RandomState;
|
||||||
|
|
||||||
|
use indexmap2::{IndexMap, IndexSet};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use util::*;
|
use util::*;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(JsonSchema)]
|
#[derive(JsonSchema)]
|
||||||
struct IndexMapTypes {
|
struct IndexMapTypes {
|
||||||
map: IndexMap<i32, bool>,
|
map: IndexMap<i32, bool, RandomState>,
|
||||||
set: IndexSet<isize>,
|
set: IndexSet<isize, RandomState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
mod util;
|
|
||||||
use indexmap2::{IndexMap, IndexSet};
|
|
||||||
use schemars::JsonSchema;
|
|
||||||
use util::*;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(JsonSchema)]
|
|
||||||
struct IndexMapTypes {
|
|
||||||
map: IndexMap<i32, bool>,
|
|
||||||
set: IndexSet<isize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn indexmap_types() -> TestResult {
|
|
||||||
test_default_generated_schema::<IndexMapTypes>("indexmap")
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
mod util;
|
|
||||||
use schemars::gen::SchemaSettings;
|
|
||||||
use schemars::schema::RootSchema;
|
|
||||||
use util::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn schema_matches_draft07() -> TestResult {
|
|
||||||
test_generated_schema::<RootSchema>("schema", SchemaSettings::draft07())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn schema_matches_2019_09() -> TestResult {
|
|
||||||
test_generated_schema::<RootSchema>("schema-2019_09", SchemaSettings::draft2019_09())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn schema_matches_openapi3() -> TestResult {
|
|
||||||
test_generated_schema::<RootSchema>("schema-openapi3", SchemaSettings::openapi3())
|
|
||||||
}
|
|
|
@ -2,7 +2,7 @@ mod util;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use util::*;
|
use util::*;
|
||||||
|
|
||||||
fn schema_fn(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
|
fn schema_fn(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema {
|
||||||
<bool>::json_schema(gen)
|
<bool>::json_schema(gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ pub enum External {
|
||||||
#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema,
|
#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema,
|
||||||
i32,
|
i32,
|
||||||
),
|
),
|
||||||
|
// FIXME this should probably only replace the "payload" of the enum
|
||||||
#[schemars(schema_with = "schema_fn")]
|
#[schemars(schema_with = "schema_fn")]
|
||||||
Unit,
|
Unit,
|
||||||
}
|
}
|
||||||
|
@ -38,6 +39,7 @@ pub enum Internal {
|
||||||
foo: DoesntImplementJsonSchema,
|
foo: DoesntImplementJsonSchema,
|
||||||
},
|
},
|
||||||
NewType(#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema),
|
NewType(#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema),
|
||||||
|
// FIXME this should probably only replace the "payload" of the enum
|
||||||
#[schemars(schema_with = "schema_fn")]
|
#[schemars(schema_with = "schema_fn")]
|
||||||
Unit,
|
Unit,
|
||||||
}
|
}
|
||||||
|
@ -59,6 +61,7 @@ pub enum Untagged {
|
||||||
#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema,
|
#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema,
|
||||||
i32,
|
i32,
|
||||||
),
|
),
|
||||||
|
// FIXME this should probably only replace the "payload" of the enum
|
||||||
#[schemars(schema_with = "schema_fn")]
|
#[schemars(schema_with = "schema_fn")]
|
||||||
Unit,
|
Unit,
|
||||||
}
|
}
|
||||||
|
@ -80,6 +83,7 @@ pub enum Adjacent {
|
||||||
#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema,
|
#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema,
|
||||||
i32,
|
i32,
|
||||||
),
|
),
|
||||||
|
// FIXME this should probably only replace the "payload" of the enum
|
||||||
#[schemars(schema_with = "schema_fn")]
|
#[schemars(schema_with = "schema_fn")]
|
||||||
Unit,
|
Unit,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ mod util;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use util::*;
|
use util::*;
|
||||||
|
|
||||||
fn schema_fn(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
|
fn schema_fn(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema {
|
||||||
<bool>::json_schema(gen)
|
<bool>::json_schema(gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
mod util;
|
mod util;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use semver::Version;
|
use semver1::Version;
|
||||||
use util::*;
|
use util::*;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
mod util;
|
mod util;
|
||||||
use smallvec::SmallVec;
|
use smallvec1::SmallVec;
|
||||||
use util::*;
|
use util::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
mod util;
|
mod util;
|
||||||
use smol_str::SmolStr;
|
use smol_str02::SmolStr;
|
||||||
use util::*;
|
use util::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
mod util;
|
mod util;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use url::Url;
|
use url2::Url;
|
||||||
use util::*;
|
use util::*;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
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::error::Error;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
|
@ -17,7 +18,16 @@ pub fn test_default_generated_schema<T: JsonSchema>(file: &str) -> TestResult {
|
||||||
test_schema(&actual, file)
|
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)) {
|
let expected_json = match fs::read_to_string(format!("tests/expected/{}.json", file)) {
|
||||||
Ok(j) => j,
|
Ok(j) => j,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -35,8 +45,30 @@ pub fn test_schema(actual: &RootSchema, file: &str) -> TestResult {
|
||||||
Ok(())
|
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)?;
|
let actual_json = serde_json::to_string_pretty(&schema)?;
|
||||||
fs::write(format!("tests/actual/{}.json", file), actual_json)?;
|
fs::write(format!("tests/actual/{}.json", file), actual_json)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
mod util;
|
mod util;
|
||||||
use util::*;
|
use util::*;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn uuid08() -> TestResult {
|
|
||||||
test_default_generated_schema::<uuid08::Uuid>("uuid")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn uuid1() -> TestResult {
|
fn uuid1() -> TestResult {
|
||||||
test_default_generated_schema::<uuid1::Uuid>("uuid")
|
test_default_generated_schema::<uuid1::Uuid>("uuid")
|
||||||
|
|
|
@ -325,120 +325,86 @@ impl ValidationAttrs {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_to_schema(&self, schema_expr: &mut TokenStream) {
|
pub fn apply_to_schema(&self, schema_expr: &mut TokenStream) {
|
||||||
if let Some(apply_expr) = self.apply_to_schema_expr() {
|
let setters = self.make_setters(quote!(&mut schema));
|
||||||
*schema_expr = quote! {
|
if !setters.is_empty() {
|
||||||
{
|
*schema_expr = quote!({
|
||||||
let mut schema = #schema_expr;
|
let mut schema = #schema_expr;
|
||||||
#apply_expr
|
#(#setters)*
|
||||||
schema
|
schema
|
||||||
}
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_to_schema_expr(&self) -> Option<TokenStream> {
|
fn make_setters(&self, mut_schema: impl ToTokens) -> Vec<TokenStream> {
|
||||||
let mut array_validation = Vec::new();
|
let mut result = Vec::new();
|
||||||
let mut number_validation = Vec::new();
|
|
||||||
let mut object_validation = Vec::new();
|
|
||||||
let mut string_validation = Vec::new();
|
|
||||||
|
|
||||||
if let Some(length_min) = self.length_min.as_ref().or(self.length_equal.as_ref()) {
|
if let Some(length_min) = self.length_min.as_ref().or(self.length_equal.as_ref()) {
|
||||||
string_validation.push(quote! {
|
result.push(quote! {
|
||||||
validation.min_length = Some(#length_min as u32);
|
schemars::_private::insert_validation_property(#mut_schema, "string", "minLength", #length_min);
|
||||||
});
|
});
|
||||||
array_validation.push(quote! {
|
result.push(quote! {
|
||||||
validation.min_items = Some(#length_min as u32);
|
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()) {
|
if let Some(length_max) = self.length_max.as_ref().or(self.length_equal.as_ref()) {
|
||||||
string_validation.push(quote! {
|
result.push(quote! {
|
||||||
validation.max_length = Some(#length_max as u32);
|
schemars::_private::insert_validation_property(#mut_schema, "string", "maxLength", #length_max);
|
||||||
});
|
});
|
||||||
array_validation.push(quote! {
|
result.push(quote! {
|
||||||
validation.max_items = Some(#length_max as u32);
|
schemars::_private::insert_validation_property(#mut_schema, "array", "maxItems", #length_max);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(range_min) = &self.range_min {
|
if let Some(range_min) = &self.range_min {
|
||||||
number_validation.push(quote! {
|
result.push(quote! {
|
||||||
validation.minimum = Some(#range_min as f64);
|
schemars::_private::insert_validation_property(#mut_schema, "number", "minimum", #range_min);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(range_max) = &self.range_max {
|
if let Some(range_max) = &self.range_max {
|
||||||
number_validation.push(quote! {
|
result.push(quote! {
|
||||||
validation.maximum = Some(#range_max as f64);
|
schemars::_private::insert_validation_property(#mut_schema, "number", "maximum", #range_max);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(regex) = &self.regex {
|
if let Some(regex) = &self.regex {
|
||||||
string_validation.push(quote! {
|
result.push(quote! {
|
||||||
validation.pattern = Some(#regex.to_string());
|
schemars::_private::insert_validation_property(#mut_schema, "string", "pattern", #regex);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(contains) = &self.contains {
|
if let Some(contains) = &self.contains {
|
||||||
object_validation.push(quote! {
|
result.push(quote! {
|
||||||
validation.required.insert(#contains.to_string());
|
schemars::_private::append_required(#mut_schema, #contains);
|
||||||
});
|
});
|
||||||
|
|
||||||
if self.regex.is_none() {
|
if self.regex.is_none() {
|
||||||
let pattern = crate::regex_syntax::escape(contains);
|
let pattern = crate::regex_syntax::escape(contains);
|
||||||
string_validation.push(quote! {
|
result.push(quote! {
|
||||||
validation.pattern = Some(#pattern.to_string());
|
schemars::_private::insert_validation_property(#mut_schema, "string", "pattern", #pattern);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let format = self.format.as_ref().map(|f| {
|
if let Some(format) = &self.format {
|
||||||
let f = f.schema_str();
|
let f = format.schema_str();
|
||||||
quote! {
|
result.push(quote! {
|
||||||
schema_object.format = Some(#f.to_string());
|
schema.ensure_object().insert("format".to_owned(), #f.into());
|
||||||
}
|
})
|
||||||
});
|
};
|
||||||
|
|
||||||
let inner_validation = self
|
if let Some(inner) = &self.inner {
|
||||||
.inner
|
let inner_setters = inner.make_setters(quote!(schema));
|
||||||
.as_deref()
|
if !inner_setters.is_empty() {
|
||||||
.and_then(|inner| inner.apply_to_schema_expr())
|
result.push(quote! {
|
||||||
.map(|apply_expr| {
|
schemars::_private::apply_inner_validation(#mut_schema, |schema| { #(#inner_setters)* });
|
||||||
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
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_lit_into_expr_path(
|
fn parse_lit_into_expr_path(
|
||||||
|
@ -456,59 +422,6 @@ fn parse_lit_into_expr_path(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrap_array_validation(v: Vec<TokenStream>) -> Option<TokenStream> {
|
|
||||||
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<TokenStream>) -> Option<TokenStream> {
|
|
||||||
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<TokenStream>) -> Option<TokenStream> {
|
|
||||||
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<TokenStream>) -> Option<TokenStream> {
|
|
||||||
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<Expr> {
|
fn str_or_num_to_expr(cx: &Ctxt, meta_item_name: &str, expr: Expr) -> Option<Expr> {
|
||||||
// this odd double-parsing is to make `-10` parsed as an Lit instead of an Expr::Unary
|
// 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()) {
|
let lit: Lit = match syn::parse2(expr.to_token_stream()) {
|
||||||
|
|
|
@ -68,11 +68,11 @@ fn derive_json_schema(mut input: syn::DeriveInput, repr: bool) -> syn::Result<To
|
||||||
<#ty as schemars::JsonSchema>::schema_id()
|
<#ty as schemars::JsonSchema>::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)
|
<#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)
|
<#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<To
|
||||||
#schema_id
|
#schema_id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
|
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema {
|
||||||
#schema_expr
|
#schema_expr
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,32 +13,46 @@ pub struct SchemaMetadata<'a> {
|
||||||
|
|
||||||
impl<'a> SchemaMetadata<'a> {
|
impl<'a> SchemaMetadata<'a> {
|
||||||
pub fn apply_to_schema(&self, schema_expr: &mut TokenStream) {
|
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<TokenStream> {
|
||||||
|
let mut setters = Vec::<TokenStream>::new();
|
||||||
|
|
||||||
if let Some(title) = &self.title {
|
if let Some(title) = &self.title {
|
||||||
*schema_expr = quote! {
|
setters.push(quote! {
|
||||||
schemars::_private::metadata::add_title(#schema_expr, #title)
|
obj.insert("title".to_owned(), #title.into());
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
if let Some(description) = &self.description {
|
if let Some(description) = &self.description {
|
||||||
*schema_expr = quote! {
|
setters.push(quote! {
|
||||||
schemars::_private::metadata::add_description(#schema_expr, #description)
|
obj.insert("description".to_owned(), #description.into());
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.deprecated {
|
if self.deprecated {
|
||||||
*schema_expr = quote! {
|
setters.push(quote! {
|
||||||
schemars::_private::metadata::add_deprecated(#schema_expr, true)
|
obj.insert("deprecated".to_owned(), true.into());
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.read_only {
|
if self.read_only {
|
||||||
*schema_expr = quote! {
|
setters.push(quote! {
|
||||||
schemars::_private::metadata::add_read_only(#schema_expr, true)
|
obj.insert("readOnly".to_owned(), true.into());
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
if self.write_only {
|
if self.write_only {
|
||||||
*schema_expr = quote! {
|
setters.push(quote! {
|
||||||
schemars::_private::metadata::add_write_only(#schema_expr, true)
|
obj.insert("writeOnly".to_owned(), true.into());
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.examples.is_empty() {
|
if !self.examples.is_empty() {
|
||||||
|
@ -47,16 +61,19 @@ impl<'a> SchemaMetadata<'a> {
|
||||||
schemars::_serde_json::value::to_value(#eg())
|
schemars::_serde_json::value::to_value(#eg())
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
setters.push(quote! {
|
||||||
*schema_expr = quote! {
|
obj.insert("examples".to_owned(), schemars::_serde_json::Value::Array([#(#examples),*].into_iter().flatten().collect()));
|
||||||
schemars::_private::metadata::add_examples(#schema_expr, [#(#examples),*].into_iter().flatten())
|
});
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(default) = &self.default {
|
if let Some(default) = &self.default {
|
||||||
*schema_expr = quote! {
|
setters.push(quote! {
|
||||||
schemars::_private::metadata::add_default(#schema_expr, #default.and_then(|d| schemars::_schemars_maybe_to_value!(d)))
|
if let Some(default) = #default.and_then(|d| schemars::_schemars_maybe_to_value!(d)) {
|
||||||
};
|
obj.insert("default".to_owned(), default);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setters
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,9 +49,18 @@ pub fn expr_for_repr(cont: &Container) -> Result<TokenStream, syn::Error> {
|
||||||
let enum_ident = &cont.ident;
|
let enum_ident = &cont.ident;
|
||||||
let variant_idents = variants.iter().map(|v| &v.ident);
|
let variant_idents = variants.iter().map(|v| &v.ident);
|
||||||
|
|
||||||
let mut schema_expr = schema_object(quote! {
|
let mut schema_expr = quote!({
|
||||||
instance_type: Some(schemars::schema::InstanceType::Integer.into()),
|
let mut map = schemars::_serde_json::Map::new();
|
||||||
enum_values: Some(vec![#((#enum_ident::#variant_idents as #repr_type).into()),*]),
|
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);
|
cont.attrs.as_metadata().apply_to_schema(&mut schema_expr);
|
||||||
|
@ -118,7 +127,7 @@ fn type_for_schema(with_attr: &WithAttr) -> (syn::Type, Option<TokenStream>) {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
|
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema {
|
||||||
#fun(gen)
|
#fun(gen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,9 +169,18 @@ fn expr_for_external_tagged_enum<'a>(
|
||||||
})
|
})
|
||||||
.partition(|v| v.is_unit() && v.attrs.is_default());
|
.partition(|v| v.is_unit() && v.attrs.is_default());
|
||||||
let unit_names = unit_variants.iter().map(|v| v.name());
|
let unit_names = unit_variants.iter().map(|v| v.name());
|
||||||
let unit_schema = schema_object(quote! {
|
let unit_schema = quote!({
|
||||||
instance_type: Some(schemars::schema::InstanceType::String.into()),
|
let mut map = schemars::_serde_json::Map::new();
|
||||||
enum_values: Some(vec![#(#unit_names.into()),*]),
|
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() {
|
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
|
let (add_content_to_props, add_content_to_required) = content_schema
|
||||||
.map(|content_schema| {
|
.map(|content_schema| {
|
||||||
(
|
(
|
||||||
quote!(props.insert(#content_name.to_owned(), #content_schema);),
|
quote!(#content_name: (#content_schema),),
|
||||||
quote!(required.insert(#content_name.to_owned());),
|
quote!(#content_name,),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let name = variant.name();
|
let name = variant.name();
|
||||||
let tag_schema = schema_object(quote! {
|
let tag_schema = quote! {
|
||||||
instance_type: Some(schemars::schema::InstanceType::String.into()),
|
schemars::json_schema!({
|
||||||
enum_values: Some(vec![#name.into()]),
|
"type": "string",
|
||||||
});
|
"enum": [#name],
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
let set_additional_properties = if deny_unknown_fields {
|
let set_additional_properties = if deny_unknown_fields {
|
||||||
quote! {
|
quote! {
|
||||||
additional_properties: Some(Box::new(false.into())),
|
"additionalProperties": false,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
TokenStream::new()
|
TokenStream::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut outer_schema = schema_object(quote! {
|
let mut outer_schema = quote! {
|
||||||
instance_type: Some(schemars::schema::InstanceType::Object.into()),
|
schemars::json_schema!({
|
||||||
object: Some(Box::new(schemars::schema::ObjectValidation {
|
"type": "object",
|
||||||
properties: {
|
"properties": {
|
||||||
let mut props = schemars::Map::new();
|
#tag_name: (#tag_schema),
|
||||||
props.insert(#tag_name.to_owned(), #tag_schema);
|
|
||||||
#add_content_to_props
|
#add_content_to_props
|
||||||
props
|
|
||||||
},
|
},
|
||||||
required: {
|
"required": [
|
||||||
let mut required = schemars::Set::new();
|
#tag_name,
|
||||||
required.insert(#tag_name.to_owned());
|
|
||||||
#add_content_to_required
|
#add_content_to_required
|
||||||
required
|
],
|
||||||
},
|
|
||||||
// As we're creating a "wrapper" object, we can honor the
|
// As we're creating a "wrapper" object, we can honor the
|
||||||
// disposition of deny_unknown_fields.
|
// disposition of deny_unknown_fields.
|
||||||
#set_additional_properties
|
#set_additional_properties
|
||||||
..Default::default()
|
})
|
||||||
})),
|
};
|
||||||
});
|
|
||||||
|
|
||||||
variant
|
variant
|
||||||
.attrs
|
.attrs
|
||||||
|
@ -333,21 +348,19 @@ fn expr_for_adjacent_tagged_enum<'a>(
|
||||||
/// Callers must determine if all subschemas are mutually exclusive. This can
|
/// 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.
|
/// be done for most tagging regimes by checking that all tag names are unique.
|
||||||
fn variant_subschemas(unique: bool, schemas: Vec<TokenStream>) -> TokenStream {
|
fn variant_subschemas(unique: bool, schemas: Vec<TokenStream>) -> TokenStream {
|
||||||
if unique {
|
let keyword = if unique { "oneOf" } else { "anyOf" };
|
||||||
schema_object(quote! {
|
quote!({
|
||||||
subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
|
let mut map = schemars::_serde_json::Map::new();
|
||||||
one_of: Some(vec![#(#schemas),*]),
|
map.insert(
|
||||||
..Default::default()
|
#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)
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
schema_object(quote! {
|
|
||||||
subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
|
|
||||||
any_of: Some(vec![#(#schemas),*]),
|
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr_for_untagged_enum_variant(variant: &Variant, deny_unknown_fields: bool) -> TokenStream {
|
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;
|
let len = fields.len() as u32;
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
schemars::schema::Schema::Object(
|
schemars::json_schema!({
|
||||||
schemars::schema::SchemaObject {
|
"type": "array",
|
||||||
instance_type: Some(schemars::schema::InstanceType::Array.into()),
|
"items": [#((#fields)),*],
|
||||||
array: Some(Box::new(schemars::schema::ArrayValidation {
|
"minItems": #len,
|
||||||
items: Some(vec![#(#fields),*].into()),
|
"maxItems": #len,
|
||||||
max_items: Some(#len),
|
|
||||||
min_items: Some(#len),
|
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
..Default::default()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -477,7 +485,7 @@ fn expr_for_struct(
|
||||||
quote! {
|
quote! {
|
||||||
{
|
{
|
||||||
#type_def
|
#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 {
|
let set_additional_properties = if deny_unknown_fields {
|
||||||
quote! {
|
quote! {
|
||||||
object_validation.additional_properties = Some(Box::new(false.into()));
|
"additionalProperties": false,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
TokenStream::new()
|
TokenStream::new()
|
||||||
|
@ -510,15 +518,12 @@ fn expr_for_struct(
|
||||||
quote! {
|
quote! {
|
||||||
{
|
{
|
||||||
#set_container_default
|
#set_container_default
|
||||||
let mut schema_object = schemars::schema::SchemaObject {
|
let mut schema = schemars::json_schema!({
|
||||||
instance_type: Some(schemars::schema::InstanceType::Object.into()),
|
"type": "object",
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let object_validation = schema_object.object();
|
|
||||||
#set_additional_properties
|
#set_additional_properties
|
||||||
|
});
|
||||||
#(#properties)*
|
#(#properties)*
|
||||||
schemars::schema::Schema::Object(schema_object)
|
schema #(.flatten(#flattens))*
|
||||||
#(.flatten(#flattens))*
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -578,16 +583,6 @@ fn field_default_expr(field: &Field, container_has_default: bool) -> Option<Toke
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn schema_object(properties: TokenStream) -> TokenStream {
|
|
||||||
quote! {
|
|
||||||
schemars::schema::Schema::Object(
|
|
||||||
schemars::schema::SchemaObject {
|
|
||||||
#properties
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prepend_type_def(type_def: Option<TokenStream>, schema_expr: &mut TokenStream) {
|
fn prepend_type_def(type_def: Option<TokenStream>, schema_expr: &mut TokenStream) {
|
||||||
if let Some(type_def) = type_def {
|
if let Some(type_def) = type_def {
|
||||||
*schema_expr = quote! {
|
*schema_expr = quote! {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue