use std::fmt::{self, Display, Formatter};
use serde_json::Value;
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ValidationError {
pub path: String,
pub related_paths: Vec<String>,
pub message: String,
pub rule: Option<String>,
pub expected: Option<Value>,
pub actual: Option<Value>,
pub tags: Vec<String>,
}
impl ValidationError {
#[must_use]
pub fn new(path: impl Into<String>, message: impl Into<String>) -> Self {
Self {
path: path.into(),
related_paths: Vec::new(),
message: message.into(),
rule: None,
expected: None,
actual: None,
tags: Vec::new(),
}
}
#[must_use]
pub fn with_rule(mut self, rule: impl Into<String>) -> Self {
self.rule = Some(rule.into());
self
}
#[must_use]
pub fn with_related_paths<I, S>(mut self, related_paths: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.related_paths = related_paths.into_iter().map(Into::into).collect();
self
}
#[must_use]
pub fn with_expected(mut self, expected: Value) -> Self {
self.expected = Some(expected);
self
}
#[must_use]
pub fn with_actual(mut self, actual: Value) -> Self {
self.actual = Some(actual);
self
}
#[must_use]
pub fn with_tags<I, S>(mut self, tags: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.tags = tags.into_iter().map(Into::into).collect();
self
}
}
impl Display for ValidationError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if self.path.is_empty() {
write!(f, "{}", self.message)
} else {
write!(f, "{}: {}", self.path, self.message)
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ValidationErrors {
errors: Vec<ValidationError>,
}
impl ValidationErrors {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn from_error(error: ValidationError) -> Self {
Self {
errors: vec![error],
}
}
#[must_use]
pub fn from_message(path: impl Into<String>, message: impl Into<String>) -> Self {
Self::from_error(ValidationError::new(path, message))
}
pub fn push(&mut self, error: ValidationError) {
self.errors.push(error);
}
pub fn extend<I>(&mut self, errors: I)
where
I: IntoIterator<Item = ValidationError>,
{
self.errors.extend(errors);
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.errors.is_empty()
}
#[must_use]
pub fn len(&self) -> usize {
self.errors.len()
}
pub fn into_vec(self) -> Vec<ValidationError> {
self.errors
}
pub fn iter(&self) -> impl Iterator<Item = &ValidationError> {
self.errors.iter()
}
}
impl IntoIterator for ValidationErrors {
type Item = ValidationError;
type IntoIter = std::vec::IntoIter<ValidationError>;
fn into_iter(self) -> Self::IntoIter {
self.errors.into_iter()
}
}
impl Display for ValidationErrors {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
for (index, error) in self.errors.iter().enumerate() {
if index > 0 {
writeln!(f)?;
}
write!(f, "- {error}")?;
}
Ok(())
}
}
impl std::error::Error for ValidationErrors {}