Merge pull request #25 from Stranger6667/try

feat: Error iterator
This commit is contained in:
Dmitry Dygalo 2020-03-29 10:47:43 +02:00 committed by GitHub
commit fbcfc5072d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 1015 additions and 988 deletions

View File

@ -2,6 +2,8 @@
Yet another JSON Schema validator implementation. It compiles schema into a validation tree to have validation as fast as possible.
To validate documents against some schema and get validation errors (if any):
```rust
use jsonschema::{JSONSchema, Draft};
use serde_json::json;
@ -11,7 +13,25 @@ fn main() {
let instance = json!("foo");
let compiled = JSONSchema::compile(&schema, Some(Draft::Draft7));
let result = compiled.validate(&instance);
if let Err(errors) = result {
for error in errors {
println!("Validation error: {}", error)
}
}
}
```
If you only need to know whether document is valid or not:
```rust
use jsonschema::is_valid;
use serde_json::json;
fn main() {
let schema = json!({"maxLength": 5});
let instance = json!("foo");
assert!(is_valid(&schema, &instance));
}
```
**NOTE**. This library is in early development.

View File

@ -85,20 +85,6 @@ fn fastjsonschema_invalid_benchmark(c: &mut Criterion) {
});
}
fn format_time_benchmark(c: &mut Criterion) {
let schema = black_box(json!({"type": "string", "format": "time"}));
let validator = JSONSchema::compile(&schema, None).unwrap();
let data = black_box(json!("10:00:00Z"));
c.bench_function("format time", |b| b.iter(|| validator.validate(&data)));
}
fn max_length_benchmark(c: &mut Criterion) {
let schema = json!({"maxLength": 5});
let validator = JSONSchema::compile(&schema, None).unwrap();
let data = black_box(json!("abc"));
c.bench_function("max length", |b| b.iter(|| validator.validate(&data)));
}
bench_validate!(
additional_items_valid,
"additional items valid",
@ -287,12 +273,12 @@ bench_compile!(c_aproperties6, "compile additional properties 6", {"properties":
criterion_group!(
benches,
// canada_benchmark,
canada_benchmark,
// canada_benchmark_alternative,
// canada_compile_benchmark,
// fastjsonschema_compile,
// fastjsonschema_valid_benchmark,
// fastjsonschema_invalid_benchmark,
canada_compile_benchmark,
fastjsonschema_compile,
fastjsonschema_valid_benchmark,
fastjsonschema_invalid_benchmark,
// type_string_valid,
// type_string_invalid,
// false_schema,
@ -349,10 +335,10 @@ criterion_group!(
// additional_properties_invalid6,
// type_integer_valid1,
// type_integer_invalid1,
type_integer_valid2,
type_integer_invalid2,
type_multiple_valid3,
type_multiple_invalid3,
// type_integer_valid2,
// type_integer_invalid2,
// type_multiple_valid3,
// type_multiple_invalid3,
// unique_items_valid,
// unique_items_invalid,
// multiple_of_integer_valid,
@ -370,9 +356,9 @@ criterion_group!(
// format_time_benchmark,
// max_length_benchmark,
// ref_valid,
// c_required,
// properties_valid,
// properties_invalid,
// c_required,
// c_properties,
// c_dependencies,
// c_enum,

View File

@ -89,36 +89,20 @@ fn make_fn_body(schema: &Value, data: &Value, description: &str, valid: bool) ->
if valid {
output.push_str(
r#"
match result.err() {
Some(err) => {
let message = format!(
"Schema: {}\nInstance: {}\nError: {}",
schema, data, err
);
assert!(false, message)
}
None => {}
let err = result.err();
let errors = err.iter().collect::<Vec<_>>();
if !errors.is_empty() {
let message = format!(
"Schema: {}\nInstance: {}\nError: {:?}",
schema, data, 1
);
assert!(false, message)
}
"#,
)
} else {
output.push_str(
r#"assert!(result.is_err(), "It should be INVALID!");
"#,
)
output.push_str(r#"assert!(result.is_err(), "It should be INVALID!");"#)
}
output.push_str("}");
output
}
// id: "json-schema://86de4a79-34d7-45d4-8821-a9a99dc2d7a6",
// children: [
// <additional properties: {
// id: "json-schema://355c236a-4057-47e8-8325-4d1c6050b861",
// children: [<false>],
// }
// <properties: {
// "foo": {
// id: "json-schema://0dff1dfb-a85e-4b26-9abe-a8e1adbf7c8e",
// children: [<ref: #>]
// }
// ]

View File

@ -1,5 +1,4 @@
use crate::error::ValidationError;
use crate::keywords::ValidationResult;
use crate::error::{error, no_error, ErrorIterator, ValidationError};
use chrono::{DateTime, NaiveDate};
use regex::Regex;
use std::net::IpAddr;
@ -24,28 +23,28 @@ lazy_static! {
.unwrap();
}
pub(crate) fn date(instance: &str) -> ValidationResult {
pub(crate) fn date(instance: &str) -> ErrorIterator {
if NaiveDate::parse_from_str(instance, "%Y-%m-%d").is_err() {
return Err(ValidationError::format(instance.to_owned(), "date"));
return error(ValidationError::format(instance.to_owned(), "date"));
}
Ok(())
no_error()
}
pub(crate) fn datetime(instance: &str) -> ValidationResult {
pub(crate) fn datetime(instance: &str) -> ErrorIterator {
if DateTime::parse_from_rfc3339(instance).is_err() {
return Err(ValidationError::format(instance.to_owned(), "date-time"));
return error(ValidationError::format(instance.to_owned(), "date-time"));
}
Ok(())
no_error()
}
pub(crate) fn email(instance: &str) -> ValidationResult {
pub(crate) fn email(instance: &str) -> ErrorIterator {
if !instance.contains('@') {
return Err(ValidationError::format(instance.to_owned(), "email"));
return error(ValidationError::format(instance.to_owned(), "email"));
}
Ok(())
no_error()
}
pub(crate) fn hostname(instance: &str) -> ValidationResult {
pub(crate) fn hostname(instance: &str) -> ErrorIterator {
if instance.ends_with('-')
|| instance.starts_with('-')
|| instance.is_empty()
@ -55,12 +54,12 @@ pub(crate) fn hostname(instance: &str) -> ValidationResult {
.any(|c| !(c.is_alphanumeric() || c == '-' || c == '.'))
|| instance.split('.').any(|part| part.chars().count() > 63)
{
return Err(ValidationError::format(instance.to_owned(), "hostname"));
return error(ValidationError::format(instance.to_owned(), "hostname"));
}
Ok(())
no_error()
}
pub(crate) fn ipv4(instance: &str) -> ValidationResult {
pub(crate) fn ipv4(instance: &str) -> ErrorIterator {
if !match IpAddr::from_str(instance) {
Ok(i) => match i {
IpAddr::V4(_) => true,
@ -68,12 +67,12 @@ pub(crate) fn ipv4(instance: &str) -> ValidationResult {
},
Err(_) => false,
} {
return Err(ValidationError::format(instance.to_owned(), "ipv4"));
return error(ValidationError::format(instance.to_owned(), "ipv4"));
}
Ok(())
no_error()
}
pub(crate) fn ipv6(instance: &str) -> ValidationResult {
pub(crate) fn ipv6(instance: &str) -> ErrorIterator {
if !match IpAddr::from_str(instance) {
Ok(i) => match i {
IpAddr::V4(_) => false,
@ -81,72 +80,72 @@ pub(crate) fn ipv6(instance: &str) -> ValidationResult {
},
Err(_) => false,
} {
return Err(ValidationError::format(instance.to_owned(), "ipv6"));
return error(ValidationError::format(instance.to_owned(), "ipv6"));
}
Ok(())
no_error()
}
pub(crate) fn iri(instance: &str) -> ValidationResult {
pub(crate) fn iri(instance: &str) -> ErrorIterator {
if Url::from_str(instance).is_err() {
return Err(ValidationError::format(instance.to_owned(), "iri"));
return error(ValidationError::format(instance.to_owned(), "iri"));
}
Ok(())
no_error()
}
pub(crate) fn iri_reference(instance: &str) -> ValidationResult {
pub(crate) fn iri_reference(instance: &str) -> ErrorIterator {
if !IRI_REFERENCE_RE.is_match(instance) {
return Err(ValidationError::format(
return error(ValidationError::format(
instance.to_owned(),
"iri-reference",
));
}
Ok(())
no_error()
}
pub(crate) fn json_pointer(instance: &str) -> ValidationResult {
pub(crate) fn json_pointer(instance: &str) -> ErrorIterator {
if !JSON_POINTER_RE.is_match(instance) {
return Err(ValidationError::format(instance.to_owned(), "json-pointer"));
return error(ValidationError::format(instance.to_owned(), "json-pointer"));
}
Ok(())
no_error()
}
pub(crate) fn regex(instance: &str) -> ValidationResult {
pub(crate) fn regex(instance: &str) -> ErrorIterator {
if Regex::new(instance).is_err() {
return Err(ValidationError::format(instance.to_owned(), "regex"));
return error(ValidationError::format(instance.to_owned(), "regex"));
}
Ok(())
no_error()
}
pub(crate) fn relative_json_pointer(instance: &str) -> ValidationResult {
pub(crate) fn relative_json_pointer(instance: &str) -> ErrorIterator {
if !RELATIVE_JSON_POINTER_RE.is_match(instance) {
return Err(ValidationError::format(
return error(ValidationError::format(
instance.to_owned(),
"relative-json-pointer",
));
}
Ok(())
no_error()
}
pub(crate) fn time(instance: &str) -> ValidationResult {
pub(crate) fn time(instance: &str) -> ErrorIterator {
if !TIME_RE.is_match(instance) {
return Err(ValidationError::format(instance.to_owned(), "time"));
return error(ValidationError::format(instance.to_owned(), "time"));
}
Ok(())
no_error()
}
pub(crate) fn uri_reference(instance: &str) -> ValidationResult {
pub(crate) fn uri_reference(instance: &str) -> ErrorIterator {
if !URI_REFERENCE_RE.is_match(instance) {
return Err(ValidationError::format(
return error(ValidationError::format(
instance.to_owned(),
"uri-reference",
));
}
Ok(())
no_error()
}
pub(crate) fn uri_template(instance: &str) -> ValidationResult {
pub(crate) fn uri_template(instance: &str) -> ErrorIterator {
if !URI_TEMPLATE_RE.is_match(instance) {
return Err(ValidationError::format(instance.to_owned(), "uri-template"));
return error(ValidationError::format(instance.to_owned(), "uri-template"));
}
Ok(())
no_error()
}

View File

@ -1,5 +1,6 @@
use serde_json::Value;
use std::fmt::{Error, Formatter};
use std::iter::{empty, once};
use std::string::FromUtf8Error;
use std::{error, fmt, io};
@ -25,6 +26,17 @@ pub struct ValidationError {
kind: ValidationErrorKind,
}
pub type ErrorIterator<'a> = Box<dyn Iterator<Item = ValidationError> + 'a>;
// Empty iterator means no error happened
pub(crate) fn no_error<'a>() -> ErrorIterator<'a> {
Box::new(empty())
}
// A wrapper for one error
pub(crate) fn error<'a>(instance: ValidationError) -> ErrorIterator<'a> {
Box::new(once(instance))
}
/// Kinds of errors that may happen during validation
#[derive(Debug)]
pub enum ValidationErrorKind {
@ -129,46 +141,46 @@ pub enum TypeKind {
}
/// Shortcuts for creation of specific error kinds.
impl ValidationError {
pub(crate) fn additional_items(items: Vec<Value>, limit: usize) -> ValidationError {
ValidationError {
impl<'a> ValidationError {
pub(crate) fn additional_items(items: Vec<Value>, limit: usize) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::AdditionalItems { items, limit },
}
})
}
pub(crate) fn any_of(instance: Value) -> ValidationError {
ValidationError {
pub(crate) fn any_of(instance: Value) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::AnyOf(instance),
}
})
}
pub(crate) fn constant(message: String) -> ValidationError {
ValidationError {
pub(crate) fn constant(message: String) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::Constant(message),
}
})
}
pub(crate) fn contains(instance: Value) -> ValidationError {
ValidationError {
pub(crate) fn contains(instance: Value) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::Contains(instance),
}
})
}
pub(crate) fn enumeration(instance: Value, options: Value) -> ValidationError {
ValidationError {
pub(crate) fn enumeration(instance: Value, options: Value) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::Enum { instance, options },
}
})
}
pub(crate) fn exclusive_maximum(instance: f64, limit: f64) -> ValidationError {
ValidationError {
pub(crate) fn exclusive_maximum(instance: f64, limit: f64) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::ExclusiveMaximum { instance, limit },
}
})
}
pub(crate) fn exclusive_minimum(instance: f64, limit: f64) -> ValidationError {
ValidationError {
pub(crate) fn exclusive_minimum(instance: f64, limit: f64) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::ExclusiveMinimum { instance, limit },
}
})
}
pub(crate) fn false_schema(instance: Value) -> ValidationError {
ValidationError {
pub(crate) fn false_schema(instance: Value) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::FalseSchema(instance),
}
})
}
pub(crate) fn file_not_found(err: io::Error) -> ValidationError {
ValidationError {
@ -195,107 +207,110 @@ impl ValidationError {
kind: ValidationErrorKind::InvalidReference(reference),
}
}
pub(crate) fn max_items(instance: Value) -> ValidationError {
ValidationError {
pub(crate) fn max_items(instance: Value) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::MaxItems(instance),
}
})
}
pub(crate) fn maximum(instance: f64, limit: f64) -> ValidationError {
ValidationError {
pub(crate) fn maximum(instance: f64, limit: f64) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::Maximum { instance, limit },
}
})
}
pub(crate) fn max_length(instance: String) -> ValidationError {
ValidationError {
pub(crate) fn max_length(instance: String) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::MaxLength(instance),
}
})
}
pub(crate) fn max_properties(instance: Value) -> ValidationError {
ValidationError {
pub(crate) fn max_properties(instance: Value) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::MaxProperties(instance),
}
})
}
pub(crate) fn min_items(instance: Value) -> ValidationError {
ValidationError {
pub(crate) fn min_items(instance: Value) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::MinItems(instance),
}
})
}
pub(crate) fn minimum(instance: f64, limit: f64) -> ValidationError {
ValidationError {
pub(crate) fn minimum(instance: f64, limit: f64) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::Minimum { instance, limit },
}
})
}
pub(crate) fn min_length(instance: String) -> ValidationError {
ValidationError {
pub(crate) fn min_length(instance: String) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::MinLength(instance),
}
})
}
pub(crate) fn min_properties(instance: Value) -> ValidationError {
ValidationError {
pub(crate) fn min_properties(instance: Value) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::MinProperties(instance),
}
})
}
pub(crate) fn multiple_of(instance: f64, multiple_of: f64) -> ValidationError {
ValidationError {
pub(crate) fn multiple_of(instance: f64, multiple_of: f64) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::MultipleOf {
instance,
multiple_of,
},
}
})
}
pub(crate) fn not(instance: Value, schema: Value) -> ValidationError {
ValidationError {
pub(crate) fn not(instance: Value, schema: Value) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::Not { instance, schema },
}
})
}
pub(crate) fn one_of_multiple_valid(instance: Value) -> ValidationError {
ValidationError {
pub(crate) fn one_of_multiple_valid(instance: Value) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::OneOfMultipleValid(instance),
}
})
}
pub(crate) fn one_of_not_valid(instance: Value) -> ValidationError {
ValidationError {
pub(crate) fn one_of_not_valid(instance: Value) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::OneOfNotValid(instance),
}
})
}
pub(crate) fn pattern(instance: String, pattern: String) -> ValidationError {
ValidationError {
pub(crate) fn pattern(instance: String, pattern: String) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::Pattern { instance, pattern },
}
})
}
pub(crate) fn required(property: String) -> ValidationError {
ValidationError {
pub(crate) fn required(property: String) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::Required(property),
}
})
}
pub(crate) fn schema() -> ValidationError {
ValidationError {
kind: ValidationErrorKind::Schema,
}
}
pub(crate) fn single_type_error(instance: Value, type_name: PrimitiveType) -> ValidationError {
ValidationError {
pub(crate) fn single_type_error(
instance: Value,
type_name: PrimitiveType,
) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::Type {
instance,
kind: TypeKind::Single(type_name),
},
}
})
}
pub(crate) fn multiple_type_error(
instance: Value,
types: Vec<PrimitiveType>,
) -> ValidationError {
ValidationError {
) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::Type {
instance,
kind: TypeKind::Multiple(types),
},
}
})
}
pub(crate) fn unique_items(instance: Value) -> ValidationError {
ValidationError {
pub(crate) fn unique_items(instance: Value) -> ErrorIterator<'a> {
error(ValidationError {
kind: ValidationErrorKind::UniqueItems(instance),
}
})
}
pub(crate) fn unknown_reference_scheme(scheme: String) -> ValidationError {
ValidationError {
@ -468,7 +483,9 @@ mod tests {
#[test]
fn type_error() {
let instance = json!(42);
let err = ValidationError::single_type_error(instance, PrimitiveType::String);
let err = ValidationError::single_type_error(instance, PrimitiveType::String)
.next()
.unwrap();
let repr = format!("{}", err);
assert_eq!(repr, "'42' is not of type 'string'")
}

View File

@ -1,26 +1,26 @@
use super::{CompilationResult, ValidationResult};
use super::CompilationResult;
use super::{Validate, Validators};
use crate::context::CompilationContext;
use crate::error::{CompilationError, ValidationError};
use crate::error::{no_error, CompilationError, ErrorIterator, ValidationError};
use crate::keywords::boolean::TrueValidator;
use crate::validator::compile_validators;
use crate::JSONSchema;
use serde_json::{Map, Value};
pub struct AdditionalItemsObjectValidator<'a> {
validators: Validators<'a>,
pub struct AdditionalItemsObjectValidator {
validators: Validators,
items_count: usize,
}
pub struct AdditionalItemsBooleanValidator {
items_count: usize,
}
impl<'a> AdditionalItemsObjectValidator<'a> {
impl AdditionalItemsObjectValidator {
pub(crate) fn compile(
schema: &'a Value,
schema: &Value,
items_count: usize,
context: &CompilationContext,
) -> CompilationResult<'a> {
) -> CompilationResult {
let validators = compile_validators(schema, context)?;
Ok(Box::new(AdditionalItemsObjectValidator {
validators,
@ -30,21 +30,26 @@ impl<'a> AdditionalItemsObjectValidator<'a> {
}
impl<'a> AdditionalItemsBooleanValidator {
pub(crate) fn compile(items_count: usize) -> CompilationResult<'a> {
pub(crate) fn compile(items_count: usize) -> CompilationResult {
Ok(Box::new(AdditionalItemsBooleanValidator { items_count }))
}
}
impl<'a> Validate<'a> for AdditionalItemsObjectValidator<'a> {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for AdditionalItemsObjectValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Array(items) = instance {
for item in items.iter().skip(self.items_count) {
for validator in self.validators.iter() {
validator.validate(schema, item)?
}
}
let errors: Vec<_> = items
.iter()
.skip(self.items_count)
.flat_map(|item| {
self.validators
.iter()
.flat_map(move |validator| validator.validate(schema, item))
})
.collect();
return Box::new(errors.into_iter());
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!(
@ -54,28 +59,25 @@ impl<'a> Validate<'a> for AdditionalItemsObjectValidator<'a> {
}
}
impl<'a> Validate<'a> for AdditionalItemsBooleanValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for AdditionalItemsBooleanValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Array(items) = instance {
if items.len() > self.items_count {
return Err(ValidationError::additional_items(
items.clone(),
self.items_count,
));
return ValidationError::additional_items(items.clone(), self.items_count);
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<additional items: {}>", self.items_count)
}
}
pub(crate) fn compile<'a>(
parent: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
parent: &Map<String, Value>,
schema: &Value,
context: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
if let Some(items) = parent.get("items") {
match items {
Value::Object(_) => Some(TrueValidator::compile()),

View File

@ -1,37 +1,38 @@
use super::{CompilationResult, ValidationResult};
use super::CompilationResult;
use super::{Validate, Validators};
use crate::context::CompilationContext;
use crate::error::CompilationError;
use crate::error::{no_error, CompilationError, ErrorIterator};
use crate::validator::compile_validators;
use crate::{JSONSchema, ValidationError};
use regex::Regex;
use serde_json::{Map, Value};
pub struct AdditionalPropertiesValidator<'a> {
validators: Validators<'a>,
pub struct AdditionalPropertiesValidator {
validators: Validators,
}
impl<'a> AdditionalPropertiesValidator<'a> {
pub(crate) fn compile(
schema: &'a Value,
context: &CompilationContext,
) -> CompilationResult<'a> {
impl AdditionalPropertiesValidator {
pub(crate) fn compile(schema: &Value, context: &CompilationContext) -> CompilationResult {
Ok(Box::new(AdditionalPropertiesValidator {
validators: compile_validators(schema, context)?,
}))
}
}
impl<'a> Validate<'a> for AdditionalPropertiesValidator<'a> {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for AdditionalPropertiesValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Object(item) = instance {
for value in item.values() {
for validator in self.validators.iter() {
validator.validate(schema, value)?
}
}
let errors: Vec<_> = self
.validators
.iter()
.flat_map(move |validator| {
item.values()
.flat_map(move |value| validator.validate(schema, value))
})
.collect();
return Box::new(errors.into_iter());
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<additional properties: {:?}>", self.validators)
@ -40,73 +41,71 @@ impl<'a> Validate<'a> for AdditionalPropertiesValidator<'a> {
pub struct AdditionalPropertiesFalseValidator {}
impl<'a> AdditionalPropertiesFalseValidator {
pub(crate) fn compile() -> CompilationResult<'a> {
pub(crate) fn compile() -> CompilationResult {
Ok(Box::new(AdditionalPropertiesFalseValidator {}))
}
}
impl<'a> Validate<'a> for AdditionalPropertiesFalseValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for AdditionalPropertiesFalseValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Object(item) = instance {
if let Some((_, value)) = item.iter().next() {
return Err(ValidationError::false_schema(value.clone()));
return ValidationError::false_schema(value.clone());
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
"<additional properties: false>".to_string()
}
}
pub struct AdditionalPropertiesNotEmptyFalseValidator<'a> {
properties: &'a Map<String, Value>,
pub struct AdditionalPropertiesNotEmptyFalseValidator {
properties: Map<String, Value>,
}
impl<'a> AdditionalPropertiesNotEmptyFalseValidator<'a> {
pub(crate) fn compile(properties: &'a Value) -> CompilationResult<'a> {
impl AdditionalPropertiesNotEmptyFalseValidator {
pub(crate) fn compile(properties: &Value) -> CompilationResult {
if let Value::Object(properties) = properties {
return Ok(Box::new(AdditionalPropertiesNotEmptyFalseValidator {
properties,
properties: properties.clone(),
}));
}
Err(CompilationError::SchemaError)
}
}
impl<'a> Validate<'a> for AdditionalPropertiesNotEmptyFalseValidator<'a> {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for AdditionalPropertiesNotEmptyFalseValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Object(item) = instance {
for property in item.keys() {
if !self.properties.contains_key(property) {
// No extra properties are allowed
return Err(ValidationError::false_schema(Value::String(
property.to_string(),
)));
return ValidationError::false_schema(Value::String(property.to_string()));
}
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
"<additional properties: false>".to_string()
}
}
pub struct AdditionalPropertiesNotEmptyValidator<'a> {
validators: Validators<'a>,
properties: &'a Map<String, Value>,
pub struct AdditionalPropertiesNotEmptyValidator {
validators: Validators,
properties: Map<String, Value>,
}
impl<'a> AdditionalPropertiesNotEmptyValidator<'a> {
impl AdditionalPropertiesNotEmptyValidator {
pub(crate) fn compile(
schema: &'a Value,
properties: &'a Value,
schema: &Value,
properties: &Value,
context: &CompilationContext,
) -> CompilationResult<'a> {
) -> CompilationResult {
if let Value::Object(properties) = properties {
return Ok(Box::new(AdditionalPropertiesNotEmptyValidator {
properties,
properties: properties.clone(),
validators: compile_validators(schema, context)?,
}));
}
@ -114,35 +113,38 @@ impl<'a> AdditionalPropertiesNotEmptyValidator<'a> {
}
}
impl<'a> Validate<'a> for AdditionalPropertiesNotEmptyValidator<'a> {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
if let Value::Object(item) = instance {
for (property, value) in item {
if !self.properties.contains_key(property) {
for validator in self.validators.iter() {
validator.validate(schema, value)?
}
}
}
impl Validate for AdditionalPropertiesNotEmptyValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Object(ref item) = instance {
let errors: Vec<_> = self
.validators
.iter()
.flat_map(move |validator| {
item.iter()
.filter(move |(property, _)| !self.properties.contains_key(*property))
.flat_map(move |(_, value)| validator.validate(schema, value))
})
.collect();
return Box::new(errors.into_iter());
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<additional properties: {:?}>", self.validators)
}
}
pub struct AdditionalPropertiesWithPatternsValidator<'a> {
validators: Validators<'a>,
pub struct AdditionalPropertiesWithPatternsValidator {
validators: Validators,
pattern: Regex,
}
impl<'a> AdditionalPropertiesWithPatternsValidator<'a> {
impl AdditionalPropertiesWithPatternsValidator {
pub(crate) fn compile(
schema: &'a Value,
schema: &Value,
pattern: Regex,
context: &CompilationContext,
) -> CompilationResult<'a> {
) -> CompilationResult {
Ok(Box::new(AdditionalPropertiesWithPatternsValidator {
validators: compile_validators(schema, context)?,
pattern,
@ -150,18 +152,21 @@ impl<'a> AdditionalPropertiesWithPatternsValidator<'a> {
}
}
impl<'a> Validate<'a> for AdditionalPropertiesWithPatternsValidator<'a> {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for AdditionalPropertiesWithPatternsValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Object(item) = instance {
for (property, value) in item {
if !self.pattern.is_match(property) {
for validator in self.validators.iter() {
validator.validate(schema, value)?
}
}
}
let errors: Vec<_> = self
.validators
.iter()
.flat_map(move |validator| {
item.iter()
.filter(move |(property, _)| !self.pattern.is_match(property))
.flat_map(move |(_, value)| validator.validate(schema, value))
})
.collect();
return Box::new(errors.into_iter());
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<additional properties: {:?}>", self.validators)
@ -173,49 +178,47 @@ pub struct AdditionalPropertiesWithPatternsFalseValidator {
}
impl<'a> AdditionalPropertiesWithPatternsFalseValidator {
pub(crate) fn compile(pattern: Regex) -> CompilationResult<'a> {
pub(crate) fn compile(pattern: Regex) -> CompilationResult {
Ok(Box::new(AdditionalPropertiesWithPatternsFalseValidator {
pattern,
}))
}
}
impl<'a> Validate<'a> for AdditionalPropertiesWithPatternsFalseValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for AdditionalPropertiesWithPatternsFalseValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Object(item) = instance {
for (property, _) in item {
if !self.pattern.is_match(property) {
return Err(ValidationError::false_schema(Value::String(
property.to_string(),
)));
return ValidationError::false_schema(Value::String(property.to_string()));
}
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
"<additional properties: false>".to_string()
}
}
pub struct AdditionalPropertiesWithPatternsNotEmptyValidator<'a> {
validators: Validators<'a>,
properties: &'a Map<String, Value>,
pub struct AdditionalPropertiesWithPatternsNotEmptyValidator {
validators: Validators,
properties: Map<String, Value>,
pattern: Regex,
}
impl<'a> AdditionalPropertiesWithPatternsNotEmptyValidator<'a> {
impl AdditionalPropertiesWithPatternsNotEmptyValidator {
pub(crate) fn compile(
schema: &'a Value,
properties: &'a Value,
schema: &Value,
properties: &Value,
pattern: Regex,
context: &CompilationContext,
) -> CompilationResult<'a> {
) -> CompilationResult {
if let Value::Object(properties) = properties {
return Ok(Box::new(
AdditionalPropertiesWithPatternsNotEmptyValidator {
validators: compile_validators(schema, context)?,
properties,
properties: properties.clone(),
pattern,
},
));
@ -224,35 +227,41 @@ impl<'a> AdditionalPropertiesWithPatternsNotEmptyValidator<'a> {
}
}
impl<'a> Validate<'a> for AdditionalPropertiesWithPatternsNotEmptyValidator<'a> {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for AdditionalPropertiesWithPatternsNotEmptyValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Object(item) = instance {
for (property, value) in item {
if !self.properties.contains_key(property) && !self.pattern.is_match(property) {
for validator in self.validators.iter() {
validator.validate(schema, value)?
}
}
}
let errors: Vec<_> = self
.validators
.iter()
.flat_map(move |validator| {
item.iter()
.filter(move |(property, _)| {
!self.properties.contains_key(*property)
&& !self.pattern.is_match(property)
})
.flat_map(move |(_, value)| validator.validate(schema, value))
})
.collect();
return Box::new(errors.into_iter());
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<additional properties: {:?}>", self.validators)
}
}
pub struct AdditionalPropertiesWithPatternsNotEmptyFalseValidator<'a> {
properties: &'a Map<String, Value>,
pub struct AdditionalPropertiesWithPatternsNotEmptyFalseValidator {
properties: Map<String, Value>,
pattern: Regex,
}
impl<'a> AdditionalPropertiesWithPatternsNotEmptyFalseValidator<'a> {
pub(crate) fn compile(properties: &'a Value, pattern: Regex) -> CompilationResult<'a> {
impl AdditionalPropertiesWithPatternsNotEmptyFalseValidator {
pub(crate) fn compile(properties: &Value, pattern: Regex) -> CompilationResult {
if let Value::Object(properties) = properties {
return Ok(Box::new(
AdditionalPropertiesWithPatternsNotEmptyFalseValidator {
properties,
properties: properties.clone(),
pattern,
},
));
@ -261,29 +270,27 @@ impl<'a> AdditionalPropertiesWithPatternsNotEmptyFalseValidator<'a> {
}
}
impl<'a> Validate<'a> for AdditionalPropertiesWithPatternsNotEmptyFalseValidator<'a> {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for AdditionalPropertiesWithPatternsNotEmptyFalseValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Object(item) = instance {
for property in item.keys() {
if !self.properties.contains_key(property) && !self.pattern.is_match(property) {
return Err(ValidationError::false_schema(Value::String(
property.to_string(),
)));
return ValidationError::false_schema(Value::String(property.to_string()));
}
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
"<additional properties: false>".to_string()
}
}
pub(crate) fn compile<'a>(
parent: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
parent: &Map<String, Value>,
schema: &Value,
context: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
let properties = parent.get("properties");
if let Some(patterns) = parent.get("patternProperties") {
if let Value::Object(obj) = patterns {

View File

@ -1,20 +1,17 @@
use super::{CompilationResult, ValidationResult};
use super::CompilationResult;
use super::{Validate, Validators};
use crate::context::CompilationContext;
use crate::error::CompilationError;
use crate::error::{CompilationError, ErrorIterator};
use crate::validator::compile_validators;
use crate::JSONSchema;
use serde_json::{Map, Value};
pub struct AllOfValidator<'a> {
schemas: Vec<Validators<'a>>,
pub struct AllOfValidator {
schemas: Vec<Validators>,
}
impl<'a> AllOfValidator<'a> {
pub(crate) fn compile(
schema: &'a Value,
context: &CompilationContext,
) -> CompilationResult<'a> {
impl AllOfValidator {
pub(crate) fn compile(schema: &Value, context: &CompilationContext) -> CompilationResult {
match schema.as_array() {
Some(items) => {
let mut schemas = Vec::with_capacity(items.len());
@ -29,23 +26,27 @@ impl<'a> AllOfValidator<'a> {
}
}
impl<'a> Validate<'a> for AllOfValidator<'a> {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
for validators in self.schemas.iter() {
for validator in validators {
validator.validate(schema, instance)?
}
}
Ok(())
impl Validate for AllOfValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
let errors: Vec<_> = self
.schemas
.iter()
.flat_map(move |validators| {
validators
.iter()
.flat_map(move |validator| validator.validate(schema, instance))
})
.collect();
Box::new(errors.into_iter())
}
fn name(&self) -> String {
format!("<all of: {:?}>", self.schemas)
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
context: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(AllOfValidator::compile(schema, context))
}

View File

@ -1,20 +1,17 @@
use super::{CompilationResult, ValidationResult};
use super::CompilationResult;
use super::{Validate, Validators};
use crate::context::CompilationContext;
use crate::error::{CompilationError, ValidationError};
use crate::error::{no_error, CompilationError, ErrorIterator, ValidationError};
use crate::validator::compile_validators;
use crate::JSONSchema;
use serde_json::{Map, Value};
pub struct AnyOfValidator<'a> {
schemas: Vec<Validators<'a>>,
pub struct AnyOfValidator {
schemas: Vec<Validators>,
}
impl<'a> AnyOfValidator<'a> {
pub(crate) fn compile(
schema: &'a Value,
context: &CompilationContext,
) -> CompilationResult<'a> {
impl AnyOfValidator {
pub(crate) fn compile(schema: &Value, context: &CompilationContext) -> CompilationResult {
match schema.as_array() {
Some(items) => {
let mut schemas = Vec::with_capacity(items.len());
@ -29,27 +26,27 @@ impl<'a> AnyOfValidator<'a> {
}
}
impl<'a> Validate<'a> for AnyOfValidator<'a> {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for AnyOfValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
for validators in self.schemas.iter() {
if validators
.iter()
.all(|validator| validator.is_valid(schema, instance))
{
return Ok(());
return no_error();
}
}
Err(ValidationError::any_of(instance.clone()))
ValidationError::any_of(instance.clone())
}
fn name(&self) -> String {
format!("<any of: {:?}>", self.schemas)
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
context: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(AnyOfValidator::compile(schema, context))
}

View File

@ -1,20 +1,20 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::error::ValidationError;
use crate::error::{no_error, ErrorIterator, ValidationError};
use crate::JSONSchema;
use serde_json::Value;
pub struct TrueValidator {}
impl TrueValidator {
pub(crate) fn compile<'a>() -> CompilationResult<'a> {
pub(crate) fn compile() -> CompilationResult {
Ok(Box::new(TrueValidator {}))
}
}
impl<'a> Validate<'a> for TrueValidator {
fn validate(&self, _: &JSONSchema, _: &Value) -> ValidationResult {
Ok(())
impl Validate for TrueValidator {
fn validate<'a>(&self, _: &'a JSONSchema, _: &'a Value) -> ErrorIterator<'a> {
no_error()
}
fn name(&self) -> String {
"<true>".to_string()
@ -24,21 +24,21 @@ impl<'a> Validate<'a> for TrueValidator {
pub struct FalseValidator {}
impl FalseValidator {
pub(crate) fn compile<'a>() -> CompilationResult<'a> {
pub(crate) fn compile() -> CompilationResult {
Ok(Box::new(FalseValidator {}))
}
}
impl<'a> Validate<'a> for FalseValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
Err(ValidationError::false_schema(instance.clone()))
impl Validate for FalseValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
ValidationError::false_schema(instance.clone())
}
fn name(&self) -> String {
"<false>".to_string()
}
}
pub(crate) fn compile<'a>(value: bool) -> Option<CompilationResult<'a>> {
pub(crate) fn compile(value: bool) -> Option<CompilationResult> {
if value {
Some(TrueValidator::compile())
} else {

View File

@ -1,39 +1,39 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::context::CompilationContext;
use crate::error::ValidationError;
use crate::error::{no_error, ErrorIterator, ValidationError};
use crate::{helpers, JSONSchema};
use serde_json::{Map, Value};
pub struct ConstValidator<'a> {
pub struct ConstValidator {
error_message: String,
value: &'a Value,
value: Value,
}
impl<'a> ConstValidator<'a> {
pub(crate) fn compile(value: &'a Value) -> CompilationResult<'a> {
impl ConstValidator {
pub(crate) fn compile(value: &Value) -> CompilationResult {
Ok(Box::new(ConstValidator {
error_message: format!("'{}' was expected", value),
value,
value: value.clone(),
}))
}
}
impl<'a> Validate<'a> for ConstValidator<'a> {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for ConstValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if !helpers::equal(instance, &self.value) {
return Err(ValidationError::constant(self.error_message.clone()));
return ValidationError::constant(self.error_message.clone());
};
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<const: {}>", self.value)
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(ConstValidator::compile(schema))
}

View File

@ -1,51 +1,48 @@
use super::{CompilationResult, ValidationResult};
use super::CompilationResult;
use super::{Validate, Validators};
use crate::context::CompilationContext;
use crate::error::ValidationError;
use crate::error::{no_error, ErrorIterator, ValidationError};
use crate::validator::compile_validators;
use crate::JSONSchema;
use serde_json::{Map, Value};
pub struct ContainsValidator<'a> {
validators: Validators<'a>,
pub struct ContainsValidator {
validators: Validators,
}
impl<'a> ContainsValidator<'a> {
pub(crate) fn compile(
schema: &'a Value,
context: &CompilationContext,
) -> CompilationResult<'a> {
impl ContainsValidator {
pub(crate) fn compile(schema: &Value, context: &CompilationContext) -> CompilationResult {
Ok(Box::new(ContainsValidator {
validators: compile_validators(schema, context)?,
}))
}
}
impl<'a> Validate<'a> for ContainsValidator<'a> {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for ContainsValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Array(items) = instance {
for item in items {
if self
.validators
.iter()
.all(|validator| validator.validate(schema, item).is_ok())
.all(|validator| validator.is_valid(schema, item))
{
return Ok(());
return no_error();
}
}
return Err(ValidationError::contains(instance.clone()));
return ValidationError::contains(instance.clone());
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<contains: {:?}>", self.validators)
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
context: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(ContainsValidator::compile(schema, context))
}

View File

@ -1,26 +1,26 @@
use super::Validate;
use super::{CompilationResult, ValidationResult};
use super::{CompilationResult, ErrorIterator};
use crate::context::CompilationContext;
use crate::error::{CompilationError, ValidationError};
use crate::error::{error, no_error, CompilationError, ValidationError};
use crate::JSONSchema;
use serde_json::{from_str, Map, Value};
pub struct ContentMediaTypeValidator {
func: fn(&str) -> ValidationResult,
func: fn(&str) -> ErrorIterator,
}
impl<'a> ContentMediaTypeValidator {
pub(crate) fn compile(func: fn(&str) -> ValidationResult) -> CompilationResult<'a> {
impl ContentMediaTypeValidator {
pub(crate) fn compile(func: fn(&str) -> ErrorIterator) -> CompilationResult {
Ok(Box::new(ContentMediaTypeValidator { func }))
}
}
impl<'a> Validate<'a> for ContentMediaTypeValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for ContentMediaTypeValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::String(item) = instance {
return (self.func)(item);
}
Ok(())
no_error()
}
fn name(&self) -> String {
// TODO. store media type
@ -29,21 +29,21 @@ impl<'a> Validate<'a> for ContentMediaTypeValidator {
}
pub struct ContentEncodingValidator {
func: fn(&str) -> ValidationResult,
func: fn(&str) -> ErrorIterator,
}
impl<'a> ContentEncodingValidator {
pub(crate) fn compile(func: fn(&str) -> ValidationResult) -> CompilationResult<'a> {
impl ContentEncodingValidator {
pub(crate) fn compile(func: fn(&str) -> ErrorIterator) -> CompilationResult {
Ok(Box::new(ContentEncodingValidator { func }))
}
}
impl<'a> Validate<'a> for ContentEncodingValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for ContentEncodingValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::String(item) = instance {
return (self.func)(item);
}
Ok(())
no_error()
}
fn name(&self) -> String {
// TODO. store encoding
@ -52,15 +52,15 @@ impl<'a> Validate<'a> for ContentEncodingValidator {
}
pub struct ContentMediaTypeAndEncodingValidator {
func: fn(&str) -> ValidationResult,
func: fn(&str) -> ErrorIterator,
converter: fn(&str) -> Result<String, ValidationError>,
}
impl<'a> ContentMediaTypeAndEncodingValidator {
pub(crate) fn compile(
func: fn(&str) -> ValidationResult,
func: fn(&str) -> ErrorIterator,
converter: fn(&str) -> Result<String, ValidationError>,
) -> CompilationResult<'a> {
) -> CompilationResult {
Ok(Box::new(ContentMediaTypeAndEncodingValidator {
func,
converter,
@ -68,13 +68,18 @@ impl<'a> ContentMediaTypeAndEncodingValidator {
}
}
impl<'a> Validate<'a> for ContentMediaTypeAndEncodingValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for ContentMediaTypeAndEncodingValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::String(item) = instance {
let converted = (self.converter)(item)?;
return (self.func)(&converted);
return match (self.converter)(item) {
Ok(converted) => {
let errors: Vec<_> = (self.func)(&converted).collect();
Box::new(errors.into_iter())
}
Err(e) => error(e),
};
}
Ok(())
no_error()
}
fn name(&self) -> String {
// TODO. store encoding
@ -82,21 +87,21 @@ impl<'a> Validate<'a> for ContentMediaTypeAndEncodingValidator {
}
}
pub(crate) fn is_json(instance: &str) -> ValidationResult {
pub(crate) fn is_json(instance: &str) -> ErrorIterator {
if from_str::<Value>(instance).is_err() {
return Err(ValidationError::format(
return error(ValidationError::format(
instance.to_owned(),
"application/json",
));
}
Ok(())
no_error()
}
pub(crate) fn is_base64(instance: &str) -> ValidationResult {
pub(crate) fn is_base64(instance: &str) -> ErrorIterator {
if base64::decode(instance).is_err() {
return Err(ValidationError::format(instance.to_owned(), "base64"));
return error(ValidationError::format(instance.to_owned(), "base64"));
}
Ok(())
no_error()
}
pub(crate) fn from_base64(instance: &str) -> Result<String, ValidationError> {
@ -106,11 +111,11 @@ pub(crate) fn from_base64(instance: &str) -> Result<String, ValidationError> {
}
}
pub(crate) fn compile_media_type<'a>(
schema: &'a Map<String, Value>,
subschema: &'a Value,
pub(crate) fn compile_media_type(
schema: &Map<String, Value>,
subschema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
match subschema {
Value::String(content_type) => {
let func = match content_type.as_str() {
@ -138,11 +143,11 @@ pub(crate) fn compile_media_type<'a>(
}
}
pub(crate) fn compile_content_encoding<'a>(
schema: &'a Map<String, Value>,
subschema: &'a Value,
pub(crate) fn compile_content_encoding(
schema: &Map<String, Value>,
subschema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
// Performed during media type validation
if schema.get("contentMediaType").is_some() {
// TODO. what if media type is not supported?

View File

@ -1,21 +1,18 @@
use super::{CompilationResult, ValidationResult};
use super::CompilationResult;
use super::{Validate, Validators};
use crate::context::CompilationContext;
use crate::error::CompilationError;
use crate::error::{no_error, CompilationError, ErrorIterator};
use crate::keywords::required::RequiredValidator;
use crate::validator::compile_validators;
use crate::JSONSchema;
use serde_json::{Map, Value};
pub struct DependenciesValidator<'a> {
dependencies: Vec<(&'a String, Validators<'a>)>,
pub struct DependenciesValidator {
dependencies: Vec<(String, Validators)>,
}
impl<'a> DependenciesValidator<'a> {
pub(crate) fn compile(
schema: &'a Value,
context: &CompilationContext,
) -> CompilationResult<'a> {
impl DependenciesValidator {
pub(crate) fn compile(schema: &Value, context: &CompilationContext) -> CompilationResult {
match schema.as_object() {
Some(map) => {
let mut dependencies = Vec::with_capacity(map.len());
@ -24,7 +21,7 @@ impl<'a> DependenciesValidator<'a> {
Value::Array(_) => vec![RequiredValidator::compile(subschema)?],
_ => compile_validators(subschema, context)?,
};
dependencies.push((key, s))
dependencies.push((key.clone(), s))
}
Ok(Box::new(DependenciesValidator { dependencies }))
}
@ -33,20 +30,23 @@ impl<'a> DependenciesValidator<'a> {
}
}
impl<'a> Validate<'a> for DependenciesValidator<'a> {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for DependenciesValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Object(item) = instance {
for (property, validators) in self.dependencies.iter() {
if !item.contains_key(*property) {
continue;
}
// TODO. custom error message for "required" case
for validator in validators.iter() {
validator.validate(schema, instance)?
}
}
let errors: Vec<_> = self
.dependencies
.iter()
.filter(|(property, _)| item.contains_key(property))
.flat_map(move |(_, validators)| {
validators
.iter()
.flat_map(move |validator| validator.validate(schema, instance))
})
.collect();
// TODO. custom error message for "required" case
return Box::new(errors.into_iter());
}
Ok(())
no_error()
}
fn name(&self) -> String {
@ -54,10 +54,10 @@ impl<'a> Validate<'a> for DependenciesValidator<'a> {
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
context: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(DependenciesValidator::compile(schema, context))
}

View File

@ -1,46 +1,43 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::context::CompilationContext;
use crate::error::{CompilationError, ValidationError};
use crate::error::{no_error, CompilationError, ErrorIterator, ValidationError};
use crate::{helpers, JSONSchema};
use serde_json::{Map, Value};
pub struct EnumValidator<'a> {
options: &'a Value,
items: &'a Vec<Value>,
pub struct EnumValidator {
options: Value,
items: Vec<Value>,
}
impl<'a> EnumValidator<'a> {
pub(crate) fn compile(schema: &'a Value) -> CompilationResult<'a> {
impl EnumValidator {
pub(crate) fn compile(schema: &Value) -> CompilationResult {
if let Value::Array(items) = schema {
return Ok(Box::new(EnumValidator {
options: schema,
items,
options: schema.clone(),
items: items.clone(),
}));
}
Err(CompilationError::SchemaError)
}
}
impl<'a> Validate<'a> for EnumValidator<'a> {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for EnumValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if !self.items.iter().any(|item| helpers::equal(instance, item)) {
return Err(ValidationError::enumeration(
instance.clone(),
self.options.clone(),
));
return ValidationError::enumeration(instance.clone(), self.options.clone());
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<enum: {:?}>", self.items)
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(EnumValidator::compile(schema))
}

View File

@ -1,7 +1,7 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::context::CompilationContext;
use crate::error::{CompilationError, ValidationError};
use crate::error::{no_error, CompilationError, ErrorIterator, ValidationError};
use crate::JSONSchema;
use serde_json::{Map, Value};
@ -10,7 +10,7 @@ pub struct ExclusiveMaximumValidator {
}
impl<'a> ExclusiveMaximumValidator {
pub(crate) fn compile(schema: &Value) -> CompilationResult<'a> {
pub(crate) fn compile(schema: &Value) -> CompilationResult {
if let Value::Number(limit) = schema {
return Ok(Box::new(ExclusiveMaximumValidator {
limit: limit.as_f64().unwrap(),
@ -20,25 +20,25 @@ impl<'a> ExclusiveMaximumValidator {
}
}
impl<'a> Validate<'a> for ExclusiveMaximumValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for ExclusiveMaximumValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Number(item) = instance {
let item = item.as_f64().unwrap();
if item >= self.limit {
return Err(ValidationError::exclusive_maximum(item, self.limit));
return ValidationError::exclusive_maximum(item, self.limit);
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<exclusive maximum: {}>", self.limit)
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(ExclusiveMaximumValidator::compile(schema))
}

View File

@ -1,7 +1,7 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::context::CompilationContext;
use crate::error::{CompilationError, ValidationError};
use crate::error::{no_error, CompilationError, ErrorIterator, ValidationError};
use crate::JSONSchema;
use serde_json::{Map, Value};
@ -10,7 +10,7 @@ pub struct ExclusiveMinimumValidator {
}
impl<'a> ExclusiveMinimumValidator {
pub(crate) fn compile(schema: &Value) -> CompilationResult<'a> {
pub(crate) fn compile(schema: &Value) -> CompilationResult {
if let Value::Number(limit) = schema {
let limit = limit.as_f64().unwrap();
return Ok(Box::new(ExclusiveMinimumValidator { limit }));
@ -19,24 +19,24 @@ impl<'a> ExclusiveMinimumValidator {
}
}
impl<'a> Validate<'a> for ExclusiveMinimumValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for ExclusiveMinimumValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Number(item) = instance {
let item = item.as_f64().unwrap();
if item <= self.limit {
return Err(ValidationError::exclusive_minimum(item, self.limit));
return ValidationError::exclusive_minimum(item, self.limit);
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<exclusive minimum: {}>", self.limit)
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(ExclusiveMinimumValidator::compile(schema))
}

View File

@ -1,26 +1,26 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::context::CompilationContext;
use crate::error::CompilationError;
use crate::error::{no_error, CompilationError, ErrorIterator};
use crate::{checks, JSONSchema};
use serde_json::{Map, Value};
pub struct FormatValidator {
check: fn(&str) -> ValidationResult,
check: fn(&str) -> ErrorIterator,
}
impl<'a> FormatValidator {
pub(crate) fn compile(check: fn(&str) -> ValidationResult) -> CompilationResult<'a> {
pub(crate) fn compile(check: fn(&str) -> ErrorIterator) -> CompilationResult {
Ok(Box::new(FormatValidator { check }))
}
}
impl<'a> Validate<'a> for FormatValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for FormatValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::String(item) = instance {
return (self.check)(item);
}
Ok(())
no_error()
}
fn name(&self) -> String {
// TODO. store name
@ -28,11 +28,11 @@ impl<'a> Validate<'a> for FormatValidator {
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
match schema.as_str() {
Some(format) => {
let func = match format {

View File

@ -1,21 +1,22 @@
use super::{CompilationResult, ValidationResult};
use super::CompilationResult;
use super::{Validate, Validators};
use crate::context::CompilationContext;
use crate::error::{no_error, ErrorIterator};
use crate::validator::compile_validators;
use crate::JSONSchema;
use serde_json::{Map, Value};
pub struct IfThenValidator<'a> {
schema: Validators<'a>,
then_schema: Validators<'a>,
pub struct IfThenValidator {
schema: Validators,
then_schema: Validators,
}
impl<'a> IfThenValidator<'a> {
impl IfThenValidator {
pub(crate) fn compile(
schema: &'a Value,
then_schema: &'a Value,
schema: &Value,
then_schema: &Value,
context: &CompilationContext,
) -> CompilationResult<'a> {
) -> CompilationResult {
Ok(Box::new(IfThenValidator {
schema: compile_validators(schema, context)?,
then_schema: compile_validators(then_schema, context)?,
@ -23,35 +24,38 @@ impl<'a> IfThenValidator<'a> {
}
}
impl<'a> Validate<'a> for IfThenValidator<'a> {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for IfThenValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if self
.schema
.iter()
.all(|validator| validator.is_valid(schema, instance))
{
for validator in self.then_schema.iter() {
validator.validate(schema, instance)?
}
let errors: Vec<_> = self
.then_schema
.iter()
.flat_map(move |validator| validator.validate(schema, instance))
.collect();
return Box::new(errors.into_iter());
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<if-then: {:?} {:?}>", self.schema, self.then_schema)
}
}
pub struct IfElseValidator<'a> {
schema: Validators<'a>,
else_schema: Validators<'a>,
pub struct IfElseValidator {
schema: Validators,
else_schema: Validators,
}
impl<'a> IfElseValidator<'a> {
impl<'a> IfElseValidator {
pub(crate) fn compile(
schema: &'a Value,
else_schema: &'a Value,
context: &CompilationContext,
) -> CompilationResult<'a> {
) -> CompilationResult {
Ok(Box::new(IfElseValidator {
schema: compile_validators(schema, context)?,
else_schema: compile_validators(else_schema, context)?,
@ -59,37 +63,40 @@ impl<'a> IfElseValidator<'a> {
}
}
impl<'a> Validate<'a> for IfElseValidator<'a> {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for IfElseValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if self
.schema
.iter()
.any(|validator| !validator.is_valid(schema, instance))
{
for validator in self.else_schema.iter() {
validator.validate(schema, instance)?
}
let errors: Vec<_> = self
.else_schema
.iter()
.flat_map(move |validator| validator.validate(schema, instance))
.collect();
return Box::new(errors.into_iter());
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<if-else: {:?} {:?}>", self.schema, self.else_schema)
}
}
pub struct IfThenElseValidator<'a> {
schema: Validators<'a>,
then_schema: Validators<'a>,
else_schema: Validators<'a>,
pub struct IfThenElseValidator {
schema: Validators,
then_schema: Validators,
else_schema: Validators,
}
impl<'a> IfThenElseValidator<'a> {
impl IfThenElseValidator {
pub(crate) fn compile(
schema: &'a Value,
then_schema: &'a Value,
else_schema: &'a Value,
schema: &Value,
then_schema: &Value,
else_schema: &Value,
context: &CompilationContext,
) -> CompilationResult<'a> {
) -> CompilationResult {
Ok(Box::new(IfThenElseValidator {
schema: compile_validators(schema, context)?,
then_schema: compile_validators(then_schema, context)?,
@ -98,22 +105,27 @@ impl<'a> IfThenElseValidator<'a> {
}
}
impl<'a> Validate<'a> for IfThenElseValidator<'a> {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for IfThenElseValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if self
.schema
.iter()
.all(|validator| validator.is_valid(schema, instance))
{
for validator in self.then_schema.iter() {
validator.validate(schema, instance)?
}
let errors: Vec<_> = self
.then_schema
.iter()
.flat_map(move |validator| validator.validate(schema, instance))
.collect();
Box::new(errors.into_iter())
} else {
for validator in self.else_schema.iter() {
validator.validate(schema, instance)?
}
let errors: Vec<_> = self
.else_schema
.iter()
.flat_map(move |validator| validator.validate(schema, instance))
.collect();
Box::new(errors.into_iter())
}
Ok(())
}
fn name(&self) -> String {
format!(
@ -123,11 +135,11 @@ impl<'a> Validate<'a> for IfThenElseValidator<'a> {
}
}
pub(crate) fn compile<'a>(
parent: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
parent: &Map<String, Value>,
schema: &Value,
context: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
let then = parent.get("then");
let else_ = parent.get("else");
match (then, else_) {

View File

@ -1,24 +1,18 @@
use super::{CompilationResult, ValidationResult};
use super::CompilationResult;
use super::{Validate, Validators};
use crate::context::CompilationContext;
use crate::error::ValidationError;
use crate::error::{no_error, ErrorIterator};
use crate::keywords::boolean::TrueValidator;
use crate::validator::compile_validators;
use crate::JSONSchema;
use rayon::prelude::*;
use serde_json::{Map, Value};
static PARALLEL_ITEMS_THRESHOLD: usize = 8;
pub struct ItemsArrayValidator<'a> {
items: Vec<Validators<'a>>,
pub struct ItemsArrayValidator {
items: Vec<Validators>,
}
impl<'a> ItemsArrayValidator<'a> {
pub(crate) fn compile(
schemas: &'a [Value],
context: &CompilationContext,
) -> CompilationResult<'a> {
impl ItemsArrayValidator {
pub(crate) fn compile(schemas: &[Value], context: &CompilationContext) -> CompilationResult {
let mut items = Vec::with_capacity(schemas.len());
for item in schemas {
let validators = compile_validators(item, context)?;
@ -28,62 +22,54 @@ impl<'a> ItemsArrayValidator<'a> {
}
}
impl<'a> Validate<'a> for ItemsArrayValidator<'a> {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for ItemsArrayValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Array(items) = instance {
for (item, validators) in items.iter().zip(self.items.iter()) {
for validator in validators {
validator.validate(schema, item)?
}
}
let errors: Vec<_> = items
.iter()
.zip(self.items.iter())
.flat_map(move |(item, validators)| {
validators
.iter()
.flat_map(move |validator| validator.validate(schema, item))
})
.collect();
return Box::new(errors.into_iter());
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<items: {:?}>", self.items)
}
}
pub struct ItemsObjectValidator<'a> {
validators: Validators<'a>,
pub struct ItemsObjectValidator {
validators: Validators,
}
impl<'a> ItemsObjectValidator<'a> {
pub(crate) fn compile(
schema: &'a Value,
context: &CompilationContext,
) -> CompilationResult<'a> {
impl ItemsObjectValidator {
pub(crate) fn compile(schema: &Value, context: &CompilationContext) -> CompilationResult {
let validators = compile_validators(schema, context)?;
Ok(Box::new(ItemsObjectValidator { validators }))
}
}
impl<'a> Validate<'a> for ItemsObjectValidator<'a> {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for ItemsObjectValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Array(items) = instance {
if items.len() > PARALLEL_ITEMS_THRESHOLD {
let validate = |item| {
for validator in self.validators.iter() {
match validator.validate(schema, item) {
Ok(_) => continue,
Err(e) => return Err(e),
}
}
Ok(())
};
if items.par_iter().map(validate).any(|res| res.is_err()) {
// TODO. it should be propagated! not necessarily "schema" error
return Err(ValidationError::schema());
}
} else {
for item in items {
for validator in self.validators.iter() {
validator.validate(schema, item)?
}
}
}
// TODO. make parallel
let errors: Vec<_> = self
.validators
.iter()
.flat_map(move |validator| {
items
.iter()
.flat_map(move |item| validator.validate(schema, item))
})
.collect();
return Box::new(errors.into_iter());
}
Ok(())
no_error()
}
fn name(&self) -> String {
@ -91,11 +77,11 @@ impl<'a> Validate<'a> for ItemsObjectValidator<'a> {
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
context: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
match schema {
Value::Array(items) => Some(ItemsArrayValidator::compile(&items, &context)),
Value::Object(_) => Some(ItemsObjectValidator::compile(schema, &context)),

View File

@ -1,7 +1,7 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::context::CompilationContext;
use crate::error::{CompilationError, ValidationError};
use crate::error::{no_error, CompilationError, ErrorIterator, ValidationError};
use crate::JSONSchema;
use serde_json::{Map, Value};
@ -10,7 +10,7 @@ pub struct MaxItemsValidator {
}
impl<'a> MaxItemsValidator {
pub(crate) fn compile(schema: &Value) -> CompilationResult<'a> {
pub(crate) fn compile(schema: &Value) -> CompilationResult {
if let Value::Number(limit) = schema {
let limit = limit.as_u64().unwrap() as usize;
return Ok(Box::new(MaxItemsValidator { limit }));
@ -19,14 +19,14 @@ impl<'a> MaxItemsValidator {
}
}
impl<'a> Validate<'a> for MaxItemsValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for MaxItemsValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Array(items) = instance {
if items.len() > self.limit {
return Err(ValidationError::max_items(instance.clone()));
return ValidationError::max_items(instance.clone());
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
@ -34,10 +34,10 @@ impl<'a> Validate<'a> for MaxItemsValidator {
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(MaxItemsValidator::compile(schema))
}

View File

@ -1,7 +1,7 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::context::CompilationContext;
use crate::error::{CompilationError, ValidationError};
use crate::error::{no_error, CompilationError, ErrorIterator, ValidationError};
use crate::JSONSchema;
use serde_json::{Map, Value};
@ -9,8 +9,8 @@ pub struct MaxLengthValidator {
limit: usize,
}
impl<'a> MaxLengthValidator {
pub(crate) fn compile(schema: &Value) -> CompilationResult<'a> {
impl MaxLengthValidator {
pub(crate) fn compile(schema: &Value) -> CompilationResult {
if let Value::Number(limit) = schema {
let limit = limit.as_u64().unwrap() as usize;
return Ok(Box::new(MaxLengthValidator { limit }));
@ -19,23 +19,23 @@ impl<'a> MaxLengthValidator {
}
}
impl<'a> Validate<'a> for MaxLengthValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for MaxLengthValidator {
fn validate<'a>(&self, _schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::String(item) = instance {
if item.chars().count() > self.limit {
return Err(ValidationError::max_length(item.clone()));
return ValidationError::max_length(item.clone());
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<max length: {}>", self.limit)
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(MaxLengthValidator::compile(schema))
}

View File

@ -1,7 +1,7 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::context::CompilationContext;
use crate::error::{CompilationError, ValidationError};
use crate::error::{no_error, CompilationError, ErrorIterator, ValidationError};
use crate::JSONSchema;
use serde_json::{Map, Value};
@ -10,7 +10,7 @@ pub struct MaxPropertiesValidator {
}
impl<'a> MaxPropertiesValidator {
pub(crate) fn compile(schema: &Value) -> CompilationResult<'a> {
pub(crate) fn compile(schema: &Value) -> CompilationResult {
if let Value::Number(limit) = schema {
let limit = limit.as_u64().unwrap() as usize;
return Ok(Box::new(MaxPropertiesValidator { limit }));
@ -19,23 +19,23 @@ impl<'a> MaxPropertiesValidator {
}
}
impl<'a> Validate<'a> for MaxPropertiesValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for MaxPropertiesValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Object(item) = instance {
if item.len() > self.limit {
return Err(ValidationError::max_properties(instance.clone()));
return ValidationError::max_properties(instance.clone());
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<max properties: {}>", self.limit)
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(MaxPropertiesValidator::compile(schema))
}

View File

@ -1,7 +1,7 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::context::CompilationContext;
use crate::error::{CompilationError, ValidationError};
use crate::error::{no_error, CompilationError, ErrorIterator, ValidationError};
use crate::JSONSchema;
use serde_json::{Map, Value};
@ -9,8 +9,8 @@ pub struct MaximumValidator {
limit: f64,
}
impl<'a> MaximumValidator {
pub(crate) fn compile(schema: &Value) -> CompilationResult<'a> {
impl MaximumValidator {
pub(crate) fn compile(schema: &Value) -> CompilationResult {
if let Value::Number(limit) = schema {
let limit = limit.as_f64().unwrap();
return Ok(Box::new(MaximumValidator { limit }));
@ -19,25 +19,25 @@ impl<'a> MaximumValidator {
}
}
impl<'a> Validate<'a> for MaximumValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for MaximumValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Number(item) = instance {
let item = item.as_f64().unwrap();
if item > self.limit {
return Err(ValidationError::maximum(item, self.limit));
return ValidationError::maximum(item, self.limit);
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<maximum: {}>", self.limit)
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(MaximumValidator::compile(schema))
}

View File

@ -1,7 +1,7 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::context::CompilationContext;
use crate::error::{CompilationError, ValidationError};
use crate::error::{no_error, CompilationError, ErrorIterator, ValidationError};
use crate::JSONSchema;
use serde_json::{Map, Value};
@ -9,8 +9,8 @@ pub struct MinItemsValidator {
limit: usize,
}
impl<'a> MinItemsValidator {
pub(crate) fn compile(schema: &Value) -> CompilationResult<'a> {
impl MinItemsValidator {
pub(crate) fn compile(schema: &Value) -> CompilationResult {
if let Value::Number(limit) = schema {
let limit = limit.as_u64().unwrap() as usize;
return Ok(Box::new(MinItemsValidator { limit }));
@ -19,23 +19,23 @@ impl<'a> MinItemsValidator {
}
}
impl<'a> Validate<'a> for MinItemsValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for MinItemsValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Array(items) = instance {
if items.len() < self.limit {
return Err(ValidationError::min_items(instance.clone()));
return ValidationError::min_items(instance.clone());
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<min items: {}>", self.limit)
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(MinItemsValidator::compile(schema))
}

View File

@ -1,7 +1,7 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::context::CompilationContext;
use crate::error::{CompilationError, ValidationError};
use crate::error::{no_error, CompilationError, ErrorIterator, ValidationError};
use crate::JSONSchema;
use serde_json::{Map, Value};
@ -10,7 +10,7 @@ pub struct MinLengthValidator {
}
impl<'a> MinLengthValidator {
pub(crate) fn compile(schema: &Value) -> CompilationResult<'a> {
pub(crate) fn compile(schema: &Value) -> CompilationResult {
if let Value::Number(limit) = schema {
let limit = limit.as_u64().unwrap() as usize;
return Ok(Box::new(MinLengthValidator { limit }));
@ -19,24 +19,24 @@ impl<'a> MinLengthValidator {
}
}
impl<'a> Validate<'a> for MinLengthValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for MinLengthValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::String(item) = instance {
if item.chars().count() < self.limit {
return Err(ValidationError::min_length(item.clone()));
return ValidationError::min_length(item.clone());
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<min length: {}>", self.limit)
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(MinLengthValidator::compile(schema))
}

View File

@ -1,7 +1,7 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::context::CompilationContext;
use crate::error::{CompilationError, ValidationError};
use crate::error::{no_error, CompilationError, ErrorIterator, ValidationError};
use crate::JSONSchema;
use serde_json::{Map, Value};
@ -10,7 +10,7 @@ pub struct MinPropertiesValidator {
}
impl<'a> MinPropertiesValidator {
pub(crate) fn compile(schema: &Value) -> CompilationResult<'a> {
pub(crate) fn compile(schema: &Value) -> CompilationResult {
if let Value::Number(limit) = schema {
let limit = limit.as_u64().unwrap() as usize;
return Ok(Box::new(MinPropertiesValidator { limit }));
@ -19,24 +19,24 @@ impl<'a> MinPropertiesValidator {
}
}
impl<'a> Validate<'a> for MinPropertiesValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for MinPropertiesValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Object(item) = instance {
if item.len() < self.limit {
return Err(ValidationError::min_properties(instance.clone()));
return ValidationError::min_properties(instance.clone());
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<min properties: {}>", self.limit)
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(MinPropertiesValidator::compile(schema))
}

View File

@ -1,7 +1,7 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::context::CompilationContext;
use crate::error::{CompilationError, ValidationError};
use crate::error::{no_error, CompilationError, ErrorIterator, ValidationError};
use crate::JSONSchema;
use serde_json::{Map, Value};
@ -19,25 +19,25 @@ impl MinimumValidator {
}
}
impl<'a> Validate<'a> for MinimumValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for MinimumValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Number(item) = instance {
let item = item.as_f64().unwrap();
if item < self.limit {
return Err(ValidationError::minimum(item, self.limit));
return ValidationError::minimum(item, self.limit);
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<minimum: {}>", self.limit)
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(MinimumValidator::compile(schema))
}

View File

@ -33,30 +33,30 @@ pub(crate) mod required;
pub(crate) mod type_;
pub(crate) mod unique_items;
use crate::error;
use crate::error::ErrorIterator;
use crate::validator::JSONSchema;
use serde_json::Value;
use std::fmt::{Debug, Error, Formatter};
pub trait Validate<'a>: Send + Sync + 'a {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult;
// The same as above, but does not construct Result.
pub trait Validate: Send + Sync {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a>;
// The same as above, but does not construct ErrorIterator.
// It is faster for cases when the result is not needed (like anyOf), since errors are
// not constructed
fn is_valid(&self, schema: &JSONSchema, instance: &Value) -> bool {
self.validate(schema, instance).is_ok() // TODO. remove it and implement everywhere
self.validate(schema, instance).next().is_none()
}
fn name(&self) -> String {
"<validator>".to_string()
}
}
impl<'a> Debug for dyn Validate<'a> + Send + Sync + 'a {
impl Debug for dyn Validate + Send + Sync {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
f.write_str(&self.name())
}
}
pub type ValidationResult = Result<(), error::ValidationError>;
pub type CompilationResult<'a> = Result<BoxedValidator<'a>, error::CompilationError>;
pub type BoxedValidator<'a> = Box<dyn Validate<'a> + Send + Sync + 'a>;
pub type Validators<'a> = Vec<BoxedValidator<'a>>;
pub type CompilationResult = Result<BoxedValidator, error::CompilationError>;
pub type BoxedValidator = Box<dyn Validate + Send + Sync>;
pub type Validators = Vec<BoxedValidator>;

View File

@ -1,7 +1,7 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::context::CompilationContext;
use crate::error::{CompilationError, ValidationError};
use crate::error::{no_error, CompilationError, ErrorIterator, ValidationError};
use crate::JSONSchema;
use serde_json::{Map, Value};
use std::f64::EPSILON;
@ -11,21 +11,21 @@ pub struct MultipleOfFloatValidator {
}
impl<'a> MultipleOfFloatValidator {
pub(crate) fn compile(multiple_of: f64) -> CompilationResult<'a> {
pub(crate) fn compile(multiple_of: f64) -> CompilationResult {
Ok(Box::new(MultipleOfFloatValidator { multiple_of }))
}
}
impl<'a> Validate<'a> for MultipleOfFloatValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for MultipleOfFloatValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Number(item) = instance {
let item = item.as_f64().unwrap();
let remainder = (item / self.multiple_of) % 1.;
if !(remainder < EPSILON && remainder < (1. - EPSILON)) {
return Err(ValidationError::multiple_of(item, self.multiple_of));
return ValidationError::multiple_of(item, self.multiple_of);
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<multiple of: {}>", self.multiple_of)
@ -37,13 +37,13 @@ pub struct MultipleOfIntegerValidator {
}
impl<'a> MultipleOfIntegerValidator {
pub(crate) fn compile(multiple_of: f64) -> CompilationResult<'a> {
pub(crate) fn compile(multiple_of: f64) -> CompilationResult {
Ok(Box::new(MultipleOfIntegerValidator { multiple_of }))
}
}
impl<'a> Validate<'a> for MultipleOfIntegerValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for MultipleOfIntegerValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Number(item) = instance {
let item = item.as_f64().unwrap();
let is_multiple = if item.fract() == 0. {
@ -53,21 +53,21 @@ impl<'a> Validate<'a> for MultipleOfIntegerValidator {
remainder < EPSILON && remainder < (1. - EPSILON)
};
if !is_multiple {
return Err(ValidationError::multiple_of(item, self.multiple_of));
return ValidationError::multiple_of(item, self.multiple_of);
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<multiple of: {}>", self.multiple_of)
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
if let Value::Number(multiple_of) = schema {
let multiple_of = multiple_of.as_f64().unwrap();
return if multiple_of.fract() == 0. {

View File

@ -1,42 +1,36 @@
use super::{CompilationResult, ValidationResult};
use super::CompilationResult;
use super::{Validate, Validators};
use crate::context::CompilationContext;
use crate::error::ValidationError;
use crate::error::{no_error, ErrorIterator, ValidationError};
use crate::validator::compile_validators;
use crate::JSONSchema;
use serde_json::{Map, Value};
pub struct NotValidator<'a> {
pub struct NotValidator {
// needed only for error representation
original: &'a Value,
validators: Validators<'a>,
original: Value,
validators: Validators,
}
impl<'a> NotValidator<'a> {
pub(crate) fn compile(
schema: &'a Value,
context: &CompilationContext,
) -> CompilationResult<'a> {
impl NotValidator {
pub(crate) fn compile(schema: &Value, context: &CompilationContext) -> CompilationResult {
Ok(Box::new(NotValidator {
original: schema,
original: schema.clone(),
validators: compile_validators(schema, context)?,
}))
}
}
impl<'a> Validate<'a> for NotValidator<'a> {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for NotValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if self
.validators
.iter()
.all(|validator| validator.is_valid(schema, instance))
{
Err(ValidationError::not(
instance.clone(),
self.original.clone(),
))
ValidationError::not(instance.clone(), self.original.clone())
} else {
Ok(())
no_error()
}
}
fn name(&self) -> String {
@ -44,10 +38,10 @@ impl<'a> Validate<'a> for NotValidator<'a> {
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
context: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(NotValidator::compile(schema, context))
}

View File

@ -1,20 +1,17 @@
use super::{CompilationResult, ValidationResult};
use super::CompilationResult;
use super::{Validate, Validators};
use crate::context::CompilationContext;
use crate::error::{CompilationError, ValidationError};
use crate::error::{no_error, CompilationError, ErrorIterator, ValidationError};
use crate::validator::compile_validators;
use crate::JSONSchema;
use serde_json::{Map, Value};
pub struct OneOfValidator<'a> {
schemas: Vec<Validators<'a>>,
pub struct OneOfValidator {
schemas: Vec<Validators>,
}
impl<'a> OneOfValidator<'a> {
pub(crate) fn compile(
schema: &'a Value,
context: &CompilationContext,
) -> CompilationResult<'a> {
impl OneOfValidator {
pub(crate) fn compile(schema: &Value, context: &CompilationContext) -> CompilationResult {
match schema.as_array() {
Some(items) => {
let mut schemas = Vec::with_capacity(items.len());
@ -31,7 +28,7 @@ impl<'a> OneOfValidator<'a> {
&self,
schema: &JSONSchema,
instance: &Value,
) -> (Option<&Validators<'a>>, Option<usize>) {
) -> (Option<&Validators>, Option<usize>) {
let mut first_valid = None;
let mut first_valid_idx = None;
for (idx, validators) in self.schemas.iter().enumerate() {
@ -60,16 +57,16 @@ impl<'a> OneOfValidator<'a> {
}
}
impl<'a> Validate<'a> for OneOfValidator<'a> {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for OneOfValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
let (first_valid, first_valid_idx) = self.get_first_valid(schema, instance);
if first_valid.is_none() {
return Err(ValidationError::one_of_not_valid(instance.clone()));
return ValidationError::one_of_not_valid(instance.clone());
}
if self.are_others_valid(schema, instance, first_valid_idx) {
return Err(ValidationError::one_of_multiple_valid(instance.clone()));
return ValidationError::one_of_multiple_valid(instance.clone());
}
Ok(())
no_error()
}
fn is_valid(&self, schema: &JSONSchema, instance: &Value) -> bool {
let (first_valid, first_valid_idx) = self.get_first_valid(schema, instance);
@ -83,10 +80,10 @@ impl<'a> Validate<'a> for OneOfValidator<'a> {
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
context: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(OneOfValidator::compile(schema, context))
}

View File

@ -1,28 +1,29 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::context::CompilationContext;
use crate::error::{CompilationError, ValidationError};
use crate::error::{no_error, CompilationError, ErrorIterator, ValidationError};
use crate::JSONSchema;
use regex::{Captures, Regex};
use serde_json::{Map, Value};
use std::ops::Index;
lazy_static! {
static ref CONTROL_GROUPS_RE: Regex = Regex::new(r"\\c[A-Za-z]").unwrap();
}
pub struct PatternValidator<'a> {
original: &'a String,
pub struct PatternValidator {
original: String,
pattern: Regex,
}
impl<'a> PatternValidator<'a> {
pub(crate) fn compile(pattern: &'a Value) -> CompilationResult<'a> {
impl PatternValidator {
pub(crate) fn compile(pattern: &Value) -> CompilationResult {
match pattern {
Value::String(item) => {
let pattern = convert_regex(item)?;
Ok(Box::new(PatternValidator {
original: item,
original: item.clone(),
pattern,
}))
}
@ -31,17 +32,14 @@ impl<'a> PatternValidator<'a> {
}
}
impl<'a> Validate<'a> for PatternValidator<'a> {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for PatternValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::String(item) = instance {
if !self.pattern.is_match(item) {
return Err(ValidationError::pattern(
item.clone(),
self.original.clone(),
));
return ValidationError::pattern(item.clone(), self.original.clone());
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<pattern: {}>", self.pattern)
@ -74,10 +72,10 @@ fn replace_control_group(captures: &Captures) -> String {
- 64) as char)
.to_string()
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(PatternValidator::compile(schema))
}

View File

@ -1,21 +1,18 @@
use super::{CompilationResult, ValidationResult};
use super::CompilationResult;
use super::{Validate, Validators};
use crate::context::CompilationContext;
use crate::error::CompilationError;
use crate::error::{no_error, CompilationError, ErrorIterator};
use crate::validator::compile_validators;
use crate::JSONSchema;
use regex::Regex;
use serde_json::{Map, Value};
pub struct PatternPropertiesValidator<'a> {
patterns: Vec<(Regex, Validators<'a>)>,
pub struct PatternPropertiesValidator {
patterns: Vec<(Regex, Validators)>,
}
impl<'a> PatternPropertiesValidator<'a> {
pub(crate) fn compile(
properties: &'a Value,
context: &CompilationContext,
) -> CompilationResult<'a> {
impl PatternPropertiesValidator {
pub(crate) fn compile(properties: &Value, context: &CompilationContext) -> CompilationResult {
match properties.as_object() {
Some(map) => {
let mut patterns = Vec::with_capacity(map.len());
@ -32,30 +29,35 @@ impl<'a> PatternPropertiesValidator<'a> {
}
}
impl<'a> Validate<'a> for PatternPropertiesValidator<'a> {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for PatternPropertiesValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Object(item) = instance {
for (re, validators) in self.patterns.iter() {
for (key, value) in item {
if re.is_match(key) {
for validator in validators.iter() {
validator.validate(schema, value)?
}
}
}
}
let errors: Vec<_> = self
.patterns
.iter()
.flat_map(move |(re, validators)| {
item.iter()
.filter(move |(key, _)| re.is_match(key))
.flat_map(move |(_key, value)| {
validators
.iter()
.flat_map(move |validator| validator.validate(schema, value))
})
})
.collect();
return Box::new(errors.into_iter());
}
Ok(())
no_error()
}
fn name(&self) -> String {
format!("<pattern properties: {:?}>", self.patterns)
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
context: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(PatternPropertiesValidator::compile(schema, context))
}

View File

@ -1,24 +1,21 @@
use super::{CompilationResult, ValidationResult};
use super::CompilationResult;
use super::{Validate, Validators};
use crate::context::CompilationContext;
use crate::error::CompilationError;
use crate::error::{no_error, CompilationError, ErrorIterator};
use crate::validator::{compile_validators, JSONSchema};
use serde_json::{Map, Value};
pub struct PropertiesValidator<'a> {
properties: Vec<(&'a String, Validators<'a>)>,
pub struct PropertiesValidator {
properties: Vec<(String, Validators)>,
}
impl<'a> PropertiesValidator<'a> {
pub(crate) fn compile(
schema: &'a Value,
context: &CompilationContext,
) -> CompilationResult<'a> {
impl PropertiesValidator {
pub(crate) fn compile(schema: &Value, context: &CompilationContext) -> CompilationResult {
match schema {
Value::Object(map) => {
let mut properties = Vec::with_capacity(map.len());
for (key, subschema) in map {
properties.push((key, compile_validators(subschema, context)?));
properties.push((key.clone(), compile_validators(subschema, context)?));
}
Ok(Box::new(PropertiesValidator { properties }))
}
@ -27,29 +24,34 @@ impl<'a> PropertiesValidator<'a> {
}
}
impl<'a> Validate<'a> for PropertiesValidator<'a> {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for PropertiesValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Object(item) = instance {
for (name, validators) in self.properties.iter() {
if let Some(item) = item.get(*name) {
for validator in validators {
validator.validate(schema, item)?
}
}
}
let errors: Vec<_> = self
.properties
.iter()
.flat_map(move |(name, validators)| {
let option = item.get(name);
option.into_iter().flat_map(move |item| {
validators
.iter()
.flat_map(move |validator| validator.validate(schema, item))
})
})
.collect();
return Box::new(errors.into_iter());
}
Ok(())
no_error()
}
fn name(&self) -> String {
// "".to_string()
format!("<properties: {:?}>", self.properties)
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
context: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(PropertiesValidator::compile(schema, context))
}

View File

@ -1,37 +1,41 @@
use super::{CompilationResult, ValidationResult};
use super::CompilationResult;
use super::{Validate, Validators};
use crate::context::CompilationContext;
use crate::error::ValidationError;
use crate::error::{no_error, ErrorIterator, ValidationError};
use crate::validator::compile_validators;
use crate::JSONSchema;
use serde_json::{Map, Value};
use std::borrow::Borrow;
pub struct PropertyNamesObjectValidator<'a> {
validators: Validators<'a>,
pub struct PropertyNamesObjectValidator {
validators: Validators,
}
impl<'a> PropertyNamesObjectValidator<'a> {
pub(crate) fn compile(
schema: &'a Value,
context: &CompilationContext,
) -> CompilationResult<'a> {
impl PropertyNamesObjectValidator {
pub(crate) fn compile(schema: &Value, context: &CompilationContext) -> CompilationResult {
Ok(Box::new(PropertyNamesObjectValidator {
validators: compile_validators(schema, context)?,
}))
}
}
impl<'a> Validate<'a> for PropertyNamesObjectValidator<'a> {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
if let Value::Object(item) = instance {
for name in item.keys() {
let wrapper = Value::String(name.to_string());
for validator in self.validators.iter() {
validator.validate(schema, &wrapper)?
}
}
impl Validate for PropertyNamesObjectValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Object(item) = &instance.borrow() {
let errors: Vec<_> = self
.validators
.iter()
.flat_map(move |validator| {
item.keys().flat_map(move |key| {
let wrapper = Value::String(key.to_string());
let errors: Vec<_> = validator.validate(schema, &wrapper).collect();
errors.into_iter()
})
})
.collect();
return Box::new(errors.into_iter());
}
Ok(())
no_error()
}
fn name(&self) -> String {
@ -41,20 +45,20 @@ impl<'a> Validate<'a> for PropertyNamesObjectValidator<'a> {
pub struct PropertyNamesBooleanValidator {}
impl<'a> PropertyNamesBooleanValidator {
pub(crate) fn compile() -> CompilationResult<'a> {
impl PropertyNamesBooleanValidator {
pub(crate) fn compile() -> CompilationResult {
Ok(Box::new(PropertyNamesBooleanValidator {}))
}
}
impl<'a> Validate<'a> for PropertyNamesBooleanValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
if let Value::Object(item) = instance {
impl Validate for PropertyNamesBooleanValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Object(item) = instance.borrow() {
if !item.is_empty() {
return Err(ValidationError::false_schema(instance.clone()));
return ValidationError::false_schema(instance.clone());
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
@ -62,11 +66,11 @@ impl<'a> Validate<'a> for PropertyNamesBooleanValidator {
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
context: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
match schema {
Value::Object(_) => Some(PropertyNamesObjectValidator::compile(schema, context)),
Value::Bool(false) => Some(PropertyNamesBooleanValidator::compile()),

View File

@ -1,6 +1,7 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::context::CompilationContext;
use crate::error::{error, ErrorIterator};
use crate::validator::{compile_validators, JSONSchema};
use serde_json::Value;
use url::Url;
@ -9,28 +10,31 @@ pub struct RefValidator {
reference: Url,
}
impl<'a> RefValidator {
pub(crate) fn compile(reference: &str, context: &CompilationContext) -> CompilationResult<'a> {
impl RefValidator {
pub(crate) fn compile(reference: &str, context: &CompilationContext) -> CompilationResult {
let reference = context.build_url(reference)?;
Ok(Box::new(RefValidator { reference }))
}
}
impl<'a> Validate<'a> for RefValidator {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for RefValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
match schema
.resolver
.resolve_fragment(schema.draft, &self.reference, schema.schema)
{
Ok((scope, resolved)) => {
let context = CompilationContext::new(scope, schema.draft);
let validators = compile_validators(&resolved, &context)?;
for v in validators.iter() {
v.validate(schema, instance)?
match compile_validators(&resolved, &context) {
Ok(validators) => Box::new(
validators
.into_iter()
.flat_map(move |validator| validator.validate(schema, instance)),
),
Err(e) => error(e.into()),
}
Ok(())
}
Err(e) => Err(e),
Err(e) => error(e),
}
}
@ -38,10 +42,10 @@ impl<'a> Validate<'a> for RefValidator {
format!("<ref: {}>", self.reference)
}
}
pub(crate) fn compile<'a>(
_: &'a Value,
pub(crate) fn compile(
_: &Value,
reference: &str,
context: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(RefValidator::compile(reference, &context))
}

View File

@ -1,22 +1,22 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::context::CompilationContext;
use crate::error::{CompilationError, ValidationError};
use crate::error::{no_error, CompilationError, ErrorIterator, ValidationError};
use crate::JSONSchema;
use serde_json::{Map, Value};
pub struct RequiredValidator<'a> {
required: Vec<&'a String>,
pub struct RequiredValidator {
required: Vec<String>,
}
impl<'a> RequiredValidator<'a> {
pub(crate) fn compile(schema: &'a Value) -> CompilationResult<'a> {
impl RequiredValidator {
pub(crate) fn compile(schema: &Value) -> CompilationResult {
match schema {
Value::Array(items) => {
let mut required = Vec::with_capacity(items.len());
for item in items {
match item {
Value::String(string) => required.push(string),
Value::String(string) => required.push(string.clone()),
_ => return Err(CompilationError::SchemaError),
}
}
@ -27,17 +27,16 @@ impl<'a> RequiredValidator<'a> {
}
}
impl<'a> Validate<'a> for RequiredValidator<'a> {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for RequiredValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if let Value::Object(item) = instance {
for property_name in self.required.iter() {
let name = *property_name;
if !item.contains_key(name) {
return Err(ValidationError::required(name.clone()));
if !item.contains_key(property_name) {
return ValidationError::required(property_name.clone());
}
}
}
Ok(())
no_error()
}
fn name(&self) -> String {
@ -45,10 +44,10 @@ impl<'a> Validate<'a> for RequiredValidator<'a> {
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
Some(RequiredValidator::compile(schema))
}

View File

@ -1,7 +1,7 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::context::CompilationContext;
use crate::error::{CompilationError, PrimitiveType, ValidationError};
use crate::error::{no_error, CompilationError, ErrorIterator, PrimitiveType, ValidationError};
use crate::validator::JSONSchema;
use serde_json::{Map, Number, Value};
@ -10,7 +10,7 @@ pub struct MultipleTypesValidator {
}
impl MultipleTypesValidator {
pub(crate) fn compile<'a>(items: &[Value]) -> CompilationResult<'a> {
pub(crate) fn compile(items: &[Value]) -> CompilationResult {
let mut types = Vec::with_capacity(items.len());
for item in items {
match item {
@ -31,15 +31,12 @@ impl MultipleTypesValidator {
}
}
impl<'a> Validate<'a> for MultipleTypesValidator {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for MultipleTypesValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if !self.is_valid(schema, instance) {
return Err(ValidationError::multiple_type_error(
instance.clone(),
self.types.clone(),
));
return ValidationError::multiple_type_error(instance.clone(), self.types.clone());
}
Ok(())
no_error()
}
fn is_valid(&self, _: &JSONSchema, instance: &Value) -> bool {
for type_ in self.types.iter() {
@ -65,20 +62,17 @@ impl<'a> Validate<'a> for MultipleTypesValidator {
pub struct NullTypeValidator {}
impl NullTypeValidator {
pub(crate) fn compile<'a>() -> CompilationResult<'a> {
pub(crate) fn compile() -> CompilationResult {
Ok(Box::new(NullTypeValidator {}))
}
}
impl<'a> Validate<'a> for NullTypeValidator {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for NullTypeValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if !self.is_valid(schema, instance) {
return Err(ValidationError::single_type_error(
instance.clone(),
PrimitiveType::Null,
));
return ValidationError::single_type_error(instance.clone(), PrimitiveType::Null);
}
Ok(())
no_error()
}
fn is_valid(&self, _: &JSONSchema, instance: &Value) -> bool {
instance.is_null()
@ -92,20 +86,17 @@ impl<'a> Validate<'a> for NullTypeValidator {
pub struct BooleanTypeValidator {}
impl BooleanTypeValidator {
pub(crate) fn compile<'a>() -> CompilationResult<'a> {
pub(crate) fn compile() -> CompilationResult {
Ok(Box::new(BooleanTypeValidator {}))
}
}
impl<'a> Validate<'a> for BooleanTypeValidator {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for BooleanTypeValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if !self.is_valid(schema, instance) {
return Err(ValidationError::single_type_error(
instance.clone(),
PrimitiveType::Boolean,
));
return ValidationError::single_type_error(instance.clone(), PrimitiveType::Boolean);
}
Ok(())
no_error()
}
fn is_valid(&self, _: &JSONSchema, instance: &Value) -> bool {
instance.is_boolean()
@ -119,20 +110,17 @@ impl<'a> Validate<'a> for BooleanTypeValidator {
pub struct StringTypeValidator {}
impl StringTypeValidator {
pub(crate) fn compile<'a>() -> CompilationResult<'a> {
pub(crate) fn compile() -> CompilationResult {
Ok(Box::new(StringTypeValidator {}))
}
}
impl<'a> Validate<'a> for StringTypeValidator {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for StringTypeValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if !self.is_valid(schema, instance) {
return Err(ValidationError::single_type_error(
instance.clone(),
PrimitiveType::String,
));
return ValidationError::single_type_error(instance.clone(), PrimitiveType::String);
}
Ok(())
no_error()
}
fn is_valid(&self, _: &JSONSchema, instance: &Value) -> bool {
@ -147,20 +135,17 @@ impl<'a> Validate<'a> for StringTypeValidator {
pub struct ArrayTypeValidator {}
impl ArrayTypeValidator {
pub(crate) fn compile<'a>() -> CompilationResult<'a> {
pub(crate) fn compile() -> CompilationResult {
Ok(Box::new(ArrayTypeValidator {}))
}
}
impl<'a> Validate<'a> for ArrayTypeValidator {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for ArrayTypeValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if !self.is_valid(schema, instance) {
return Err(ValidationError::single_type_error(
instance.clone(),
PrimitiveType::Array,
));
return ValidationError::single_type_error(instance.clone(), PrimitiveType::Array);
}
Ok(())
no_error()
}
fn is_valid(&self, _: &JSONSchema, instance: &Value) -> bool {
@ -175,20 +160,17 @@ impl<'a> Validate<'a> for ArrayTypeValidator {
pub struct ObjectTypeValidator {}
impl ObjectTypeValidator {
pub(crate) fn compile<'a>() -> CompilationResult<'a> {
pub(crate) fn compile() -> CompilationResult {
Ok(Box::new(ObjectTypeValidator {}))
}
}
impl<'a> Validate<'a> for ObjectTypeValidator {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for ObjectTypeValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if !self.is_valid(schema, instance) {
return Err(ValidationError::single_type_error(
instance.clone(),
PrimitiveType::Object,
));
return ValidationError::single_type_error(instance.clone(), PrimitiveType::Object);
}
Ok(())
no_error()
}
fn is_valid(&self, _: &JSONSchema, instance: &Value) -> bool {
instance.is_object()
@ -202,20 +184,17 @@ impl<'a> Validate<'a> for ObjectTypeValidator {
pub struct NumberTypeValidator {}
impl NumberTypeValidator {
pub(crate) fn compile<'a>() -> CompilationResult<'a> {
pub(crate) fn compile() -> CompilationResult {
Ok(Box::new(NumberTypeValidator {}))
}
}
impl<'a> Validate<'a> for NumberTypeValidator {
fn validate(&self, _: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for NumberTypeValidator {
fn validate<'a>(&self, _: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if !instance.is_number() {
return Err(ValidationError::single_type_error(
instance.clone(),
PrimitiveType::Number,
));
return ValidationError::single_type_error(instance.clone(), PrimitiveType::Number);
}
Ok(())
no_error()
}
fn is_valid(&self, _: &JSONSchema, instance: &Value) -> bool {
instance.is_number()
@ -229,20 +208,17 @@ impl<'a> Validate<'a> for NumberTypeValidator {
pub struct IntegerTypeValidator {}
impl IntegerTypeValidator {
pub(crate) fn compile<'a>() -> CompilationResult<'a> {
pub(crate) fn compile() -> CompilationResult {
Ok(Box::new(IntegerTypeValidator {}))
}
}
impl<'a> Validate<'a> for IntegerTypeValidator {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for IntegerTypeValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if !self.is_valid(schema, instance) {
return Err(ValidationError::single_type_error(
instance.clone(),
PrimitiveType::Integer,
));
return ValidationError::single_type_error(instance.clone(), PrimitiveType::Integer);
}
Ok(())
no_error()
}
fn is_valid(&self, _: &JSONSchema, instance: &Value) -> bool {
@ -261,11 +237,11 @@ fn is_integer(num: &Number) -> bool {
num.is_u64() || num.is_i64() || num.as_f64().unwrap().fract() == 0.
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
match schema {
Value::String(item) => compile_single_type(item.as_str()),
Value::Array(items) => {
@ -283,7 +259,7 @@ pub(crate) fn compile<'a>(
}
}
fn compile_single_type<'a>(item: &str) -> Option<CompilationResult<'a>> {
fn compile_single_type(item: &str) -> Option<CompilationResult> {
match item {
"integer" => Some(IntegerTypeValidator::compile()),
"null" => Some(NullTypeValidator::compile()),

View File

@ -1,7 +1,7 @@
use super::CompilationResult;
use super::Validate;
use super::{CompilationResult, ValidationResult};
use crate::context::CompilationContext;
use crate::error::ValidationError;
use crate::error::{no_error, ErrorIterator, ValidationError};
use crate::validator::JSONSchema;
use serde_json::{Map, Value};
use std::collections::hash_map::DefaultHasher;
@ -59,17 +59,17 @@ pub fn is_unique(items: &[Value]) -> bool {
pub struct UniqueItemsValidator {}
impl UniqueItemsValidator {
pub(crate) fn compile<'a>() -> CompilationResult<'a> {
pub(crate) fn compile() -> CompilationResult {
Ok(Box::new(UniqueItemsValidator {}))
}
}
impl<'a> Validate<'a> for UniqueItemsValidator {
fn validate(&self, schema: &JSONSchema, instance: &Value) -> ValidationResult {
impl Validate for UniqueItemsValidator {
fn validate<'a>(&self, schema: &'a JSONSchema, instance: &'a Value) -> ErrorIterator<'a> {
if !self.is_valid(schema, instance) {
return Err(ValidationError::unique_items(instance.clone()));
return ValidationError::unique_items(instance.clone());
}
Ok(())
no_error()
}
fn is_valid(&self, _: &JSONSchema, instance: &Value) -> bool {
@ -85,11 +85,11 @@ impl<'a> Validate<'a> for UniqueItemsValidator {
"<unique items>".to_string()
}
}
pub(crate) fn compile<'a>(
_: &'a Map<String, Value>,
schema: &'a Value,
pub(crate) fn compile(
_: &Map<String, Value>,
schema: &Value,
_: &CompilationContext,
) -> Option<CompilationResult<'a>> {
) -> Option<CompilationResult> {
if let Value::Bool(value) = schema {
if *value {
Some(UniqueItemsValidator::compile())

View File

@ -6,9 +6,31 @@ mod keywords;
mod resolver;
mod schemas;
mod validator;
pub use error::ValidationError;
pub use error::{ErrorIterator, ValidationError};
pub use schemas::Draft;
use serde_json::Value;
pub use validator::JSONSchema;
#[macro_use]
extern crate lazy_static;
/// Validates `instance` against `schema`. Draft version is detected automatically.
pub fn is_valid(schema: &Value, instance: &Value) -> bool {
let compiled = JSONSchema::compile(schema, None).expect("Invalid schema");
compiled.is_valid(instance)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_is_valid() {
let schema = json!({"minLength": 5});
let valid = json!("foobar");
let invalid = json!("foo");
assert!(is_valid(&schema, &valid));
assert!(!is_valid(&schema, &invalid));
}
}

View File

@ -10,14 +10,11 @@ pub enum Draft {
Draft7,
}
type CompileFunc<'a> = fn(
&'a Map<String, Value>,
&'a Value,
&CompilationContext,
) -> Option<keywords::CompilationResult<'a>>;
type CompileFunc =
fn(&Map<String, Value>, &Value, &CompilationContext) -> Option<keywords::CompilationResult>;
impl Draft {
pub(crate) fn get_validator<'a>(self, keyword: &str) -> Option<CompileFunc<'a>> {
pub(crate) fn get_validator(self, keyword: &str) -> Option<CompileFunc> {
match self {
Draft::Draft7 => match keyword {
"additionalItems" => Some(keywords::additional_items::compile),

View File

@ -1,6 +1,6 @@
use crate::context::CompilationContext;
use crate::error::CompilationError;
use crate::keywords::{ValidationResult, Validators};
use crate::error::{CompilationError, ErrorIterator};
use crate::keywords::Validators;
use crate::resolver::Resolver;
use crate::schemas::{id_of, Draft};
use crate::{keywords, schemas};
@ -11,7 +11,7 @@ pub(crate) const DOCUMENT_PROTOCOL: &str = "json-schema:///";
pub struct JSONSchema<'a> {
pub(crate) draft: Draft,
pub(crate) schema: &'a Value,
pub(crate) validators: Validators<'a>,
pub(crate) validators: Validators,
pub(crate) resolver: Resolver<'a>,
}
@ -39,11 +39,17 @@ impl<'a> JSONSchema<'a> {
})
}
pub fn validate(&self, instance: &Value) -> ValidationResult {
for v in self.validators.iter() {
v.validate(self, instance)?
pub fn validate(&'a self, instance: &'a Value) -> Result<(), ErrorIterator<'a>> {
let mut errors = self
.validators
.iter()
.flat_map(move |validator| validator.validate(self, instance))
.peekable();
if errors.peek().is_none() {
Ok(())
} else {
Err(Box::new(errors))
}
Ok(())
}
pub fn is_valid(&self, instance: &Value) -> bool {
@ -53,10 +59,10 @@ impl<'a> JSONSchema<'a> {
}
}
pub(crate) fn compile_validators<'a>(
schema: &'a Value,
pub(crate) fn compile_validators(
schema: &Value,
context: &CompilationContext,
) -> Result<Validators<'a>, CompilationError> {
) -> Result<Validators, CompilationError> {
let context = context.push(schema);
match schema {
Value::Bool(value) => {
@ -96,6 +102,7 @@ pub(crate) fn compile_validators<'a>(
#[cfg(test)]
mod tests {
use super::*;
use crate::ValidationError;
use serde_json::*;
use std::borrow::Cow;
use std::fs::File;
@ -117,9 +124,9 @@ mod tests {
fn only_keyword() {
// When only one keyword is specified
let schema = json!({"type": "string"});
let compiled = JSONSchema::compile(&schema, None).unwrap();
let value1 = json!("AB");
let value2 = json!(1);
let compiled = JSONSchema::compile(&schema, None).unwrap();
// And only this validator
assert_eq!(compiled.validators.len(), 1);
assert!(compiled.validate(&value1).is_ok());
@ -150,4 +157,19 @@ mod tests {
let value = json!({"bar": true});
assert!(compiled.validate(&value).is_err());
}
#[test]
fn multiple_errors() {
let schema = json!({"minProperties": 2, "propertyNames": {"minLength": 3}});
let value = json!({"a": 3});
let compiled = JSONSchema::compile(&schema, None).unwrap();
let result = compiled.validate(&value);
let errors: Vec<ValidationError> = result.unwrap_err().collect();
assert_eq!(errors.len(), 2);
assert_eq!(
format!("{}", errors[0]),
r#"'{"a":3}' does not have enough properties"#
);
assert_eq!(format!("{}", errors[1]), r#"'a' is too short"#);
}
}