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}
36
37impl Schema {
38 pub fn compiled(self) -> &'static JSONSchema {
39 match self {
40 Schema::Control => schema!("control", "control.schema.json"),
41 Schema::Inventory => schema!("inventory", "inventory.schema.json"),
42 Schema::Schedule => schema!("schedule", "schedule.schema.json"),
43 Schema::State => schema!("state", "state.schema.json"),
44 Schema::Manifest => schema!("manifest", "manifest.schema.json"),
45 Schema::Prepare => schema!("prepare", "prepare.schema.json"),
46 Schema::Result => schema!("result", "result.schema.json"),
47 Schema::Config => schema!("_config", "_config.schema.json"),
48 }
49 }
50
51 pub fn validate(self, value: &Value) -> Vec<String> {
54 match self.compiled().validate(value) {
55 Ok(()) => Vec::new(),
56 Err(errors) => errors
57 .map(|e| format!("{}: {}", e.instance_path, e))
58 .collect(),
59 }
60 }
61}