feat: Implement `prefixItems` keyword
This commit is contained in:
parent
f8b8ec0d6c
commit
0e150641e1
|
@ -38,8 +38,9 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
draft: [draft201909, draft202012]
|
||||
|
||||
name: Test (stable) on ${{ matrix.os}}
|
||||
name: Test ${{ matrix.draft }} (stable) on ${{ matrix.os}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
@ -51,7 +52,7 @@ jobs:
|
|||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
- run: cargo test --no-fail-fast --features draft201909
|
||||
- run: cargo test --no-fail-fast --features ${{ matrix.draft }}
|
||||
working-directory: ./jsonschema
|
||||
|
||||
coverage:
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
- Support for `prefixItems` keyword. [#303](https://github.com/Stranger6667/jsonschema-rs/pull/303)
|
||||
|
||||
## [0.13.1] - 2021-10-28
|
||||
|
||||
### Fixed
|
||||
|
@ -14,7 +16,7 @@
|
|||
|
||||
- `uuid` format validator. [#266](https://github.com/Stranger6667/jsonschema-rs/issues/266)
|
||||
- `duration` format validator. [#265](https://github.com/Stranger6667/jsonschema-rs/issues/265)
|
||||
- Collect annotations whilst evaulating schemas. [#262](https://github.com/Stranger6667/jsonschema-rs/issues/262)
|
||||
- Collect annotations whilst evaluating schemas. [#262](https://github.com/Stranger6667/jsonschema-rs/issues/262)
|
||||
- Option to turn off processing of the `format` keyword. [#261](https://github.com/Stranger6667/jsonschema-rs/issues/261)
|
||||
- `basic` & `flag` output formatting styles. [#100](https://github.com/Stranger6667/jsonschema-rs/issues/100)
|
||||
- Support for `dependentRequired` & `dependentSchemas` keywords. [#286](https://github.com/Stranger6667/jsonschema-rs/issues/286)
|
||||
|
|
|
@ -19,6 +19,7 @@ name = "jsonschema"
|
|||
cli = ["structopt"]
|
||||
default = ["reqwest", "cli"]
|
||||
draft201909 = []
|
||||
draft202012 = []
|
||||
reqwest-native-tls = ["reqwest/native-tls"]
|
||||
reqwest-native-tls-alpn = ["reqwest/native-tls-alpn"]
|
||||
reqwest-native-tls-vendored = ["reqwest/native-tls-vendored"]
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://json-schema.org/draft/2020-12/meta/applicator",
|
||||
"$vocabulary": {
|
||||
"https://json-schema.org/draft/2020-12/vocab/applicator": true
|
||||
},
|
||||
"$dynamicAnchor": "meta",
|
||||
|
||||
"title": "Applicator vocabulary meta-schema",
|
||||
"type": ["object", "boolean"],
|
||||
"properties": {
|
||||
"prefixItems": { "$ref": "#/$defs/schemaArray" },
|
||||
"items": { "$dynamicRef": "#meta" },
|
||||
"contains": { "$dynamicRef": "#meta" },
|
||||
"additionalProperties": { "$dynamicRef": "#meta" },
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "$dynamicRef": "#meta" },
|
||||
"default": {}
|
||||
},
|
||||
"patternProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "$dynamicRef": "#meta" },
|
||||
"propertyNames": { "format": "regex" },
|
||||
"default": {}
|
||||
},
|
||||
"dependentSchemas": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "$dynamicRef": "#meta" },
|
||||
"default": {}
|
||||
},
|
||||
"propertyNames": { "$dynamicRef": "#meta" },
|
||||
"if": { "$dynamicRef": "#meta" },
|
||||
"then": { "$dynamicRef": "#meta" },
|
||||
"else": { "$dynamicRef": "#meta" },
|
||||
"allOf": { "$ref": "#/$defs/schemaArray" },
|
||||
"anyOf": { "$ref": "#/$defs/schemaArray" },
|
||||
"oneOf": { "$ref": "#/$defs/schemaArray" },
|
||||
"not": { "$dynamicRef": "#meta" }
|
||||
},
|
||||
"$defs": {
|
||||
"schemaArray": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "$dynamicRef": "#meta" }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://json-schema.org/draft/2020-12/meta/content",
|
||||
"$vocabulary": {
|
||||
"https://json-schema.org/draft/2020-12/vocab/content": true
|
||||
},
|
||||
"$dynamicAnchor": "meta",
|
||||
|
||||
"title": "Content vocabulary meta-schema",
|
||||
|
||||
"type": ["object", "boolean"],
|
||||
"properties": {
|
||||
"contentEncoding": { "type": "string" },
|
||||
"contentMediaType": { "type": "string" },
|
||||
"contentSchema": { "$dynamicRef": "#meta" }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://json-schema.org/draft/2020-12/meta/core",
|
||||
"$vocabulary": {
|
||||
"https://json-schema.org/draft/2020-12/vocab/core": true
|
||||
},
|
||||
"$dynamicAnchor": "meta",
|
||||
|
||||
"title": "Core vocabulary meta-schema",
|
||||
"type": ["object", "boolean"],
|
||||
"properties": {
|
||||
"$id": {
|
||||
"$ref": "#/$defs/uriReferenceString",
|
||||
"$comment": "Non-empty fragments not allowed.",
|
||||
"pattern": "^[^#]*#?$"
|
||||
},
|
||||
"$schema": { "$ref": "#/$defs/uriString" },
|
||||
"$ref": { "$ref": "#/$defs/uriReferenceString" },
|
||||
"$anchor": { "$ref": "#/$defs/anchorString" },
|
||||
"$dynamicRef": { "$ref": "#/$defs/uriReferenceString" },
|
||||
"$dynamicAnchor": { "$ref": "#/$defs/anchorString" },
|
||||
"$vocabulary": {
|
||||
"type": "object",
|
||||
"propertyNames": { "$ref": "#/$defs/uriString" },
|
||||
"additionalProperties": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"$comment": {
|
||||
"type": "string"
|
||||
},
|
||||
"$defs": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "$dynamicRef": "#meta" }
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"anchorString": {
|
||||
"type": "string",
|
||||
"pattern": "^[A-Za-z_][-A-Za-z0-9._]*$"
|
||||
},
|
||||
"uriString": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"uriReferenceString": {
|
||||
"type": "string",
|
||||
"format": "uri-reference"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://json-schema.org/draft/2020-12/meta/format-annotation",
|
||||
"$vocabulary": {
|
||||
"https://json-schema.org/draft/2020-12/vocab/format-annotation": true
|
||||
},
|
||||
"$dynamicAnchor": "meta",
|
||||
|
||||
"title": "Format vocabulary meta-schema for annotation results",
|
||||
"type": ["object", "boolean"],
|
||||
"properties": {
|
||||
"format": { "type": "string" }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://json-schema.org/draft/2020-12/meta/meta-data",
|
||||
"$vocabulary": {
|
||||
"https://json-schema.org/draft/2020-12/vocab/meta-data": true
|
||||
},
|
||||
"$dynamicAnchor": "meta",
|
||||
|
||||
"title": "Meta-data vocabulary meta-schema",
|
||||
|
||||
"type": ["object", "boolean"],
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": true,
|
||||
"deprecated": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"readOnly": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"writeOnly": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"examples": {
|
||||
"type": "array",
|
||||
"items": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://json-schema.org/draft/2020-12/meta/unevaluated",
|
||||
"$vocabulary": {
|
||||
"https://json-schema.org/draft/2020-12/vocab/unevaluated": true
|
||||
},
|
||||
"$dynamicAnchor": "meta",
|
||||
|
||||
"title": "Unevaluated applicator vocabulary meta-schema",
|
||||
"type": ["object", "boolean"],
|
||||
"properties": {
|
||||
"unevaluatedItems": { "$dynamicRef": "#meta" },
|
||||
"unevaluatedProperties": { "$dynamicRef": "#meta" }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://json-schema.org/draft/2020-12/meta/validation",
|
||||
"$vocabulary": {
|
||||
"https://json-schema.org/draft/2020-12/vocab/validation": true
|
||||
},
|
||||
"$dynamicAnchor": "meta",
|
||||
|
||||
"title": "Validation vocabulary meta-schema",
|
||||
"type": ["object", "boolean"],
|
||||
"properties": {
|
||||
"type": {
|
||||
"anyOf": [
|
||||
{ "$ref": "#/$defs/simpleTypes" },
|
||||
{
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/simpleTypes" },
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"const": true,
|
||||
"enum": {
|
||||
"type": "array",
|
||||
"items": true
|
||||
},
|
||||
"multipleOf": {
|
||||
"type": "number",
|
||||
"exclusiveMinimum": 0
|
||||
},
|
||||
"maximum": {
|
||||
"type": "number"
|
||||
},
|
||||
"exclusiveMaximum": {
|
||||
"type": "number"
|
||||
},
|
||||
"minimum": {
|
||||
"type": "number"
|
||||
},
|
||||
"exclusiveMinimum": {
|
||||
"type": "number"
|
||||
},
|
||||
"maxLength": { "$ref": "#/$defs/nonNegativeInteger" },
|
||||
"minLength": { "$ref": "#/$defs/nonNegativeIntegerDefault0" },
|
||||
"pattern": {
|
||||
"type": "string",
|
||||
"format": "regex"
|
||||
},
|
||||
"maxItems": { "$ref": "#/$defs/nonNegativeInteger" },
|
||||
"minItems": { "$ref": "#/$defs/nonNegativeIntegerDefault0" },
|
||||
"uniqueItems": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"maxContains": { "$ref": "#/$defs/nonNegativeInteger" },
|
||||
"minContains": {
|
||||
"$ref": "#/$defs/nonNegativeInteger",
|
||||
"default": 1
|
||||
},
|
||||
"maxProperties": { "$ref": "#/$defs/nonNegativeInteger" },
|
||||
"minProperties": { "$ref": "#/$defs/nonNegativeIntegerDefault0" },
|
||||
"required": { "$ref": "#/$defs/stringArray" },
|
||||
"dependentRequired": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/$defs/stringArray"
|
||||
}
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"nonNegativeInteger": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"nonNegativeIntegerDefault0": {
|
||||
"$ref": "#/$defs/nonNegativeInteger",
|
||||
"default": 0
|
||||
},
|
||||
"simpleTypes": {
|
||||
"enum": [
|
||||
"array",
|
||||
"boolean",
|
||||
"integer",
|
||||
"null",
|
||||
"number",
|
||||
"object",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"stringArray": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"uniqueItems": true,
|
||||
"default": []
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$vocabulary": {
|
||||
"https://json-schema.org/draft/2020-12/vocab/core": true,
|
||||
"https://json-schema.org/draft/2020-12/vocab/applicator": true,
|
||||
"https://json-schema.org/draft/2020-12/vocab/unevaluated": true,
|
||||
"https://json-schema.org/draft/2020-12/vocab/validation": true,
|
||||
"https://json-schema.org/draft/2020-12/vocab/meta-data": true,
|
||||
"https://json-schema.org/draft/2020-12/vocab/format-annotation": true,
|
||||
"https://json-schema.org/draft/2020-12/vocab/content": true
|
||||
},
|
||||
"$dynamicAnchor": "meta",
|
||||
|
||||
"title": "Core and Validation specifications meta-schema",
|
||||
"allOf": [
|
||||
{"$ref": "meta/core"},
|
||||
{"$ref": "meta/applicator"},
|
||||
{"$ref": "meta/unevaluated"},
|
||||
{"$ref": "meta/validation"},
|
||||
{"$ref": "meta/meta-data"},
|
||||
{"$ref": "meta/format-annotation"},
|
||||
{"$ref": "meta/content"}
|
||||
],
|
||||
"type": ["object", "boolean"],
|
||||
"$comment": "This meta-schema also defines keywords that have appeared in previous drafts in order to prevent incompatible extensions as they remain in common use.",
|
||||
"properties": {
|
||||
"definitions": {
|
||||
"$comment": "\"definitions\" has been replaced by \"$defs\".",
|
||||
"type": "object",
|
||||
"additionalProperties": { "$dynamicRef": "#meta" },
|
||||
"deprecated": true,
|
||||
"default": {}
|
||||
},
|
||||
"dependencies": {
|
||||
"$comment": "\"dependencies\" has been split and replaced by \"dependentSchemas\" and \"dependentRequired\" in order to serve their differing semantics.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"anyOf": [
|
||||
{ "$dynamicRef": "#meta" },
|
||||
{ "$ref": "meta/validation#/$defs/stringArray" }
|
||||
]
|
||||
},
|
||||
"deprecated": true,
|
||||
"default": {}
|
||||
},
|
||||
"$recursiveAnchor": {
|
||||
"$comment": "\"$recursiveAnchor\" has been replaced by \"$dynamicAnchor\".",
|
||||
"$ref": "meta/core#/$defs/anchorString",
|
||||
"deprecated": true
|
||||
},
|
||||
"$recursiveRef": {
|
||||
"$comment": "\"$recursiveRef\" has been replaced by \"$dynamicRef\".",
|
||||
"$ref": "meta/core#/$defs/uriReferenceString",
|
||||
"deprecated": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,6 +24,14 @@ lazy_static::lazy_static! {
|
|||
static ref DRAFT201909_FORMAT:serde_json::Value = serde_json::from_str(include_str!("../../meta_schemas/draft2019-09/meta/format.json")).expect("Valid schema!");
|
||||
static ref DRAFT201909_META_DATA:serde_json::Value = serde_json::from_str(include_str!("../../meta_schemas/draft2019-09/meta/meta-data.json")).expect("Valid schema!");
|
||||
static ref DRAFT201909_VALIDATION:serde_json::Value = serde_json::from_str(include_str!("../../meta_schemas/draft2019-09/meta/validation.json")).expect("Valid schema!");
|
||||
static ref DRAFT202012:serde_json::Value = serde_json::from_str(include_str!("../../meta_schemas/draft2020-12/schema.json")).expect("Valid schema!");
|
||||
static ref DRAFT202012_CORE:serde_json::Value = serde_json::from_str(include_str!("../../meta_schemas/draft2020-12/meta/core.json")).expect("Valid schema!");
|
||||
static ref DRAFT202012_APPLICATOR:serde_json::Value = serde_json::from_str(include_str!("../../meta_schemas/draft2020-12/meta/applicator.json")).expect("Valid schema!");
|
||||
static ref DRAFT202012_UNEVALUATED:serde_json::Value = serde_json::from_str(include_str!("../../meta_schemas/draft2020-12/meta/unevaluated.json")).expect("Valid schema!");
|
||||
static ref DRAFT202012_VALIDATION:serde_json::Value = serde_json::from_str(include_str!("../../meta_schemas/draft2020-12/meta/validation.json")).expect("Valid schema!");
|
||||
static ref DRAFT202012_META_DATA:serde_json::Value = serde_json::from_str(include_str!("../../meta_schemas/draft2020-12/meta/meta-data.json")).expect("Valid schema!");
|
||||
static ref DRAFT202012_FORMAT_ANNOTATION:serde_json::Value = serde_json::from_str(include_str!("../../meta_schemas/draft2020-12/meta/format-annotation.json")).expect("Valid schema!");
|
||||
static ref DRAFT202012_CONTENT:serde_json::Value = serde_json::from_str(include_str!("../../meta_schemas/draft2020-12/meta/content.json")).expect("Valid schema!");
|
||||
|
||||
static ref META_SCHEMAS: AHashMap<String, Arc<serde_json::Value>> = {
|
||||
let mut store = AHashMap::with_capacity(3);
|
||||
|
@ -70,6 +78,41 @@ lazy_static::lazy_static! {
|
|||
Arc::new(DRAFT201909_VALIDATION.clone())
|
||||
);
|
||||
}
|
||||
#[cfg(feature = "draft202012")]
|
||||
{
|
||||
store.insert(
|
||||
"https://json-schema.org/draft/2020-12/schema".to_string(),
|
||||
Arc::new(DRAFT202012.clone())
|
||||
);
|
||||
store.insert(
|
||||
"https://json-schema.org/draft/2020-12/meta/core".to_string(),
|
||||
Arc::new(DRAFT202012_CORE.clone())
|
||||
);
|
||||
store.insert(
|
||||
"https://json-schema.org/draft/2020-12/meta/applicator".to_string(),
|
||||
Arc::new(DRAFT202012_APPLICATOR.clone())
|
||||
);
|
||||
store.insert(
|
||||
"https://json-schema.org/draft/2020-12/meta/unevaluated".to_string(),
|
||||
Arc::new(DRAFT202012_UNEVALUATED.clone())
|
||||
);
|
||||
store.insert(
|
||||
"https://json-schema.org/draft/2020-12/meta/validation".to_string(),
|
||||
Arc::new(DRAFT202012_VALIDATION.clone())
|
||||
);
|
||||
store.insert(
|
||||
"https://json-schema.org/draft/2020-12/meta/meta-data".to_string(),
|
||||
Arc::new(DRAFT202012_META_DATA.clone())
|
||||
);
|
||||
store.insert(
|
||||
"https://json-schema.org/draft/2020-12/meta/format-annotation".to_string(),
|
||||
Arc::new(DRAFT202012_FORMAT_ANNOTATION.clone())
|
||||
);
|
||||
store.insert(
|
||||
"https://json-schema.org/draft/2020-12/meta/content".to_string(),
|
||||
Arc::new(DRAFT202012_CONTENT.clone())
|
||||
);
|
||||
}
|
||||
store
|
||||
};
|
||||
|
||||
|
@ -119,6 +162,46 @@ lazy_static::lazy_static! {
|
|||
.compile(&DRAFT201909)
|
||||
.expect(EXPECT_MESSAGE)
|
||||
);
|
||||
#[cfg(feature = "draft202012")]
|
||||
store.insert(
|
||||
schemas::Draft::Draft202012,
|
||||
JSONSchema::options()
|
||||
.without_schema_validation()
|
||||
.with_document(
|
||||
"https://json-schema.org/draft/2020-12/meta/applicator".to_string(),
|
||||
DRAFT202012_APPLICATOR.clone()
|
||||
)
|
||||
.with_document(
|
||||
"https://json-schema.org/draft/2020-12/meta/core".to_string(),
|
||||
DRAFT202012_CORE.clone()
|
||||
)
|
||||
.with_document(
|
||||
"https://json-schema.org/draft/2020-12/meta/applicator".to_string(),
|
||||
DRAFT202012_APPLICATOR.clone()
|
||||
)
|
||||
.with_document(
|
||||
"https://json-schema.org/draft/2020-12/meta/unevaluated".to_string(),
|
||||
DRAFT202012_UNEVALUATED.clone()
|
||||
)
|
||||
.with_document(
|
||||
"https://json-schema.org/draft/2020-12/meta/validation".to_string(),
|
||||
DRAFT202012_VALIDATION.clone()
|
||||
)
|
||||
.with_document(
|
||||
"https://json-schema.org/draft/2020-12/meta/meta-data".to_string(),
|
||||
DRAFT202012_META_DATA.clone()
|
||||
)
|
||||
.with_document(
|
||||
"https://json-schema.org/draft/2020-12/meta/format-annotation".to_string(),
|
||||
DRAFT202012_FORMAT_ANNOTATION.clone()
|
||||
)
|
||||
.with_document(
|
||||
"https://json-schema.org/draft/2020-12/meta/content".to_string(),
|
||||
DRAFT202012_CONTENT.clone()
|
||||
)
|
||||
.compile(&DRAFT202012)
|
||||
.expect(EXPECT_MESSAGE)
|
||||
);
|
||||
store
|
||||
};
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
#[cfg(feature = "draft201909")]
|
||||
#[cfg(any(feature = "draft201909", feature = "draft202012"))]
|
||||
use super::helpers::map_get_u64;
|
||||
|
||||
pub(crate) struct ContainsValidator {
|
||||
|
@ -423,8 +423,22 @@ pub(crate) fn compile<'a>(
|
|||
Draft::Draft4 | Draft::Draft6 | Draft::Draft7 => {
|
||||
Some(ContainsValidator::compile(schema, context))
|
||||
}
|
||||
#[cfg(feature = "draft201909")]
|
||||
Draft::Draft201909 => {
|
||||
#[cfg(all(feature = "draft201909", feature = "draft202012"))]
|
||||
Draft::Draft201909 | Draft::Draft202012 => compile_contains(parent, schema, context),
|
||||
#[cfg(all(feature = "draft201909", not(feature = "draft202012")))]
|
||||
Draft::Draft201909 => compile_contains(parent, schema, context),
|
||||
#[cfg(all(feature = "draft202012", not(feature = "draft201909")))]
|
||||
Draft::Draft202012 => compile_contains(parent, schema, context),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "draft201909", feature = "draft202012"))]
|
||||
#[inline]
|
||||
fn compile_contains<'a>(
|
||||
parent: &'a Map<String, Value>,
|
||||
schema: &'a Value,
|
||||
context: &CompilationContext,
|
||||
) -> Option<CompilationResult<'a>> {
|
||||
let min_contains = match map_get_u64(parent, context, "minContains").transpose() {
|
||||
Ok(n) => n,
|
||||
Err(err) => return Some(Err(err)),
|
||||
|
@ -435,15 +449,11 @@ pub(crate) fn compile<'a>(
|
|||
};
|
||||
|
||||
match (min_contains, max_contains) {
|
||||
(Some(min), Some(max)) => {
|
||||
Some(MinMaxContainsValidator::compile(schema, context, min, max))
|
||||
}
|
||||
(Some(min), Some(max)) => Some(MinMaxContainsValidator::compile(schema, context, min, max)),
|
||||
(Some(min), None) => Some(MinContainsValidator::compile(schema, context, min)),
|
||||
(None, Some(max)) => Some(MaxContainsValidator::compile(schema, context, max)),
|
||||
(None, None) => Some(ContainsValidator::compile(schema, context)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -75,6 +75,7 @@ impl core::fmt::Display for ItemsArrayValidator {
|
|||
pub(crate) struct ItemsObjectValidator {
|
||||
node: SchemaNode,
|
||||
}
|
||||
|
||||
impl ItemsObjectValidator {
|
||||
#[inline]
|
||||
pub(crate) fn compile<'a>(
|
||||
|
@ -131,8 +132,9 @@ impl Validate for ItemsObjectValidator {
|
|||
let mut output: PartialApplication = results.into_iter().collect();
|
||||
// Per draft 2020-12 section https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.10.3.1.2
|
||||
// we must produce an annotation with a boolean value indicating whether the subschema
|
||||
// was applied to any positions in the underlying array. As we have not yet implemented
|
||||
// prefixItems this is true if there are any items in the instance.
|
||||
// was applied to any positions in the underlying array. Since the struct
|
||||
// `ItemsObjectValidator` is not used when prefixItems is defined, this is true if
|
||||
// there are any items in the instance.
|
||||
let schema_was_applied = !items.is_empty();
|
||||
output.annotate(serde_json::json! {schema_was_applied}.into());
|
||||
output
|
||||
|
@ -148,21 +150,111 @@ impl core::fmt::Display for ItemsObjectValidator {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ItemsObjectSkipPrefixValidator {
|
||||
node: SchemaNode,
|
||||
skip_prefix: usize,
|
||||
}
|
||||
|
||||
impl ItemsObjectSkipPrefixValidator {
|
||||
#[inline]
|
||||
pub(crate) fn compile<'a>(
|
||||
schema: &'a Value,
|
||||
skip_prefix: usize,
|
||||
context: &CompilationContext,
|
||||
) -> CompilationResult<'a> {
|
||||
let keyword_context = context.with_path("items");
|
||||
let node = compile_validators(schema, &keyword_context)?;
|
||||
Ok(Box::new(ItemsObjectSkipPrefixValidator {
|
||||
node,
|
||||
skip_prefix,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Validate for ItemsObjectSkipPrefixValidator {
|
||||
fn is_valid(&self, schema: &JSONSchema, instance: &Value) -> bool {
|
||||
if let Value::Array(items) = instance {
|
||||
items
|
||||
.iter()
|
||||
.skip(self.skip_prefix)
|
||||
.all(|i| self.node.is_valid(schema, i))
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_collect)]
|
||||
fn validate<'a, 'b>(
|
||||
&self,
|
||||
schema: &'a JSONSchema,
|
||||
instance: &'b Value,
|
||||
instance_path: &InstancePath,
|
||||
) -> ErrorIterator<'b> {
|
||||
if let Value::Array(items) = instance {
|
||||
let errors: Vec<_> = items
|
||||
.iter()
|
||||
.skip(self.skip_prefix)
|
||||
.enumerate()
|
||||
.flat_map(move |(idx, item)| {
|
||||
self.node
|
||||
.validate(schema, item, &instance_path.push(idx + self.skip_prefix))
|
||||
})
|
||||
.collect();
|
||||
Box::new(errors.into_iter())
|
||||
} else {
|
||||
no_error()
|
||||
}
|
||||
}
|
||||
|
||||
fn apply<'a>(
|
||||
&'a self,
|
||||
schema: &JSONSchema,
|
||||
instance: &Value,
|
||||
instance_path: &InstancePath,
|
||||
) -> PartialApplication<'a> {
|
||||
if let Value::Array(items) = instance {
|
||||
let mut results = Vec::with_capacity(items.len() - self.skip_prefix);
|
||||
for (idx, item) in items.iter().skip(self.skip_prefix).enumerate() {
|
||||
let path = instance_path.push(idx + self.skip_prefix);
|
||||
results.push(self.node.apply_rooted(schema, item, &path));
|
||||
}
|
||||
let mut output: PartialApplication = results.into_iter().collect();
|
||||
// Per draft 2020-12 section https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.10.3.1.2
|
||||
// we must produce an annotation with a boolean value indicating whether the subschema
|
||||
// was applied to any positions in the underlying array.
|
||||
let schema_was_applied = items.len() > self.skip_prefix;
|
||||
output.annotate(serde_json::json! {schema_was_applied}.into());
|
||||
output
|
||||
} else {
|
||||
PartialApplication::valid_empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for ItemsObjectSkipPrefixValidator {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "items: {}", format_validators(self.node.validators()))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn compile<'a>(
|
||||
_: &'a Map<String, Value>,
|
||||
parent: &'a Map<String, Value>,
|
||||
schema: &'a Value,
|
||||
context: &CompilationContext,
|
||||
) -> Option<CompilationResult<'a>> {
|
||||
match schema {
|
||||
Value::Array(items) => Some(ItemsArrayValidator::compile(items, context)),
|
||||
Value::Object(_) => Some(ItemsObjectValidator::compile(schema, context)),
|
||||
Value::Bool(value) => {
|
||||
if *value {
|
||||
None
|
||||
} else {
|
||||
Some(ItemsObjectValidator::compile(schema, context))
|
||||
Value::Object(_) | Value::Bool(false) => {
|
||||
#[cfg(feature = "draft202012")]
|
||||
if let Some(Value::Array(prefix_items)) = parent.get("prefixItems") {
|
||||
return Some(ItemsObjectSkipPrefixValidator::compile(
|
||||
schema,
|
||||
prefix_items.len(),
|
||||
context,
|
||||
));
|
||||
}
|
||||
Some(ItemsObjectValidator::compile(schema, context))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ pub(crate) mod not;
|
|||
pub(crate) mod one_of;
|
||||
pub(crate) mod pattern;
|
||||
pub(crate) mod pattern_properties;
|
||||
pub(crate) mod prefix_items;
|
||||
pub(crate) mod properties;
|
||||
pub(crate) mod property_names;
|
||||
pub(crate) mod ref_;
|
||||
|
|
|
@ -0,0 +1,262 @@
|
|||
use crate::{
|
||||
compilation::{compile_validators, context::CompilationContext, JSONSchema},
|
||||
error::{no_error, ErrorIterator, ValidationError},
|
||||
paths::{InstancePath, JSONPointer},
|
||||
primitive_type::PrimitiveType,
|
||||
schema_node::SchemaNode,
|
||||
validator::{format_iter_of_validators, PartialApplication, Validate},
|
||||
};
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
use super::CompilationResult;
|
||||
|
||||
pub(crate) struct PrefixItemsValidator {
|
||||
schemas: Vec<SchemaNode>,
|
||||
}
|
||||
|
||||
impl PrefixItemsValidator {
|
||||
#[inline]
|
||||
pub(crate) fn compile<'a>(
|
||||
items: &'a [Value],
|
||||
context: &CompilationContext,
|
||||
) -> CompilationResult<'a> {
|
||||
let keyword_context = context.with_path("prefixItems");
|
||||
let mut schemas = Vec::with_capacity(items.len());
|
||||
for (idx, item) in items.iter().enumerate() {
|
||||
let item_context = keyword_context.with_path(idx);
|
||||
let validators = compile_validators(item, &item_context)?;
|
||||
schemas.push(validators)
|
||||
}
|
||||
Ok(Box::new(PrefixItemsValidator { schemas }))
|
||||
}
|
||||
}
|
||||
|
||||
impl Validate for PrefixItemsValidator {
|
||||
fn is_valid(&self, schema: &JSONSchema, instance: &Value) -> bool {
|
||||
if let Value::Array(items) = instance {
|
||||
self.schemas
|
||||
.iter()
|
||||
.zip(items.iter())
|
||||
.all(|(n, i)| n.is_valid(schema, i))
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_collect)]
|
||||
fn validate<'a, 'b>(
|
||||
&self,
|
||||
schema: &'a JSONSchema,
|
||||
instance: &'b Value,
|
||||
instance_path: &InstancePath,
|
||||
) -> ErrorIterator<'b> {
|
||||
if let Value::Array(items) = instance {
|
||||
let errors: Vec<_> = self
|
||||
.schemas
|
||||
.iter()
|
||||
.zip(items.iter())
|
||||
.enumerate()
|
||||
.flat_map(|(idx, (n, i))| n.validate(schema, i, &instance_path.push(idx)))
|
||||
.collect();
|
||||
Box::new(errors.into_iter())
|
||||
} else {
|
||||
no_error()
|
||||
}
|
||||
}
|
||||
|
||||
fn apply<'a>(
|
||||
&'a self,
|
||||
schema: &JSONSchema,
|
||||
instance: &Value,
|
||||
instance_path: &InstancePath,
|
||||
) -> PartialApplication<'a> {
|
||||
if let Value::Array(items) = instance {
|
||||
if !items.is_empty() {
|
||||
let validate_total = self.schemas.len();
|
||||
let mut results = Vec::with_capacity(validate_total);
|
||||
let mut max_index_applied = 0;
|
||||
for (idx, (schema_node, item)) in self.schemas.iter().zip(items.iter()).enumerate()
|
||||
{
|
||||
let path = instance_path.push(idx);
|
||||
results.push(schema_node.apply_rooted(schema, item, &path));
|
||||
max_index_applied = idx;
|
||||
}
|
||||
// Per draft 2020-12 section https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.10.3.1.1
|
||||
// we must produce an annotation with the largest index of the underlying
|
||||
// array which the subschema was applied. The value MAY be a boolean true if
|
||||
// a subschema was applied to every index of the instance.
|
||||
let schema_was_applied: Value = if results.len() == items.len() {
|
||||
true.into()
|
||||
} else {
|
||||
max_index_applied.into()
|
||||
};
|
||||
let mut output: PartialApplication = results.into_iter().collect();
|
||||
output.annotate(schema_was_applied.into());
|
||||
return output;
|
||||
}
|
||||
}
|
||||
PartialApplication::valid_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for PrefixItemsValidator {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"prefixItems: [{}]",
|
||||
format_iter_of_validators(self.schemas.iter().map(SchemaNode::validators))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn compile<'a>(
|
||||
_: &'a Map<String, Value>,
|
||||
schema: &'a Value,
|
||||
context: &CompilationContext,
|
||||
) -> Option<CompilationResult<'a>> {
|
||||
if let Value::Array(items) = schema {
|
||||
Some(PrefixItemsValidator::compile(items, context))
|
||||
} else {
|
||||
Some(Err(ValidationError::single_type_error(
|
||||
JSONPointer::default(),
|
||||
context.clone().into_pointer(),
|
||||
schema,
|
||||
PrimitiveType::Array,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "draft202012"))]
|
||||
mod tests {
|
||||
use crate::compilation::JSONSchema;
|
||||
use crate::tests_util;
|
||||
use serde_json::{json, Value};
|
||||
use test_case::test_case;
|
||||
|
||||
#[test_case(&json!({"prefixItems": [{"type": "integer"}, {"maximum": 5}]}), &json!(["string"]), "/prefixItems/0/type")]
|
||||
#[test_case(&json!({"prefixItems": [{"type": "integer"}, {"maximum": 5}]}), &json!([42, 42]), "/prefixItems/1/maximum")]
|
||||
#[test_case(&json!({"prefixItems": [{"type": "integer"}, {"maximum": 5}], "items": {"type": "boolean"}}), &json!([42, 1, 42]), "/items/type")]
|
||||
#[test_case(&json!({"prefixItems": [{"type": "integer"}, {"maximum": 5}], "items": {"type": "boolean"}}), &json!([42, 42, true]), "/prefixItems/1/maximum")]
|
||||
fn schema_path(schema: &Value, instance: &Value, expected: &str) {
|
||||
tests_util::assert_schema_path(schema, instance, expected)
|
||||
}
|
||||
|
||||
#[test_case(&json!({"prefixItems": [{"type": "integer"}, {"maximum": 5}]}), "prefixItems: [{type: integer}, {maximum: 5}]")]
|
||||
fn debug_representation(schema: &Value, expected: &str) {
|
||||
let compiled = JSONSchema::compile(schema).unwrap();
|
||||
assert_eq!(
|
||||
format!("{:?}", compiled.node.validators().next().unwrap()),
|
||||
expected
|
||||
);
|
||||
}
|
||||
|
||||
#[test_case{
|
||||
&json!({
|
||||
"type": "array",
|
||||
"prefixItems": [
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}),
|
||||
&json!{[]},
|
||||
&json!({
|
||||
"valid": true,
|
||||
"annotations": []
|
||||
}); "valid prefixItems empty array"
|
||||
}]
|
||||
#[test_case{
|
||||
&json!({
|
||||
"type": "array",
|
||||
"prefixItems": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
}
|
||||
]
|
||||
}),
|
||||
&json!{["string", 1]},
|
||||
&json!({
|
||||
"valid": true,
|
||||
"annotations": [
|
||||
{
|
||||
"keywordLocation": "/prefixItems",
|
||||
"instanceLocation": "",
|
||||
"annotations": true
|
||||
},
|
||||
]
|
||||
}); "prefixItems valid items"
|
||||
}]
|
||||
#[test_case{
|
||||
&json!({
|
||||
"type": "array",
|
||||
"prefixItems": [
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}),
|
||||
&json!{["string", 1]},
|
||||
&json!({
|
||||
"valid": true,
|
||||
"annotations": [
|
||||
{
|
||||
"keywordLocation": "/prefixItems",
|
||||
"instanceLocation": "",
|
||||
"annotations": 0
|
||||
},
|
||||
]
|
||||
}); "prefixItems valid mixed items"
|
||||
}]
|
||||
#[test_case{
|
||||
&json!({
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "number",
|
||||
"annotation": "value"
|
||||
},
|
||||
"prefixItems": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
}),
|
||||
&json!{["string", true, 2, 3]},
|
||||
&json!({
|
||||
"valid": true,
|
||||
"annotations": [
|
||||
{
|
||||
"keywordLocation": "/prefixItems",
|
||||
"instanceLocation": "",
|
||||
"annotations": 1
|
||||
},
|
||||
{
|
||||
"keywordLocation": "/items",
|
||||
"instanceLocation": "",
|
||||
"annotations": true
|
||||
},
|
||||
{
|
||||
"annotations": {"annotation": "value" },
|
||||
"instanceLocation": "/2",
|
||||
"keywordLocation": "/items"
|
||||
},
|
||||
{
|
||||
"annotations": {"annotation": "value" },
|
||||
"instanceLocation": "/3",
|
||||
"keywordLocation": "/items"
|
||||
}
|
||||
]
|
||||
}); "valid prefixItems with mixed items"
|
||||
}]
|
||||
fn test_basic_output(schema_json: &Value, instance: &Value, expected_output: &Value) {
|
||||
let schema = JSONSchema::options().compile(schema_json).unwrap();
|
||||
let output_json = serde_json::to_value(schema.apply(instance).basic()).unwrap();
|
||||
assert_eq!(&output_json, expected_output);
|
||||
}
|
||||
}
|
|
@ -14,6 +14,10 @@ pub enum Draft {
|
|||
#[cfg(feature = "draft201909")]
|
||||
/// JSON Schema Draft 2019-09
|
||||
Draft201909,
|
||||
|
||||
#[cfg(feature = "draft202012")]
|
||||
/// JSON Schema Draft 2020-12
|
||||
Draft202012,
|
||||
}
|
||||
|
||||
impl Default for Draft {
|
||||
|
@ -26,8 +30,12 @@ impl Draft {
|
|||
pub(crate) const fn validate_formats_by_default(self) -> bool {
|
||||
match self {
|
||||
Draft::Draft4 | Draft::Draft6 | Draft::Draft7 => true,
|
||||
#[cfg(feature = "draft201909")]
|
||||
#[cfg(all(feature = "draft201909", feature = "draft202012"))]
|
||||
Draft::Draft201909 | Draft::Draft202012 => false,
|
||||
#[cfg(all(feature = "draft201909", not(feature = "draft202012")))]
|
||||
Draft::Draft201909 => false,
|
||||
#[cfg(all(feature = "draft202012", not(feature = "draft201909")))]
|
||||
Draft::Draft202012 => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,12 +59,16 @@ impl Draft {
|
|||
Draft::Draft6 | Draft::Draft7 => Some(keywords::const_::compile),
|
||||
#[cfg(feature = "draft201909")]
|
||||
Draft::Draft201909 => Some(keywords::const_::compile),
|
||||
#[cfg(feature = "draft202012")]
|
||||
Draft::Draft202012 => Some(keywords::const_::compile),
|
||||
},
|
||||
"contains" => match self {
|
||||
Draft::Draft4 => None,
|
||||
Draft::Draft6 | Draft::Draft7 => Some(keywords::contains::compile),
|
||||
#[cfg(feature = "draft201909")]
|
||||
Draft::Draft201909 => Some(keywords::contains::compile),
|
||||
#[cfg(feature = "draft202012")]
|
||||
Draft::Draft202012 => Some(keywords::contains::compile),
|
||||
},
|
||||
"contentMediaType" => match self {
|
||||
Draft::Draft7 | Draft::Draft6 => Some(keywords::content::compile_media_type),
|
||||
|
@ -64,6 +76,8 @@ impl Draft {
|
|||
#[cfg(feature = "draft201909")]
|
||||
// Should be collected as an annotation
|
||||
Draft::Draft201909 => None,
|
||||
#[cfg(feature = "draft202012")]
|
||||
Draft::Draft202012 => None,
|
||||
},
|
||||
"contentEncoding" => match self {
|
||||
Draft::Draft7 | Draft::Draft6 => Some(keywords::content::compile_content_encoding),
|
||||
|
@ -71,11 +85,13 @@ impl Draft {
|
|||
#[cfg(feature = "draft201909")]
|
||||
// Should be collected as an annotation
|
||||
Draft::Draft201909 => None,
|
||||
#[cfg(feature = "draft202012")]
|
||||
Draft::Draft202012 => None,
|
||||
},
|
||||
"dependencies" => Some(keywords::dependencies::compile),
|
||||
#[cfg(feature = "draft201909")]
|
||||
#[cfg(any(feature = "draft201909", feature = "draft202012"))]
|
||||
"dependentRequired" => Some(keywords::dependencies::compile_dependent_required),
|
||||
#[cfg(feature = "draft201909")]
|
||||
#[cfg(any(feature = "draft201909", feature = "draft202012"))]
|
||||
"dependentSchemas" => Some(keywords::dependencies::compile_dependent_schemas),
|
||||
"enum" => Some(keywords::enum_::compile),
|
||||
"exclusiveMaximum" => match self {
|
||||
|
@ -83,12 +99,16 @@ impl Draft {
|
|||
Draft::Draft4 => None,
|
||||
#[cfg(feature = "draft201909")]
|
||||
Draft::Draft201909 => Some(keywords::exclusive_maximum::compile),
|
||||
#[cfg(feature = "draft202012")]
|
||||
Draft::Draft202012 => Some(keywords::exclusive_maximum::compile),
|
||||
},
|
||||
"exclusiveMinimum" => match self {
|
||||
Draft::Draft7 | Draft::Draft6 => Some(keywords::exclusive_minimum::compile),
|
||||
Draft::Draft4 => None,
|
||||
#[cfg(feature = "draft201909")]
|
||||
Draft::Draft201909 => Some(keywords::exclusive_minimum::compile),
|
||||
#[cfg(feature = "draft202012")]
|
||||
Draft::Draft202012 => Some(keywords::exclusive_minimum::compile),
|
||||
},
|
||||
"format" => Some(keywords::format::compile),
|
||||
"if" => match self {
|
||||
|
@ -96,6 +116,8 @@ impl Draft {
|
|||
Draft::Draft6 | Draft::Draft4 => None,
|
||||
#[cfg(feature = "draft201909")]
|
||||
Draft::Draft201909 => Some(keywords::if_::compile),
|
||||
#[cfg(feature = "draft202012")]
|
||||
Draft::Draft202012 => Some(keywords::if_::compile),
|
||||
},
|
||||
"items" => Some(keywords::items::compile),
|
||||
"maximum" => match self {
|
||||
|
@ -103,6 +125,8 @@ impl Draft {
|
|||
Draft::Draft6 | Draft::Draft7 => Some(keywords::maximum::compile),
|
||||
#[cfg(feature = "draft201909")]
|
||||
Draft::Draft201909 => Some(keywords::maximum::compile),
|
||||
#[cfg(feature = "draft202012")]
|
||||
Draft::Draft202012 => Some(keywords::maximum::compile),
|
||||
},
|
||||
"maxItems" => Some(keywords::max_items::compile),
|
||||
"maxLength" => Some(keywords::max_length::compile),
|
||||
|
@ -112,6 +136,8 @@ impl Draft {
|
|||
Draft::Draft6 | Draft::Draft7 => Some(keywords::minimum::compile),
|
||||
#[cfg(feature = "draft201909")]
|
||||
Draft::Draft201909 => Some(keywords::minimum::compile),
|
||||
#[cfg(feature = "draft202012")]
|
||||
Draft::Draft202012 => Some(keywords::minimum::compile),
|
||||
},
|
||||
"minItems" => Some(keywords::min_items::compile),
|
||||
"minLength" => Some(keywords::min_length::compile),
|
||||
|
@ -121,12 +147,16 @@ impl Draft {
|
|||
"oneOf" => Some(keywords::one_of::compile),
|
||||
"pattern" => Some(keywords::pattern::compile),
|
||||
"patternProperties" => Some(keywords::pattern_properties::compile),
|
||||
#[cfg(feature = "draft202012")]
|
||||
"prefixItems" => Some(keywords::prefix_items::compile),
|
||||
"properties" => Some(keywords::properties::compile),
|
||||
"propertyNames" => match self {
|
||||
Draft::Draft4 => None,
|
||||
Draft::Draft6 | Draft::Draft7 => Some(keywords::property_names::compile),
|
||||
#[cfg(feature = "draft201909")]
|
||||
Draft::Draft201909 => Some(keywords::property_names::compile),
|
||||
#[cfg(feature = "draft202012")]
|
||||
Draft::Draft202012 => Some(keywords::property_names::compile),
|
||||
},
|
||||
"required" => Some(keywords::required::compile),
|
||||
"type" => match self {
|
||||
|
@ -134,6 +164,8 @@ impl Draft {
|
|||
Draft::Draft6 | Draft::Draft7 => Some(keywords::type_::compile),
|
||||
#[cfg(feature = "draft201909")]
|
||||
Draft::Draft201909 => Some(keywords::type_::compile),
|
||||
#[cfg(feature = "draft202012")]
|
||||
Draft::Draft202012 => Some(keywords::type_::compile),
|
||||
},
|
||||
"uniqueItems" => Some(keywords::unique_items::compile),
|
||||
_ => None,
|
||||
|
@ -145,6 +177,8 @@ impl Draft {
|
|||
#[inline]
|
||||
pub(crate) fn draft_from_url(url: &str) -> Option<Draft> {
|
||||
match url {
|
||||
#[cfg(feature = "draft202012")]
|
||||
"https://json-schema.org/draft/2020-12/schema#" => Some(Draft::Draft202012),
|
||||
#[cfg(feature = "draft201909")]
|
||||
"https://json-schema.org/draft/2019-09/schema#" => Some(Draft::Draft201909),
|
||||
"http://json-schema.org/draft-07/schema#" => Some(Draft::Draft7),
|
||||
|
@ -184,6 +218,7 @@ mod tests {
|
|||
use test_case::test_case;
|
||||
|
||||
#[cfg_attr(feature = "draft201909", test_case(&json!({"$schema": "https://json-schema.org/draft/2019-09/schema#"}), Some(Draft::Draft201909)))]
|
||||
#[cfg_attr(feature = "draft202012", test_case(&json!({"$schema": "https://json-schema.org/draft/2020-12/schema#"}), Some(Draft::Draft202012)))]
|
||||
#[test_case(&json!({"$schema": "http://json-schema.org/draft-07/schema#"}), Some(Draft::Draft7))]
|
||||
#[test_case(&json!({"$schema": "http://json-schema.org/draft-06/schema#"}), Some(Draft::Draft6))]
|
||||
#[test_case(&json!({"$schema": "http://json-schema.org/draft-04/schema#"}), Some(Draft::Draft4))]
|
||||
|
|
|
@ -37,6 +37,43 @@ use std::fs;
|
|||
r"unevaluatedItems_.+",
|
||||
r"unevaluatedProperties_.+",
|
||||
}))]
|
||||
#[cfg_attr(feature = "draft202012", json_schema_test_suite("tests/suite", "draft2020-12", {
|
||||
r"optional_format_idn_hostname_0_\d+", // https://github.com/Stranger6667/jsonschema-rs/issues/101
|
||||
r"format_\d+_6", // https://github.com/Stranger6667/jsonschema-rs/issues/261
|
||||
// These depend on the new `$defs` keyword (which is renamed from `definitions`)
|
||||
r"id_0_[0-6]",
|
||||
// Various types of new behavior used in the `$ref` context
|
||||
"ref_5_1",
|
||||
"ref_13_0",
|
||||
"refRemote_4_0",
|
||||
"refRemote_4_1",
|
||||
"recursiveRef_0_3",
|
||||
"recursiveRef_1_2",
|
||||
"recursiveRef_1_4",
|
||||
"recursiveRef_3_2",
|
||||
"recursiveRef_3_4",
|
||||
"recursiveRef_4_2",
|
||||
"recursiveRef_4_4",
|
||||
"recursiveRef_5_2",
|
||||
"recursiveRef_6_2",
|
||||
"recursiveRef_7_0",
|
||||
"recursiveRef_7_1",
|
||||
// New keywords & formats.
|
||||
// https://github.com/Stranger6667/jsonschema-rs/issues/100
|
||||
r"anchor_.+",
|
||||
r"defs_.+",
|
||||
r"dynamicRef_.+",
|
||||
r"uniqueItems_.+",
|
||||
r"optional_format_duration_.+", // https://github.com/Stranger6667/jsonschema-rs/issues/265
|
||||
r"optional_format_uuid_.+", // https://github.com/Stranger6667/jsonschema-rs/issues/266
|
||||
r"optional_format_iri_.+",
|
||||
r"optional_format_json_pointer_.+",
|
||||
r"optional_format_relative_json_pointer_.+",
|
||||
r"optional_format_uri_reference_.+",
|
||||
r"optional_format_uri_template_.+",
|
||||
r"unevaluatedItems_.+",
|
||||
r"unevaluatedProperties_.+",
|
||||
}))]
|
||||
fn test_draft(_server_address: &str, test_case: TestCase) {
|
||||
let draft_version = match test_case.draft_version.as_ref() {
|
||||
"draft4" => Draft::Draft4,
|
||||
|
@ -44,6 +81,8 @@ fn test_draft(_server_address: &str, test_case: TestCase) {
|
|||
"draft7" => Draft::Draft7,
|
||||
#[cfg(feature = "draft201909")]
|
||||
"draft2019-09" => Draft::Draft201909,
|
||||
#[cfg(feature = "draft202012")]
|
||||
"draft2020-12" => Draft::Draft202012,
|
||||
_ => panic!("Unsupported draft"),
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue