Skip to main content

proef_core/pack/
schema.rs

1//! JSON Schema for packs: schemars-derived from the serde model **plus** the
2//! step-kind fragments contributed by registered engines (TECH-SPEC ยง6).
3
4use crate::engine::StepKindSpec;
5
6/// The pack JSON Schema with engine payload fragments merged into the step
7/// object's properties. Falls back to the plain derived schema when the
8/// generated shape does not match expectations (never fails).
9pub fn json_schema(kinds: &[StepKindSpec]) -> serde_json::Value {
10    let schema = schemars::schema_for!(super::RawPack);
11    let mut root = serde_json::to_value(&schema).unwrap_or(serde_json::Value::Bool(true));
12
13    if let Some(step_properties) = root
14        .pointer_mut("/$defs/RawStep/properties")
15        .and_then(serde_json::Value::as_object_mut)
16    {
17        for kind in kinds {
18            let fragment: serde_json::Value =
19                serde_json::from_str(kind.schema).unwrap_or(serde_json::Value::Bool(true));
20            step_properties.insert(kind.prefix.to_owned(), fragment);
21        }
22    }
23    root
24}
25
26#[cfg(test)]
27mod tests {
28    use super::*;
29
30    #[test]
31    fn engine_fragments_merge_into_the_step_schema() {
32        let kinds = [StepKindSpec {
33            prefix: "hurl",
34            schema: r#"{ "type": "string" }"#,
35            validate: None,
36            fragments: None,
37            options: None,
38        }];
39        let schema = json_schema(&kinds);
40        assert_eq!(
41            schema.pointer("/$defs/RawStep/properties/hurl/type"),
42            Some(&serde_json::Value::String("string".into()))
43        );
44        // The fixed schema part is still present.
45        assert!(schema.pointer("/$defs/RawMacro/properties/match").is_some());
46    }
47}