// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2026 ndaal Gesellschaft für Sicherheit in der Informationstechnik mbH & Co KG, Cologne
// SPDX-FileCopyrightText: Author: Pierre Gronau <Pierre.Gronau@ndaal.eu>

//! Compiled JSON-Schema validators for the two SARIF schema versions.
//!
//! Both schemas are vendored from the OASIS `sarif-spec` repository and
//! embedded at compile time so validation works fully offline and
//! deterministically:
//!
//! * `sarif-schema-2.1.0.json` — the OASIS 2.1.0 errata schema (JSON-Schema
//!   draft-04).
//! * `sarif-schema-2.2.0-draft.json` — the OASIS 2.2 working draft (JSON-Schema
//!   2020-12).
//!
//! Each [`jsonschema::Validator`] is compiled lazily on first use and cached
//! for the lifetime of the process.

use std::sync::OnceLock;

use jsonschema::Validator;
use serde_json::Value;

/// Embedded SARIF 2.1.0 schema source.
pub const SARIF_2_1_0_SCHEMA: &str = include_str!("../schemas/sarif-schema-2.1.0.json");
/// Embedded SARIF 2.2 (draft) schema source.
pub const SARIF_2_2_SCHEMA: &str = include_str!("../schemas/sarif-schema-2.2.0-draft.json");

/// Compile a schema source string into a validator.
fn build(source: &str) -> Result<Validator, String> {
    let schema: Value = serde_json::from_str(source).map_err(|e| format!("schema parse: {e}"))?;
    jsonschema::validator_for(&schema).map_err(|e| format!("schema compile: {e}"))
}

/// The compiled SARIF 2.1.0 validator (cached after first call).
///
/// # Errors
///
/// Returns a message if the embedded schema fails to parse or compile —
/// impossible with the vendored schema, but surfaced rather than panicking.
pub fn validator_2_1_0() -> Result<&'static Validator, String> {
    static CELL: OnceLock<Result<Validator, String>> = OnceLock::new();
    CELL.get_or_init(|| build(SARIF_2_1_0_SCHEMA))
        .as_ref()
        .map_err(Clone::clone)
}

/// The compiled SARIF 2.2 (draft) validator (cached after first call).
///
/// # Errors
///
/// Returns a message if the embedded schema fails to parse or compile.
pub fn validator_2_2() -> Result<&'static Validator, String> {
    static CELL: OnceLock<Result<Validator, String>> = OnceLock::new();
    CELL.get_or_init(|| build(SARIF_2_2_SCHEMA))
        .as_ref()
        .map_err(Clone::clone)
}

#[cfg(test)]
mod tests {
    use super::{validator_2_1_0, validator_2_2};

    #[test]
    fn both_schemas_compile() {
        assert!(validator_2_1_0().is_ok(), "2.1.0 schema compiles");
        assert!(validator_2_2().is_ok(), "2.2 draft schema compiles");
    }

    #[test]
    fn minimal_log_validates_against_2_1_0() {
        let v = validator_2_1_0().expect("compiled");
        let doc = serde_json::json!({
            "version": "2.1.0",
            "runs": [ { "tool": { "driver": { "name": "demo" } }, "results": [] } ]
        });
        assert!(v.is_valid(&doc), "minimal 2.1.0 log is valid");
    }
}
