use std::fmt;
use std::sync::OnceLock;
use jsonschema::Validator;
use serde_json::Value;
use crate::JSON_RESUME_SCHEMA;
#[derive(Debug, Clone, PartialEq)]
pub struct ValidationError {
pub path: String,
pub message: String,
}
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.path, self.message)
}
}
impl std::error::Error for ValidationError {}
pub fn validate_value(value: &Value) -> Result<(), Vec<ValidationError>> {
let validator = compiled_validator();
if validator.is_valid(value) {
return Ok(());
}
let errors: Vec<ValidationError> = validator
.iter_errors(value)
.map(|err| ValidationError {
path: err.instance_path().as_str().to_owned(),
message: err.to_string(),
})
.collect();
if errors.is_empty() {
return Err(vec![ValidationError {
path: String::new(),
message: "document failed validation but no errors were reported".to_owned(),
}]);
}
Err(errors)
}
fn compiled_validator() -> &'static Validator {
static VALIDATOR: OnceLock<Validator> = OnceLock::new();
VALIDATOR.get_or_init(|| {
let schema: Value = serde_json::from_str(JSON_RESUME_SCHEMA)
.expect("embedded JSON Resume schema must parse as JSON");
jsonschema::draft4::options()
.should_validate_formats(true)
.build(&schema)
.expect("embedded JSON Resume schema must compile as a draft-04 schema")
})
}