feat: Support Draft 2019-09
This commit is contained in:
parent
9ff2dfd727
commit
9410582289
|
@ -59,6 +59,7 @@ pub fn test_draft(input: TokenStream) -> TokenStream {
|
|||
let dir = Path::new(&test_case.dir_name);
|
||||
let draft = test_case
|
||||
.dir_name
|
||||
.replace("-", "_")
|
||||
.trim_end_matches('/')
|
||||
.split('/')
|
||||
.last()
|
||||
|
@ -192,7 +193,7 @@ fn render_test(
|
|||
should_ignore: bool,
|
||||
) -> TokenStream2 {
|
||||
let test_case_name_ident = string_to_ident(test_case_name);
|
||||
let version_ident = string_to_ident(&draft.to_title_case());
|
||||
let version_ident = string_to_ident(&draft.to_title_case().replace(" ", ""));
|
||||
let maybe_ignore_attr: Option<syn::Attribute> = if should_ignore {
|
||||
Some(syn::parse_quote! { #[ignore] })
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
use super::{CompilationResult, Validate};
|
||||
use crate::compilation::{CompilationContext, JSONSchema};
|
||||
use crate::error::{no_error, CompilationError, ErrorIterator, ValidationError};
|
||||
use serde_json::{Map, Value};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
pub struct DependentRequiredValidator {
|
||||
dependent: HashMap<String, Vec<String>>,
|
||||
}
|
||||
|
||||
impl DependentRequiredValidator {
|
||||
pub(crate) fn compile(schema: &Value) -> CompilationResult {
|
||||
match schema {
|
||||
Value::Object(items) => {
|
||||
let mut dependent = HashMap::with_capacity(items.len());
|
||||
for (key, value) in items {
|
||||
match value {
|
||||
Value::Array(required) => {
|
||||
let capacity = required.len();
|
||||
let dependent_required = dependent
|
||||
.entry(key.clone())
|
||||
.or_insert_with(|| Vec::with_capacity(capacity));
|
||||
let mut seen = HashSet::with_capacity(capacity);
|
||||
for item in required {
|
||||
match item {
|
||||
Value::String(string) => {
|
||||
if seen.insert(string) {
|
||||
dependent_required.push(string.clone())
|
||||
} else {
|
||||
return Err(CompilationError::SchemaError);
|
||||
}
|
||||
}
|
||||
_ => return Err(CompilationError::SchemaError),
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => return Err(CompilationError::SchemaError),
|
||||
}
|
||||
}
|
||||
Ok(Box::new(DependentRequiredValidator { dependent }))
|
||||
}
|
||||
_ => Err(CompilationError::SchemaError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Validate for DependentRequiredValidator {
|
||||
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
|
||||
if let Value::Object(item) = instance {
|
||||
for (property_name, dependent) in self.dependent.iter() {
|
||||
if item.contains_key(property_name) {
|
||||
for required in dependent.iter() {
|
||||
if !item.contains_key(required) {
|
||||
// Might be more verbose and specify "why" it is required
|
||||
return ValidationError::required(required.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return no_error();
|
||||
}
|
||||
no_error()
|
||||
}
|
||||
|
||||
fn is_valid(&self, _: &JSONSchema, instance: &Value) -> bool {
|
||||
if let Value::Object(item) = instance {
|
||||
return self.dependent.iter().all(|(property_name, dependent)| {
|
||||
// Seems like it could be done with `filter`
|
||||
if item.contains_key(property_name) {
|
||||
dependent.iter().all(|required| item.contains_key(required))
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
format!("<required: {:?}>", self.dependent)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn compile(
|
||||
_: &Map<String, Value>,
|
||||
schema: &Value,
|
||||
_: &CompilationContext,
|
||||
) -> Option<CompilationResult> {
|
||||
Some(DependentRequiredValidator::compile(schema))
|
||||
}
|
|
@ -7,6 +7,7 @@ pub(crate) mod const_;
|
|||
pub(crate) mod contains;
|
||||
pub(crate) mod content;
|
||||
pub(crate) mod dependencies;
|
||||
pub(crate) mod dependent_required;
|
||||
pub(crate) mod enum_;
|
||||
pub(crate) mod exclusive_maximum;
|
||||
pub(crate) mod exclusive_minimum;
|
||||
|
|
|
@ -8,6 +8,7 @@ pub enum Draft {
|
|||
Draft4,
|
||||
Draft6,
|
||||
Draft7,
|
||||
Draft201909,
|
||||
}
|
||||
|
||||
type CompileFunc =
|
||||
|
@ -16,6 +17,43 @@ type CompileFunc =
|
|||
impl Draft {
|
||||
pub(crate) fn get_validator(self, keyword: &str) -> Option<CompileFunc> {
|
||||
match self {
|
||||
Draft::Draft201909 => match keyword {
|
||||
"additionalItems" => Some(keywords::additional_items::compile),
|
||||
"additionalProperties" => Some(keywords::additional_properties::compile),
|
||||
"allOf" => Some(keywords::all_of::compile),
|
||||
"anyOf" => Some(keywords::any_of::compile),
|
||||
"const" => Some(keywords::const_::compile),
|
||||
"contains" => Some(keywords::contains::compile),
|
||||
"contentMediaType" => Some(keywords::content::compile_media_type),
|
||||
"contentEncoding" => Some(keywords::content::compile_content_encoding),
|
||||
"dependencies" => Some(keywords::dependencies::compile),
|
||||
"dependentRequired" => Some(keywords::dependent_required::compile),
|
||||
"enum" => Some(keywords::enum_::compile),
|
||||
"exclusiveMaximum" => Some(keywords::exclusive_maximum::compile),
|
||||
"exclusiveMinimum" => Some(keywords::exclusive_minimum::compile),
|
||||
"format" => Some(keywords::format::compile),
|
||||
"if" => Some(keywords::if_::compile),
|
||||
"items" => Some(keywords::items::compile),
|
||||
"maximum" => Some(keywords::maximum::compile),
|
||||
"maxItems" => Some(keywords::max_items::compile),
|
||||
"maxLength" => Some(keywords::max_length::compile),
|
||||
"maxProperties" => Some(keywords::max_properties::compile),
|
||||
"minimum" => Some(keywords::minimum::compile),
|
||||
"minItems" => Some(keywords::min_items::compile),
|
||||
"minLength" => Some(keywords::min_length::compile),
|
||||
"minProperties" => Some(keywords::min_properties::compile),
|
||||
"multipleOf" => Some(keywords::multiple_of::compile),
|
||||
"not" => Some(keywords::not::compile),
|
||||
"oneOf" => Some(keywords::one_of::compile),
|
||||
"pattern" => Some(keywords::pattern::compile),
|
||||
"patternProperties" => Some(keywords::pattern_properties::compile),
|
||||
"properties" => Some(keywords::properties::compile),
|
||||
"propertyNames" => Some(keywords::property_names::compile),
|
||||
"required" => Some(keywords::required::compile),
|
||||
"type" => Some(keywords::type_::compile),
|
||||
"uniqueItems" => Some(keywords::unique_items::compile),
|
||||
_ => None,
|
||||
},
|
||||
Draft::Draft7 => match keyword {
|
||||
"additionalItems" => Some(keywords::additional_items::compile),
|
||||
"additionalProperties" => Some(keywords::additional_properties::compile),
|
||||
|
|
|
@ -3,3 +3,4 @@ use draft::test_draft;
|
|||
test_draft!("tests/suite/tests/draft4/", {"optional_bignum_0_0", "optional_bignum_2_0"});
|
||||
test_draft!("tests/suite/tests/draft6/");
|
||||
test_draft!("tests/suite/tests/draft7/");
|
||||
test_draft!("tests/suite/tests/draft2019-09/");
|
||||
|
|
Loading…
Reference in New Issue