1use std::fmt;
8
9use schemars::{JsonSchema, Schema, SchemaGenerator};
10use serde::{Deserialize, Serialize};
11
12use crate::hash::{Digest, HashError, canonical_digest};
13
14#[must_use]
16pub fn schema_for<T: JsonSchema>() -> Schema {
17 SchemaGenerator::default().into_root_schema_for::<T>()
18}
19
20#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
22#[serde(transparent)]
23pub struct SchemaFingerprint(pub Digest);
24
25impl SchemaFingerprint {
26 pub fn of<T: JsonSchema>() -> Result<Self, HashError> {
28 Self::of_schema(&schema_for::<T>())
29 }
30
31 pub fn of_schema(schema: &Schema) -> Result<Self, HashError> {
33 canonical_digest(schema).map(Self)
34 }
35
36 #[must_use]
38 pub fn as_str(&self) -> &str {
39 self.0.as_str()
40 }
41}
42
43impl fmt::Display for SchemaFingerprint {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 fmt::Display::fmt(&self.0, f)
46 }
47}
48
49pub fn validate_against(
56 schema: &schemars::Schema,
57 instance: &serde_json::Value,
58) -> Result<(), crate::error::SchemaCheckError> {
59 let validator = jsonschema::validator_for(schema.as_value())
60 .map_err(|_| crate::error::SchemaCheckError::InvalidSchema)?;
61 validator.validate(instance).map_err(|error| {
62 crate::error::SchemaCheckError::Violation(crate::error::SchemaValidationError {
63 instance_path: error.instance_path().to_string(),
64 schema_path: error.schema_path().to_string(),
65 })
66 })
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[derive(JsonSchema)]
74 #[allow(dead_code)]
75 struct A {
76 x: u32,
77 }
78
79 #[derive(JsonSchema)]
80 #[allow(dead_code)]
81 struct B {
82 x: String,
83 }
84
85 #[test]
86 fn fingerprint_distinguishes_schemas_and_is_stable() {
87 let a1 = SchemaFingerprint::of::<A>().unwrap();
88 let a2 = SchemaFingerprint::of::<A>().unwrap();
89 let b = SchemaFingerprint::of::<B>().unwrap();
90 assert_eq!(a1, a2);
91 assert_ne!(a1, b);
92 }
93}