Skip to main content

comnum_spec/blocks/schema/
json_schema.rs

1//! The JSON Schema of a mapping of parameters and of a block of a link.
2
3use super::{ParamDefault, ParamInfo};
4use crate::blocks::BlockInfo;
5use serde_json::{Map, Value as Json, json};
6use std::borrow::Borrow;
7
8/// The JSON Schema of a mapping of parameters: its `properties` (the schema of every
9/// parameter), the `required` ones, no other key; `fixed` adds properties first (the
10/// `type` of a block or of a variant) and makes them required.
11pub fn object_schema(params: &[ParamInfo], fixed: &[(&str, Json)]) -> Map<String, Json> {
12    let mut props = Map::new();
13    let mut required: Vec<Json> = Vec::new();
14    for (k, v) in fixed {
15        props.insert((*k).into(), v.clone());
16        required.push(json!(k));
17    }
18    for p in params {
19        props.insert(p.name.into(), p.json_schema());
20        if matches!(p.default, ParamDefault::Required) {
21            required.push(json!(p.name));
22        }
23    }
24    let mut s = Map::new();
25    s.insert("type".into(), json!("object"));
26    s.insert("properties".into(), Json::Object(props));
27    s.insert("required".into(), Json::Array(required));
28    s.insert("additionalProperties".into(), json!(false));
29    s
30}
31
32/// The JSON Schema of a mapping with a `type` among `variants`, each with its parameters
33/// (their types, defaults, units, accepted values and bounds), for
34/// [`ParamType::schema`](super::ParamType::schema).
35pub fn variants_schema(variants: &[(&'static str, Vec<ParamInfo>)]) -> Json {
36    let one_of: Vec<Json> = variants
37        .iter()
38        .map(|(kind, params)| {
39            let mut s = object_schema(params, &[("type", json!({"const": kind}))]);
40            s.insert("title".into(), json!(kind));
41            Json::Object(s)
42        })
43        .collect();
44    json!({"type": "object", "oneOf": one_of})
45}
46
47/// The JSON Schema of a block of a link: one alternative per block of the registry (a
48/// mapping with its `type`, an optional `label` and its parameters, or its name alone
49/// when it needs no parameter).
50pub fn json_schema<B: Borrow<BlockInfo>>(registry: &[B]) -> Json {
51    let mut alternatives = Vec::new();
52    for b in registry.iter().map(Borrow::borrow) {
53        let mut s = object_schema(&b.params, &[("type", json!({"const": b.name}))]);
54        if let Some(Json::Object(props)) = s.get_mut("properties") {
55            props.insert("label".into(), json!({"type": "string", "description": "prefix of the recorded values"}));
56        }
57        let only_type = s.get("required").and_then(Json::as_array).is_some_and(|r| r.len() == 1);
58        s.insert("title".into(), json!(b.name));
59        s.insert("description".into(), json!(b.summary));
60        s.insert("x-needs".into(), json!(b.needs.iter().map(|f| f.name()).collect::<Vec<_>>()));
61        s.insert("x-gives".into(), json!(b.gives.iter().map(|f| f.name()).collect::<Vec<_>>()));
62        s.insert("x-starts".into(), json!(b.starts));
63        alternatives.push(Json::Object(s));
64        if only_type {
65            alternatives.push(json!({"const": b.name, "description": b.summary}));
66        }
67    }
68    json!({
69        "$schema": "https://json-schema.org/draft/2020-12/schema",
70        "title": "comnum link block",
71        "oneOf": alternatives,
72    })
73}