vdev/commands/build/component_docs/
schema_core.rs

1use super::{SchemaContext, get_schema_ref, nested_merge};
2use anyhow::{Result, anyhow};
3use serde_json::Value;
4
5impl SchemaContext {
6    pub fn get_json_schema_instance_type<'a>(&self, schema: &'a Value) -> Option<&'a str> {
7        let maybe_type = schema.get("type")?;
8
9        if maybe_type.is_null() || maybe_type.as_str() == Some("null") {
10            return None;
11        }
12
13        if let Value::Array(arr) = maybe_type {
14            let filtered: Vec<_> = arr.iter().filter(|v| v.as_str() != Some("null")).collect();
15            if filtered.len() == 1 {
16                return filtered[0].as_str();
17            }
18        } else if let Some(s) = maybe_type.as_str() {
19            return Some(s);
20        }
21
22        None
23    }
24
25    pub fn get_json_schema_type<'a>(&self, schema: &'a Value) -> Option<&'a str> {
26        if schema.get("allOf").is_some() {
27            Some("all-of")
28        } else if schema.get("oneOf").is_some() {
29            Some("one-of")
30        } else if schema.get("anyOf").is_some() {
31            Some("any-of")
32        } else if schema.get("type").is_some() {
33            self.get_json_schema_instance_type(schema)
34        } else if schema.get("const").is_some() {
35            Some("const")
36        } else if schema.get("enum").is_some() {
37            Some("enum")
38        } else {
39            None
40        }
41    }
42
43    pub fn get_schema_by_name(&self, schema_name: &str) -> Result<Value> {
44        let name = schema_name.replace("#/definitions/", "");
45        let def = self
46            .root_schema
47            .get("definitions")
48            .and_then(|defs| defs.get(&name))
49            .cloned();
50
51        if let Some(d) = def {
52            Ok(d)
53        } else {
54            Err(anyhow!(
55                "Could not find schema definition '{name}' in given schema."
56            ))
57        }
58    }
59
60    pub fn expand_schema_references(&mut self, unexpanded_schema: &Value) -> Result<Value> {
61        let mut schema = unexpanded_schema.clone();
62
63        let original_title = schema.get("title").cloned();
64        let original_description = schema.get("description").cloned();
65
66        let schema_ref = get_schema_ref(&schema).map(String::from);
67        if let Some(r) = schema_ref {
68            let expanded_ref = if let Some(cached) = self.expanded_schema_cache.get(&r) {
69                cached.clone()
70            } else {
71                debug!("Expanding top-level schema ref of '{}'...", r);
72                let unexpanded = self.get_schema_by_name(&r)?;
73                let expanded = self.expand_schema_references(&unexpanded)?;
74                self.expanded_schema_cache
75                    .insert(r.clone(), expanded.clone());
76                expanded
77            };
78
79            let obj = schema.as_object_mut().unwrap();
80            obj.shift_remove("$ref");
81
82            let mut new_schema = expanded_ref.clone();
83            nested_merge(&mut new_schema, &Value::Object(obj.clone()));
84            schema = new_schema;
85        }
86
87        if let Some(items) = schema.get("items").cloned()
88            && items.get("$ref").is_some()
89        {
90            let expanded_items = self.expand_schema_references(&items)?;
91            let items_mut = schema.get_mut("items").unwrap().as_object_mut().unwrap();
92            items_mut.shift_remove("$ref");
93
94            let mut new_items = expanded_items;
95            nested_merge(&mut new_items, &Value::Object(items_mut.clone()));
96            *schema.get_mut("items").unwrap() = new_items;
97        }
98
99        if let Some(Value::Object(properties)) = schema.get_mut("properties") {
100            for (_, prop_schema) in properties.iter_mut() {
101                *prop_schema = self.expand_schema_references(&prop_schema.clone())?;
102            }
103        }
104
105        for key in &["allOf", "oneOf", "anyOf"] {
106            if let Some(Value::Array(arr)) = schema.get_mut(*key) {
107                let mut new_arr = Vec::new();
108                for subschema in arr.iter() {
109                    new_arr.push(self.expand_schema_references(subschema)?);
110                }
111                *arr = new_arr;
112            }
113        }
114
115        if original_title.is_some() || original_description.is_some() {
116            let obj = schema.as_object_mut().unwrap();
117            if let Some(t) = original_title {
118                obj.insert("title".to_string(), t);
119            } else {
120                obj.shift_remove("title");
121            }
122            if let Some(d) = original_description {
123                obj.insert("description".to_string(), d);
124            } else {
125                obj.shift_remove("description");
126            }
127        }
128
129        Ok(schema)
130    }
131}