Skip to main content

greentic_dev/dev_runner/
schema.rs

1use 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}