json_eval_rs/jsoneval/
layout.rs1use super::JSONEval;
2use crate::jsoneval::path_utils;
3use crate::time_block;
4
5use serde_json::Value;
6use std::mem;
7
8impl JSONEval {
9 pub fn resolve_layout(&mut self, evaluate: bool) -> Result<(), String> {
19 if evaluate {
20 let data_str = serde_json::to_string(&self.data)
22 .map_err(|e| format!("Failed to serialize data: {}", e))?;
23 self.evaluate(&data_str, None, None, None)?;
24 }
25
26 self.resolve_layout_internal();
27 Ok(())
28 }
29
30 fn resolve_layout_internal(&mut self) {
31 time_block!(" resolve_layout_internal()", {
32 let layout_paths = self.layout_paths.clone();
34
35 time_block!(" resolve_layout_elements", {
36 for layout_path in layout_paths.iter() {
37 self.resolve_layout_elements(layout_path);
38 }
39 });
40
41 time_block!(" propagate_parent_conditions", {
43 for layout_path in layout_paths.iter() {
44 self.propagate_parent_conditions(layout_path);
45 }
46 });
47
48 time_block!(" sync_layout_hidden_to_schema", {
51 self.sync_layout_hidden_to_schema(&layout_paths);
52 });
53 });
54 }
55
56 fn resolve_layout_elements(&mut self, layout_elements_path: &str) {
58 let normalized_path = path_utils::normalize_to_json_pointer(layout_elements_path);
60
61 let elements = if let Some(Value::Array(arr)) = self.schema.pointer(&normalized_path) {
64 arr.clone()
65 } else {
66 return;
67 };
68
69 let parent_path = normalized_path
71 .trim_start_matches('/')
72 .replace("/elements", "")
73 .replace('/', ".");
74
75 let mut resolved_elements = Vec::with_capacity(elements.len());
77 for (index, element) in elements.iter().enumerate() {
78 let element_path = if parent_path.is_empty() {
79 format!("elements.{}", index)
80 } else {
81 format!("{}.elements.{}", parent_path, index)
82 };
83 let resolved = self.resolve_element_ref_recursive(element.clone(), &element_path);
84 resolved_elements.push(resolved);
85 }
86
87 if let Some(target) = self.evaluated_schema.pointer_mut(&normalized_path) {
89 *target = Value::Array(resolved_elements);
90 }
91 }
92
93 fn resolve_element_ref_recursive(&self, element: Value, path_context: &str) -> Value {
96 let resolved = self.resolve_element_ref(element);
98
99 if let Value::Object(mut map) = resolved {
101 if !map.contains_key("$parentHide") {
103 map.insert("$parentHide".to_string(), Value::Bool(false));
104 }
105
106 if !map.contains_key("$fullpath") {
108 map.insert("$fullpath".to_string(), Value::String(path_context.to_string()));
109 }
110
111 if !map.contains_key("$path") {
112 let last_segment = path_context.split('.').last().unwrap_or(path_context);
113 map.insert("$path".to_string(), Value::String(last_segment.to_string()));
114 }
115
116 if let Some(Value::Array(elements)) = map.get("elements") {
118 let mut resolved_nested = Vec::with_capacity(elements.len());
119 for (index, nested_element) in elements.iter().enumerate() {
120 let nested_path = format!("{}.elements.{}", path_context, index);
121 resolved_nested.push(self.resolve_element_ref_recursive(nested_element.clone(), &nested_path));
122 }
123 map.insert("elements".to_string(), Value::Array(resolved_nested));
124 }
125
126 return Value::Object(map);
127 }
128
129 resolved
130 }
131
132 fn resolve_element_ref(&self, element: Value) -> Value {
134 match element {
135 Value::Object(mut map) => {
136 if let Some(Value::String(ref_path)) = map.get("$ref").cloned() {
138 let dotted_path = path_utils::pointer_to_dot_notation(&ref_path);
140
141 let last_segment = dotted_path.split('.').last().unwrap_or(&dotted_path);
143
144 map.insert("$fullpath".to_string(), Value::String(dotted_path.clone()));
146 map.insert("$path".to_string(), Value::String(last_segment.to_string()));
147 map.insert("$parentHide".to_string(), Value::Bool(false));
148
149 let normalized_path = if ref_path.starts_with('#') || ref_path.starts_with('/') {
151 path_utils::normalize_to_json_pointer(&ref_path)
152 } else {
153 let schema_pointer = path_utils::dot_notation_to_schema_pointer(&ref_path);
155 let schema_path = path_utils::normalize_to_json_pointer(&schema_pointer);
156
157 if self.evaluated_schema.pointer(&schema_path).is_some() {
159 schema_path
160 } else {
161 format!("/properties/{}", ref_path.replace('.', "/properties/"))
163 }
164 };
165
166 if let Some(referenced_value) = self.evaluated_schema.pointer(&normalized_path) {
168 let resolved = referenced_value.clone();
169
170 if let Value::Object(mut resolved_map) = resolved {
171 map.remove("$ref");
172
173 if let Some(Value::Object(layout_obj)) = resolved_map.remove("$layout") {
175 let mut result = layout_obj.clone();
176
177 for (key, value) in resolved_map {
181 if key != "type" || !result.contains_key("type") {
182 result.insert(key, value);
183 }
184 }
185
186 for (key, value) in map {
188 result.insert(key, value);
189 }
190
191 return Value::Object(result);
192 } else {
193 for (key, value) in map {
195 resolved_map.insert(key, value);
196 }
197
198 return Value::Object(resolved_map);
199 }
200 } else {
201 return resolved;
202 }
203 }
204 }
205
206 Value::Object(map)
207 }
208 _ => element,
209 }
210 }
211
212 fn propagate_parent_conditions(&mut self, layout_elements_path: &str) {
214 let normalized_path = path_utils::normalize_to_json_pointer(layout_elements_path);
215
216 let elements = if let Some(Value::Array(arr)) = self.evaluated_schema.pointer_mut(&normalized_path) {
218 mem::take(arr)
219 } else {
220 return;
221 };
222
223 let mut updated_elements = Vec::with_capacity(elements.len());
225 for element in elements {
226 updated_elements.push(self.apply_parent_conditions(element, false, false));
227 }
228
229 if let Some(target) = self.evaluated_schema.pointer_mut(&normalized_path) {
231 *target = Value::Array(updated_elements);
232 }
233 }
234
235 fn apply_parent_conditions(&self, element: Value, parent_hidden: bool, parent_disabled: bool) -> Value {
237 if let Value::Object(mut map) = element {
238 let mut element_hidden = parent_hidden;
240 let mut element_disabled = parent_disabled;
241
242 if let Some(Value::Object(condition)) = map.get("condition") {
244 if let Some(Value::Bool(hidden)) = condition.get("hidden") {
245 element_hidden = element_hidden || *hidden;
246 }
247 if let Some(Value::Bool(disabled)) = condition.get("disabled") {
248 element_disabled = element_disabled || *disabled;
249 }
250 }
251
252 if let Some(Value::Object(hide_layout)) = map.get("hideLayout") {
254 if let Some(Value::Bool(all)) = hide_layout.get("all") {
255 if *all {
256 element_hidden = true;
257 }
258 }
259 }
260
261 if parent_hidden || parent_disabled {
263 if map.contains_key("condition")
265 || map.contains_key("$ref")
266 || map.contains_key("$fullpath")
267 {
268 let mut condition = if let Some(Value::Object(c)) = map.get("condition") {
269 c.clone()
270 } else {
271 serde_json::Map::new()
272 };
273
274 if parent_hidden {
275 condition.insert("hidden".to_string(), Value::Bool(true));
276 element_hidden = true;
277 }
278 if parent_disabled {
279 condition.insert("disabled".to_string(), Value::Bool(true));
280 element_disabled = true;
281 }
282
283 map.insert("condition".to_string(), Value::Object(condition));
284 }
285
286 if parent_hidden && (map.contains_key("hideLayout") || map.contains_key("type")) {
288 let mut hide_layout = if let Some(Value::Object(h)) = map.get("hideLayout") {
289 h.clone()
290 } else {
291 serde_json::Map::new()
292 };
293
294 hide_layout.insert("all".to_string(), Value::Bool(true));
296 map.insert("hideLayout".to_string(), Value::Object(hide_layout));
297 }
298 }
299
300 if map.contains_key("$parentHide") {
302 map.insert("$parentHide".to_string(), Value::Bool(parent_hidden));
303 }
304
305 if let Some(Value::Array(elements)) = map.get("elements") {
307 let mut updated_children = Vec::with_capacity(elements.len());
308 for child in elements {
309 updated_children.push(self.apply_parent_conditions(child.clone(), element_hidden, element_disabled));
310 }
311 map.insert("elements".to_string(), Value::Array(updated_children));
312 }
313
314 return Value::Object(map);
315 }
316
317 element
318 }
319
320 fn sync_layout_hidden_to_schema(&mut self, layout_paths: &[String]) {
322 let mut hidden_paths = Vec::new();
323
324 for layout_path in layout_paths {
326 let normalized_path = path_utils::normalize_to_json_pointer(layout_path);
327 if let Some(Value::Array(elements)) = self.evaluated_schema.pointer(&normalized_path) {
328 for element in elements {
329 self.collect_hidden_paths_recursive(element, &mut hidden_paths);
330 }
331 }
332 }
333
334 for schema_path_dot in hidden_paths {
336 let schema_pointer = path_utils::dot_notation_to_schema_pointer(&schema_path_dot);
337 let normalized_pointer = path_utils::normalize_to_json_pointer(&schema_pointer);
338
339 if let Some(schema_node) = self.evaluated_schema.pointer_mut(&normalized_pointer) {
340 if let Value::Object(map) = schema_node {
341 let mut condition = if let Some(Value::Object(c)) = map.get("condition") {
343 c.clone()
344 } else {
345 serde_json::Map::new()
346 };
347
348 condition.insert("hidden".to_string(), Value::Bool(true));
349 map.insert("condition".to_string(), Value::Object(condition));
350 }
351 } else {
352 let alt_pointer = format!("/properties{}", normalized_pointer);
354 if let Some(schema_node) = self.evaluated_schema.pointer_mut(&alt_pointer) {
355 if let Value::Object(map) = schema_node {
356 let mut condition = if let Some(Value::Object(c)) = map.get("condition") {
357 c.clone()
358 } else {
359 serde_json::Map::new()
360 };
361
362 condition.insert("hidden".to_string(), Value::Bool(true));
363 map.insert("condition".to_string(), Value::Object(condition));
364 }
365 }
366 }
367 }
368 }
369
370 fn collect_hidden_paths_recursive(&self, element: &Value, hidden_paths: &mut Vec<String>) {
371 if let Value::Object(map) = element {
372 let is_hidden = if let Some(Value::Object(condition)) = map.get("condition") {
374 condition.get("hidden").and_then(|v| v.as_bool()).unwrap_or(false)
375 } else {
376 false
377 };
378
379 if is_hidden {
380 if let Some(Value::String(fullpath)) = map.get("fullpath") {
382 hidden_paths.push(fullpath.clone());
383 } else if let Some(Value::String(fullpath)) = map.get("$fullpath") {
384 hidden_paths.push(fullpath.clone());
385 }
386 }
387
388 if let Some(Value::Array(elements)) = map.get("elements") {
390 for child in elements {
391 self.collect_hidden_paths_recursive(child, hidden_paths);
392 }
393 }
394 }
395 }
396}