use crate::{
error::ErrorIterator,
keywords::BoxedValidator,
output::{Annotations, ErrorDescription, OutputUnit},
paths::JsonPointerNode,
schema_node::SchemaNode,
};
use serde_json::Value;
use std::{collections::VecDeque, fmt};
pub(crate) trait Validate: Send + Sync + core::fmt::Display {
fn validate<'instance>(
&self,
instance: &'instance Value,
instance_path: &JsonPointerNode,
) -> ErrorIterator<'instance>;
fn is_valid(&self, instance: &Value) -> bool;
fn apply<'a>(
&'a self,
instance: &Value,
instance_path: &JsonPointerNode,
) -> PartialApplication<'a> {
let errors: Vec<ErrorDescription> = self
.validate(instance, instance_path)
.map(ErrorDescription::from)
.collect();
if errors.is_empty() {
PartialApplication::valid_empty()
} else {
PartialApplication::invalid_empty(errors)
}
}
}
#[derive(Clone, PartialEq)]
pub(crate) enum PartialApplication<'a> {
Valid {
annotations: Option<Annotations<'a>>,
child_results: VecDeque<OutputUnit<Annotations<'a>>>,
},
Invalid {
errors: Vec<ErrorDescription>,
child_results: VecDeque<OutputUnit<ErrorDescription>>,
},
}
impl<'a> PartialApplication<'a> {
pub(crate) fn valid_empty() -> PartialApplication<'static> {
PartialApplication::Valid {
annotations: None,
child_results: VecDeque::new(),
}
}
pub(crate) fn invalid_empty(errors: Vec<ErrorDescription>) -> PartialApplication<'static> {
PartialApplication::Invalid {
errors,
child_results: VecDeque::new(),
}
}
#[must_use]
pub(crate) const fn is_valid(&self) -> bool {
match self {
Self::Valid { .. } => true,
Self::Invalid { .. } => false,
}
}
pub(crate) fn annotate(&mut self, new_annotations: Annotations<'a>) {
match self {
Self::Valid { annotations, .. } => *annotations = Some(new_annotations),
Self::Invalid { .. } => {}
}
}
pub(crate) fn mark_errored(&mut self, error: ErrorDescription) {
match self {
Self::Invalid { errors, .. } => errors.push(error),
Self::Valid { .. } => {
*self = Self::Invalid {
errors: vec![error],
child_results: VecDeque::new(),
}
}
}
}
}
impl fmt::Debug for dyn Validate + Send + Sync {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.to_string())
}
}
pub(crate) fn format_validators<'a, I: ExactSizeIterator + Iterator<Item = &'a BoxedValidator>>(
mut validators: I,
) -> String {
match validators.len() {
0 => "{}".to_string(),
1 => {
let validator = validators.next().unwrap();
let name = validator.to_string();
match name.as_str() {
"true" | "false" => name,
_ => format!("{{{}}}", name),
}
}
_ => format!(
"{{{}}}",
validators
.map(|validator| format!("{:?}", validator))
.collect::<Vec<String>>()
.join(", ")
),
}
}
pub(crate) fn format_iter_of_validators<'a, G, I>(validators: I) -> String
where
I: Iterator<Item = G>,
G: ExactSizeIterator + Iterator<Item = &'a BoxedValidator>,
{
validators
.map(format_validators)
.collect::<Vec<String>>()
.join(", ")
}
pub(crate) fn format_key_value_validators(validators: &[(String, SchemaNode)]) -> String {
validators
.iter()
.map(|(name, node)| format!("{}: {}", name, format_validators(node.validators())))
.collect::<Vec<String>>()
.join(", ")
}