Skip to main content

polyspec_crudui_generator/
outline.rs

1//! The structure map model derived from evaluated nodes: one entry per form row.
2
3use serde_json::{Map, Value};
4
5/// The child nodes of `node`, none when it has no `children` list.
6fn children(node: &Value) -> &[Value] {
7    node.get("children")
8        .and_then(Value::as_array)
9        .map_or(&[], Vec::as_slice)
10}
11
12/// The rows under `nodes`, looking through every node that is not a collection.
13fn rows(nodes: &[Value]) -> Vec<Value> {
14    nodes
15        .iter()
16        .flat_map(|node| {
17            if node["kind"] == "collection" {
18                let path = node["path"].as_str().unwrap_or("");
19                children(node).iter().map(|row| entry(path, row)).collect()
20            } else {
21                rows(children(node))
22            }
23        })
24        .collect()
25}
26
27/// The entry of one row of the collection at `path`, with the rows nested in it.
28fn entry(path: &str, row: &Value) -> Value {
29    let mut entry = Map::new();
30    entry.insert("path".into(), path.into());
31    entry.insert("key".into(), row["key"].as_str().unwrap_or("").into());
32    for name in ["number", "title"] {
33        if let Some(text) = row["header"].get(name).filter(|text| !text.is_null()) {
34            entry.insert(name.into(), text.clone());
35        }
36    }
37    if row["controls"]["placement"] == "outline" {
38        entry.insert("controls".into(), row["controls"].clone());
39    }
40    entry.insert("rows".into(), Value::Array(rows(children(row))));
41    Value::Object(entry)
42}
43
44/// Build the structure map: the form's rows, nested as in the form. Collections, counts and empty collections are not rows.
45pub fn build_outline(nodes: &[Value]) -> Vec<Value> {
46    rows(nodes)
47}