use std::sync::OnceLock;
use crate::error::{OlError, OL_4210_SCHEMA_MISMATCH};
const MANIFEST_SCHEMA: &str = include_str!("../../schemas/manifest.schema.json");
const MANIFEST_EDITOR: &str = include_str!("../../schemas/manifest-editor.schema.json");
const MANIFEST_TOOL: &str = include_str!("../../schemas/manifest-tool.schema.json");
const MANIFEST_PROVIDER: &str = include_str!("../../schemas/manifest-provider.schema.json");
const MANIFEST_BINDING: &str = include_str!("../../schemas/manifest-binding.schema.json");
const MANIFEST_CAPABILITY: &str = include_str!("../../schemas/manifest-capability.schema.json");
const ENUMS: &str = include_str!("../../schemas/enums.schema.json");
fn validator() -> &'static jsonschema::Validator {
static V: OnceLock<jsonschema::Validator> = OnceLock::new();
V.get_or_init(|| build_validator().expect("manifest schema must compile"))
}
fn build_validator() -> Result<jsonschema::Validator, jsonschema::ValidationError<'static>> {
let resources = [
(
"https://schemas.openlatch.ai/provider/v1/manifest-editor.schema.json",
MANIFEST_EDITOR,
),
(
"https://schemas.openlatch.ai/provider/v1/manifest-tool.schema.json",
MANIFEST_TOOL,
),
(
"https://schemas.openlatch.ai/provider/v1/manifest-provider.schema.json",
MANIFEST_PROVIDER,
),
(
"https://schemas.openlatch.ai/provider/v1/manifest-binding.schema.json",
MANIFEST_BINDING,
),
(
"https://schemas.openlatch.ai/provider/v1/manifest-capability.schema.json",
MANIFEST_CAPABILITY,
),
(
"https://schemas.openlatch.ai/client/v1/enums.schema.json",
ENUMS,
),
];
let parsed: serde_json::Value =
serde_json::from_str(MANIFEST_SCHEMA).expect("manifest.schema.json must parse");
let resource_pairs: Vec<(String, serde_json::Value)> = resources
.iter()
.map(|(uri, raw)| {
let value = serde_json::from_str(raw).expect("vendored manifest sub-schema must parse");
((*uri).to_string(), value)
})
.collect();
jsonschema::options()
.with_resources(
resource_pairs
.into_iter()
.map(|(uri, value)| (uri, jsonschema::Resource::from_contents(value).unwrap())),
)
.build(&parsed)
}
pub fn validate(value: &serde_json::Value) -> Result<(), OlError> {
if let Err(err) = validator().validate(value) {
return Err(OlError::new(
OL_4210_SCHEMA_MISMATCH,
format!("schema mismatch at `{}`: {}", err.instance_path, err),
));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_object_fails_required_fields() {
let v = serde_json::json!({});
let err = validate(&v).unwrap_err();
assert_eq!(err.code.code, "OL-4210");
}
#[test]
fn minimal_valid_manifest_accepted() {
let v = serde_json::json!({
"schema_version": 1,
"editor": {
"slug": "acme",
"display_name": "Acme Security",
"description": "x"
}
});
validate(&v).unwrap();
}
}