use serde_json::Value;
use crate::merge::path::{CanonicalPath, build_path_map};
use crate::merge::sdl::{PropertyType, SdlDocument};
use crate::validation::{
ValidationError, ValidationResult, check_required_paths, check_type_match,
};
pub fn validate_merged(schema: &SdlDocument, merged: &Value) -> ValidationResult {
let mut errors = Vec::new();
errors.extend(check_required_paths(schema, merged));
let path_map = build_path_map(merged, schema);
for (path, def) in &schema.schema.properties {
let value = path_map.get(path).or_else(|| {
CanonicalPath::parse(path)
.ok()
.and_then(|cp| crate::merge::path::resolve_path(merged, &cp))
});
if let Some(val) = value {
if let Some(err) = check_type_match(&def.type_, val, path) {
errors.push(err);
}
if matches!(def.type_, PropertyType::Array)
&& let Some(ref identity) = def.merge.identity
&& let Value::Array(arr) = val
&& let crate::merge::sdl::IdentityRule::Key { key } = identity
{
for (idx, item) in arr.iter().enumerate() {
if let Value::Object(obj) = item
&& !obj.contains_key(key)
{
errors.push(ValidationError::new(
format!(
"merged item at index {} is missing identity key '{}'",
idx, key
),
format!("{}[{}]", path, idx),
));
}
}
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::merge::sdl::SdlDocument;
use serde_json::json;
fn test_sdl() -> SdlDocument {
SdlDocument::from_yaml(
r#"
schema:
version: "1.0"
required:
- id
properties:
id:
type: string
merge:
type: replace
name:
type: string
merge:
type: replace
version:
type: number
merge:
type: atomic
tags:
type: array
merge:
type: ordered_unique
identity:
mode: primitive_value
characters:
type: array
merge:
type: ordered_unique
identity:
mode: key
key: id
"#,
)
.unwrap()
}
#[test]
fn test_validate_merged_valid() {
let merged = json!({
"id": "nap://test/char/luke",
"name": "Luke Skywalker",
"version": 2,
"tags": ["hero", "jedi"],
"characters": [
{"id": "obiwan", "name": "Obi-Wan"}
]
});
assert!(validate_merged(&test_sdl(), &merged).is_ok());
}
#[test]
fn test_validate_merged_missing_required() {
let merged = json!({
"name": "Luke"
});
let result = validate_merged(&test_sdl(), &merged);
assert!(result.is_err());
}
#[test]
fn test_validate_merged_type_mismatch() {
let merged = json!({
"id": "nap://test/char/luke",
"version": "not-a-number"
});
let result = validate_merged(&test_sdl(), &merged);
assert!(result.is_err());
}
}