use serde_json::Value;
use crate::draft::Draft;
use crate::error::{ValidationError, ValidationErrorBuilder, ValidationErrorKind};
use crate::paths::Location;
use crate::ValidationOptions;
#[must_use]
pub fn meta_schema(draft: Draft) -> Value {
let json = match draft {
Draft::Draft4 => include_str!("../artefacts/draft-04-schema.json"),
Draft::Draft6 => include_str!("../artefacts/draft-06-schema.json"),
Draft::Draft7 => include_str!("../artefacts/draft-07-schema.json"),
Draft::Draft201909 => include_str!("../artefacts/draft-2019-09-schema.json"),
Draft::Draft202012 => include_str!("../artefacts/draft-2020-12-schema.json"),
};
serde_json::from_str(json).expect("embedded meta-schema must be valid JSON")
}
pub fn validate_schema(schema: &Value) -> Result<(), ValidationError> {
let draft = Draft::detect(schema).ok_or_else(|| {
ValidationErrorBuilder::new(Location::new(), Location::new()).build(
ValidationErrorKind::Schema {
reason:
"no $schema keyword found; cannot auto-detect draft for meta-schema validation"
.into(),
},
)
})?;
validate_schema_with_draft(schema, draft)
}
pub fn validate_schema_with_draft(schema: &Value, draft: Draft) -> Result<(), ValidationError> {
let meta = meta_schema(draft);
let validator = ValidationOptions::new().with_draft(draft).build(&meta)?;
if validator.is_valid(schema) {
return Ok(());
}
let mut errors = validator.iter_errors(schema);
if let Some(err) = errors.next() {
return Err(err);
}
Ok(())
}
pub fn is_schema_valid(schema: &Value) -> Result<bool, ValidationError> {
let draft = Draft::detect(schema).ok_or_else(|| {
ValidationErrorBuilder::new(Location::new(), Location::new()).build(
ValidationErrorKind::Schema {
reason: "no $schema keyword found".into(),
},
)
})?;
Ok(is_schema_valid_with_draft(schema, draft))
}
#[must_use]
pub fn is_schema_valid_with_draft(schema: &Value, draft: Draft) -> bool {
let meta = meta_schema(draft);
let Ok(validator) = ValidationOptions::new().with_draft(draft).build(&meta) else {
return false;
};
validator.is_valid(schema)
}
pub mod draft4 {
use super::{Draft, ValidationError, Value};
pub fn validate(schema: &Value) -> Result<(), ValidationError> {
super::validate_schema_with_draft(schema, Draft::Draft4)
}
#[must_use]
pub fn is_valid(schema: &Value) -> bool {
super::is_schema_valid_with_draft(schema, Draft::Draft4)
}
}
pub mod draft6 {
use super::{Draft, ValidationError, Value};
pub fn validate(schema: &Value) -> Result<(), ValidationError> {
super::validate_schema_with_draft(schema, Draft::Draft6)
}
#[must_use]
pub fn is_valid(schema: &Value) -> bool {
super::is_schema_valid_with_draft(schema, Draft::Draft6)
}
}
pub mod draft7 {
use super::{Draft, ValidationError, Value};
pub fn validate(schema: &Value) -> Result<(), ValidationError> {
super::validate_schema_with_draft(schema, Draft::Draft7)
}
#[must_use]
pub fn is_valid(schema: &Value) -> bool {
super::is_schema_valid_with_draft(schema, Draft::Draft7)
}
}
pub mod draft201909 {
use super::{Draft, ValidationError, Value};
pub fn validate(schema: &Value) -> Result<(), ValidationError> {
super::validate_schema_with_draft(schema, Draft::Draft201909)
}
#[must_use]
pub fn is_valid(schema: &Value) -> bool {
super::is_schema_valid_with_draft(schema, Draft::Draft201909)
}
}
pub mod draft202012 {
use super::{Draft, ValidationError, Value};
pub fn validate(schema: &Value) -> Result<(), ValidationError> {
super::validate_schema_with_draft(schema, Draft::Draft202012)
}
#[must_use]
pub fn is_valid(schema: &Value) -> bool {
super::is_schema_valid_with_draft(schema, Draft::Draft202012)
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_meta_schema_embedded() {
let _ = meta_schema(Draft::Draft4);
let _ = meta_schema(Draft::Draft6);
let _ = meta_schema(Draft::Draft7);
let _ = meta_schema(Draft::Draft201909);
let _ = meta_schema(Draft::Draft202012);
}
#[test]
fn test_no_schema_keyword_fails_auto_detect() {
let schema = json!({"type": "string"});
assert!(validate_schema(&schema).is_err());
assert!(is_schema_valid(&schema).is_err());
}
#[test]
fn test_schema_uri_detection_works() {
let s4 = json!({"$schema": "http://json-schema.org/draft-04/schema#"});
assert_eq!(Draft::detect(&s4), Some(Draft::Draft4));
let s7 = json!({"$schema": "http://json-schema.org/draft-07/schema#"});
assert_eq!(Draft::detect(&s7), Some(Draft::Draft7));
let s2020 = json!({"$schema": "https://json-schema.org/draft/2020-12/schema"});
assert_eq!(Draft::detect(&s2020), Some(Draft::Draft202012));
}
}