use alloc::boxed::Box;
use alloc::vec::Vec;
use serde_json::Value;
use crate::error::{ErrorIterator, ValidationError, ValidationErrorBuilder, ValidationErrorKind};
use crate::node::SchemaNode;
use crate::paths::{LazyLocation, Location};
use super::{Validate, ValidationContext};
pub struct AnyOfValidator {
schemas: Vec<SchemaNode>,
}
impl AnyOfValidator {
#[must_use]
pub fn new(schemas: Vec<SchemaNode>) -> Self {
Self { schemas }
}
}
impl Validate for AnyOfValidator {
fn is_valid(&self, instance: &Value, ctx: &mut ValidationContext) -> bool {
let mut any_valid = false;
let base_state = ctx.save_evaluation_state();
for schema in &self.schemas {
let state = ctx.save_evaluation_state();
if schema.is_valid(instance, ctx) {
any_valid = true;
} else {
ctx.restore_evaluation_state(&state);
}
}
if !any_valid {
ctx.restore_evaluation_state(&base_state);
}
any_valid
}
fn validate(
&self,
instance: &Value,
instance_path: &LazyLocation<'_>,
ctx: &mut ValidationContext,
) -> Result<(), ValidationError> {
let mut all_errors: Vec<ValidationError> = Vec::new();
let mut any_valid = false;
let base_state = ctx.save_evaluation_state();
for schema in &self.schemas {
let state = ctx.save_evaluation_state();
let result = schema.validate(instance, instance_path, ctx);
if result.is_ok() {
any_valid = true;
} else {
ctx.restore_evaluation_state(&state);
if let Err(e) = result {
all_errors.push(e);
}
}
}
if any_valid {
Ok(())
} else {
ctx.restore_evaluation_state(&base_state);
Err(all_errors.into_iter().next().unwrap_or_else(|| {
ValidationErrorBuilder::new(instance_path.materialize(), Location::new())
.build(ValidationErrorKind::AnyOf)
}))
}
}
fn iter_errors(
&self,
instance: &Value,
instance_path: &LazyLocation<'_>,
ctx: &mut ValidationContext,
) -> ErrorIterator {
let base_state = ctx.save_evaluation_state();
let mut any_valid = false;
for schema in &self.schemas {
let state = ctx.save_evaluation_state();
if schema.is_valid(instance, ctx) {
any_valid = true;
} else {
ctx.restore_evaluation_state(&state);
}
}
if any_valid {
return Box::new(core::iter::empty());
}
ctx.restore_evaluation_state(&base_state);
let err = ValidationErrorBuilder::new(instance_path.materialize(), Location::new())
.build(ValidationErrorKind::AnyOf);
Box::new(core::iter::once(err))
}
}