use std::fmt;
#[cfg(feature = "json-schema-validation")]
use serde_json::Value;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ValidationIssue {
pub path: String,
pub message: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ValidationIssues {
pub issues: Vec<ValidationIssue>,
}
impl ValidationIssues {
#[must_use]
pub fn new(issues: Vec<ValidationIssue>) -> Self {
Self { issues }
}
#[must_use]
pub fn message(message: impl Into<String>) -> Self {
Self {
issues: vec![ValidationIssue {
path: String::new(),
message: message.into(),
}],
}
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.issues.is_empty()
}
}
impl fmt::Display for ValidationIssues {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.issues.as_slice() {
[] => f.write_str("validation failed"),
[single] if single.path.is_empty() => f.write_str(&single.message),
[single] => write!(f, "{}: {}", single.path, single.message),
many => {
write!(f, "{} validation issues:", many.len())?;
for issue in many {
if issue.path.is_empty() {
write!(f, " [{}]", issue.message)?;
} else {
write!(f, " [{}: {}]", issue.path, issue.message)?;
}
}
Ok(())
}
}
}
}
impl std::error::Error for ValidationIssues {}
#[cfg(feature = "json-schema-validation")]
#[derive(Debug)]
pub struct Validator {
inner: jsonschema::Validator,
}
#[cfg(feature = "json-schema-validation")]
impl Validator {
pub fn compile(schema: &Value) -> Result<Self, crate::SchemaError> {
let options = if schema.get("$schema").is_some() {
jsonschema::options()
} else {
jsonschema::draft7::options()
};
let inner = options
.build(schema)
.map_err(|error| crate::SchemaError::InvalidSchema {
message: error.to_string(),
})?;
Ok(Self { inner })
}
#[must_use]
pub fn is_valid(&self, value: &Value) -> bool {
self.inner.is_valid(value)
}
#[must_use]
pub fn issues(&self, value: &Value) -> ValidationIssues {
let issues = self
.inner
.iter_errors(value)
.map(|error| ValidationIssue {
path: error.instance_path().to_string(),
message: error.to_string(),
})
.collect();
ValidationIssues::new(issues)
}
pub fn validate(&self, value: &Value) -> Result<(), ValidationIssues> {
let issues = self.issues(value);
if issues.is_empty() {
Ok(())
} else {
Err(issues)
}
}
}