polyspec_crudui_generator/
view.rs1use serde_json::Value;
4
5#[derive(Debug, Clone, Default, PartialEq, Eq)]
8pub struct ViewState {
9 pub collapsed: Vec<String>,
11}
12
13fn inside(row_path: &str, path: &str) -> bool {
15 path == row_path
16 || path
17 .strip_prefix(row_path)
18 .is_some_and(|rest| rest.starts_with('.'))
19}
20
21pub fn initial_view() -> ViewState {
23 ViewState::default()
24}
25
26pub fn collapsible_rows(nodes: &[Value]) -> Vec<String> {
28 let mut rows = Vec::new();
29 collect(nodes, &mut rows);
30 rows
31}
32
33fn children(node: &Value) -> &[Value] {
34 node.get("children")
35 .and_then(Value::as_array)
36 .map_or(&[], Vec::as_slice)
37}
38
39fn collect(nodes: &[Value], rows: &mut Vec<String>) {
40 for node in nodes {
41 if node["kind"] != "collection" {
42 collect(children(node), rows);
43 continue;
44 }
45 for row in children(node) {
46 if row["collapsible"] == true {
47 rows.push(format!(
48 "{}.{}",
49 node["path"].as_str().unwrap_or_default(),
50 row["key"].as_str().unwrap_or_default()
51 ));
52 }
53 collect(children(row), rows);
54 }
55 }
56}
57
58pub fn toggle_row_view(view: &ViewState, row_path: &str) -> ViewState {
60 let mut collapsed: Vec<String> = view
61 .collapsed
62 .iter()
63 .filter(|path| *path != row_path)
64 .cloned()
65 .collect();
66 if collapsed.len() == view.collapsed.len() {
67 collapsed.push(row_path.to_owned());
68 }
69 ViewState { collapsed }
70}
71
72pub fn set_all_expanded_view(nodes: &[Value], expanded: bool) -> ViewState {
75 if expanded {
76 initial_view()
77 } else {
78 ViewState {
79 collapsed: collapsible_rows(nodes),
80 }
81 }
82}
83
84pub fn remove_row_view(view: &ViewState, row_path: &str) -> ViewState {
86 ViewState {
87 collapsed: view
88 .collapsed
89 .iter()
90 .filter(|path| !inside(row_path, path))
91 .cloned()
92 .collect(),
93 }
94}
95
96pub fn rekey_row_view(view: &ViewState, old_path: &str, new_path: &str) -> ViewState {
98 let mut collapsed: Vec<String> = Vec::new();
99 for path in &view.collapsed {
100 let moved = if inside(old_path, path) {
101 format!("{new_path}{}", &path[old_path.len()..])
102 } else {
103 path.clone()
104 };
105 if !collapsed.contains(&moved) {
106 collapsed.push(moved);
107 }
108 }
109 ViewState { collapsed }
110}