1use std::sync::OnceLock;
6
7use jsonschema::{Draft, JSONSchema};
8use serde_json::Value;
9
10macro_rules! schema {
11 ($name:literal, $path:literal) => {{
12 static CELL: OnceLock<JSONSchema> = OnceLock::new();
13 CELL.get_or_init(|| {
14 let raw = include_str!(concat!("../schemas/", $path));
15 let json: Value = serde_json::from_str(raw)
16 .unwrap_or_else(|e| panic!("schema {}: invalid JSON: {e}", $name));
17 JSONSchema::options()
18 .with_draft(Draft::Draft202012)
19 .compile(&json)
20 .unwrap_or_else(|e| panic!("schema {}: compile failed: {e}", $name))
21 })
22 }};
23}
24
25#[derive(Debug, Clone, Copy)]
26pub enum Schema {
27 Control,
28 Inventory,
29 Schedule,
30 State,
31 Manifest,
32 Prepare,
33 Result,
34 Config,
35 RiskEvent,
36 RiskIndex,
37}
38
39impl Schema {
40 pub fn compiled(self) -> &'static JSONSchema {
41 match self {
42 Schema::Control => schema!("control", "control.schema.json"),
43 Schema::Inventory => schema!("inventory", "inventory.schema.json"),
44 Schema::Schedule => schema!("schedule", "schedule.schema.json"),
45 Schema::State => schema!("state", "state.schema.json"),
46 Schema::Manifest => schema!("manifest", "manifest.schema.json"),
47 Schema::Prepare => schema!("prepare", "prepare.schema.json"),
48 Schema::Result => schema!("result", "result.schema.json"),
49 Schema::Config => schema!("_config", "_config.schema.json"),
50 Schema::RiskEvent => schema!("risk-event", "risk-event.schema.json"),
51 Schema::RiskIndex => schema!("risk-index", "risk-index.schema.json"),
52 }
53 }
54
55 pub fn validate(self, value: &Value) -> Vec<String> {
58 match self.compiled().validate(value) {
59 Ok(()) => Vec::new(),
60 Err(errors) => errors
61 .map(|e| format!("{}: {}", e.instance_path, e))
62 .collect(),
63 }
64 }
65}