Skip to main content

agent_rules_tool/
schema.rs

1//! JSON Schema validation of rule frontmatter against the embedded schema.
2
3use crate::error::Error;
4use crate::spec::{EMBEDDED_SCHEMA, SCHEMA_URL};
5use crate::{Severity, Violation};
6use jsonschema::Validator;
7use serde_json::Value;
8use std::sync::LazyLock;
9
10static VALIDATOR: LazyLock<Validator> = LazyLock::new(|| {
11    let schema: Value =
12        serde_json::from_str(EMBEDDED_SCHEMA).expect("embedded schema is valid JSON");
13    Validator::new(&schema).expect("embedded schema compiles")
14});
15
16/// Validate `frontmatter` against the embedded agent-rules-spec JSON Schema.
17///
18/// Returns an empty vector for null or empty-object frontmatter.
19pub fn validate_frontmatter(frontmatter: &Value) -> Result<Vec<Violation>, Error> {
20    if matches!(frontmatter, Value::Null) {
21        return Ok(Vec::new());
22    }
23    if let Value::Object(map) = frontmatter
24        && map.is_empty()
25    {
26        return Ok(Vec::new());
27    }
28
29    let violations: Vec<Violation> = VALIDATOR
30        .iter_errors(frontmatter)
31        .map(|err| {
32            let instance_path = err.instance_path().to_string();
33            let field = if instance_path.is_empty() {
34                "frontmatter".to_string()
35            } else {
36                instance_path.trim_start_matches('/').to_string()
37            };
38            Violation {
39                severity: Severity::Error,
40                field,
41                message: err.to_string(),
42                spec_ref: SCHEMA_URL,
43            }
44        })
45        .collect();
46    Ok(violations)
47}