greentic_dev/dev_runner/
schema.rs1use serde_json::Value as JsonValue;
2use serde_yaml_bw::Value as YamlValue;
3
4pub struct CompiledSchema {
5 pub validator: jsonschema::Validator,
6 pub schema_id: Option<String>,
7}
8
9pub fn compile_schema(schema_json: &str) -> Result<CompiledSchema, String> {
10 let schema: JsonValue = serde_json::from_str(schema_json)
11 .map_err(|error| format!("invalid schema JSON: {error}"))?;
12 let schema_id = schema
13 .get("$id")
14 .and_then(|value| value.as_str())
15 .map(ToOwned::to_owned);
16 let validator = jsonschema::validator_for(&schema)
17 .map_err(|error| format!("schema did not compile: {error}"))?;
18
19 Ok(CompiledSchema {
20 validator,
21 schema_id,
22 })
23}
24
25pub fn validate_yaml_against_compiled_schema(
26 node: &YamlValue,
27 validator: &jsonschema::Validator,
28) -> Result<(), String> {
29 let node_json = serde_json::to_value(node)
30 .map_err(|error| format!("failed to convert YAML to JSON: {error}"))?;
31 validator
32 .validate(&node_json)
33 .map_err(|error| error.to_string())
34}
35
36pub fn validate_yaml_against_schema(node: &YamlValue, schema_json: &str) -> Result<(), String> {
37 let compiled = compile_schema(schema_json)?;
38 validate_yaml_against_compiled_schema(node, &compiled.validator)
39}
40
41pub fn schema_id_from_json(schema_json: &str) -> Option<String> {
42 let schema: JsonValue = serde_json::from_str(schema_json).ok()?;
43 schema
44 .get("$id")
45 .and_then(|value| value.as_str())
46 .map(|value| value.to_string())
47}
48
49#[cfg(test)]
50mod tests {
51 use super::{schema_id_from_json, validate_yaml_against_schema};
52 use serde_yaml_bw::Value as YamlValue;
53
54 #[test]
55 fn validates_matching_document() {
56 let node: YamlValue = serde_yaml_bw::from_str("name: demo\n").unwrap();
57 let schema = r#"{
58 "type": "object",
59 "required": ["name"],
60 "properties": {
61 "name": { "type": "string" }
62 }
63 }"#;
64
65 assert!(validate_yaml_against_schema(&node, schema).is_ok());
66 }
67
68 #[test]
69 fn rejects_invalid_schema_json() {
70 let node: YamlValue = serde_yaml_bw::from_str("name: demo\n").unwrap();
71 let err = validate_yaml_against_schema(&node, "{").unwrap_err();
72 assert!(err.contains("invalid schema JSON"));
73 }
74
75 #[test]
76 fn extracts_schema_id() {
77 let schema = r#"{"$id":"greentic://schema/demo","type":"object"}"#;
78 assert_eq!(
79 schema_id_from_json(schema).as_deref(),
80 Some("greentic://schema/demo")
81 );
82 assert!(schema_id_from_json("not json").is_none());
83 }
84}