use std::fmt;
use schemars::{JsonSchema, Schema, SchemaGenerator};
use serde::{Deserialize, Serialize};
use crate::hash::{Digest, HashError, canonical_digest};
#[must_use]
pub fn schema_for<T: JsonSchema>() -> Schema {
SchemaGenerator::default().into_root_schema_for::<T>()
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct SchemaFingerprint(pub Digest);
impl SchemaFingerprint {
pub fn of<T: JsonSchema>() -> Result<Self, HashError> {
Self::of_schema(&schema_for::<T>())
}
pub fn of_schema(schema: &Schema) -> Result<Self, HashError> {
canonical_digest(schema).map(Self)
}
#[must_use]
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
impl fmt::Display for SchemaFingerprint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
pub fn validate_against(
schema: &schemars::Schema,
instance: &serde_json::Value,
) -> Result<(), crate::error::SchemaCheckError> {
let validator = jsonschema::validator_for(schema.as_value())
.map_err(|_| crate::error::SchemaCheckError::InvalidSchema)?;
validator.validate(instance).map_err(|error| {
crate::error::SchemaCheckError::Violation(crate::error::SchemaValidationError {
instance_path: error.instance_path().to_string(),
schema_path: error.schema_path().to_string(),
})
})
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(JsonSchema)]
#[allow(dead_code)]
struct A {
x: u32,
}
#[derive(JsonSchema)]
#[allow(dead_code)]
struct B {
x: String,
}
#[test]
fn fingerprint_distinguishes_schemas_and_is_stable() {
let a1 = SchemaFingerprint::of::<A>().unwrap();
let a2 = SchemaFingerprint::of::<A>().unwrap();
let b = SchemaFingerprint::of::<B>().unwrap();
assert_eq!(a1, a2);
assert_ne!(a1, b);
}
}