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(
109 "$fullpath".to_string(),
110 Value::String(path_context.to_string()),
111 );
112 }
113
114 if !map.contains_key("$path") {
115 let last_segment = path_context.split('.').last().unwrap_or(path_context);
116 map.insert("$path".to_string(), Value::String(last_segment.to_string()));
117 }
118
119 if let Some(Value::Array(elements)) = map.get("elements") {
121 let mut resolved_nested = Vec::with_capacity(elements.len());
122 for (index, nested_element) in elements.iter().enumerate() {
123 let nested_path = format!("{}.elements.{}", path_context, index);
124 resolved_nested.push(
125 self.resolve_element_ref_recursive(nested_element.clone(), &nested_path),
126 );
127 }
128 map.insert("elements".to_string(), Value::Array(resolved_nested));
129 }
130
131 return Value::Object(map);
132 }
133
134 resolved
135 }
136
137 fn resolve_element_ref(&self, element: Value) -> Value {
139 match element {
140 Value::Object(mut map) => {
141 if let Some(Value::String(ref_path)) = map.get("$ref").cloned() {
143 let dotted_path = path_utils::pointer_to_dot_notation(&ref_path);
145
146 let last_segment = dotted_path.split('.').last().unwrap_or(&dotted_path);
148
149 map.insert("$fullpath".to_string(), Value::String(dotted_path.clone()));
151 map.insert("$path".to_string(), Value::String(last_segment.to_string()));
152 map.insert("$parentHide".to_string(), Value::Bool(false));
153
154 let normalized_path = if ref_path.starts_with('#') || ref_path.starts_with('/')
156 {
157 path_utils::normalize_to_json_pointer(&ref_path).into_owned()
158 } else {
159 let schema_pointer = path_utils::dot_notation_to_schema_pointer(&ref_path);
160 let schema_path =
161 path_utils::normalize_to_json_pointer(&schema_pointer).into_owned();
162
163 if self.evaluated_schema.pointer(&schema_path).is_some() {
164 schema_path
165 } else {
166 format!("/properties/{}", ref_path.replace('.', "/properties/"))
167 }
168 };
169
170 if let Some(referenced_value) = self.evaluated_schema.pointer(&normalized_path)
172 {
173 let resolved = referenced_value.clone();
174
175 if let Value::Object(mut resolved_map) = resolved {
176 map.remove("$ref");
177
178 if let Some(Value::Object(layout_obj)) = resolved_map.remove("$layout")
180 {
181 let mut result = layout_obj.clone();
182
183 for (key, value) in resolved_map {
187 if key != "type" || !result.contains_key("type") {
188 result.insert(key, value);
189 }
190 }
191
192 for (key, value) in map {
194 result.insert(key, value);
195 }
196
197 return Value::Object(result);
198 } else {
199 for (key, value) in map {
201 resolved_map.insert(key, value);
202 }
203
204 return Value::Object(resolved_map);
205 }
206 } else {
207 return resolved;
208 }
209 }
210 }
211
212 Value::Object(map)
213 }
214 _ => element,
215 }
216 }
217
218 fn propagate_parent_conditions(&mut self, layout_elements_path: &str) {
220 let normalized_path = path_utils::normalize_to_json_pointer(layout_elements_path);
221
222 let elements =
224 if let Some(Value::Array(arr)) = self.evaluated_schema.pointer_mut(&normalized_path) {
225 mem::take(arr)
226 } else {
227 return;
228 };
229
230 let mut updated_elements = Vec::with_capacity(elements.len());
232 for element in elements {
233 updated_elements.push(self.apply_parent_conditions(element, false, false));
234 }
235
236 if let Some(target) = self.evaluated_schema.pointer_mut(&normalized_path) {
238 *target = Value::Array(updated_elements);
239 }
240 }
241
242 fn apply_parent_conditions(
244 &self,
245 element: Value,
246 parent_hidden: bool,
247 parent_disabled: bool,
248 ) -> Value {
249 if let Value::Object(mut map) = element {
250 let mut element_hidden = parent_hidden;
252 let mut element_disabled = parent_disabled;
253
254 if let Some(Value::Object(condition)) = map.get("condition") {
256 if let Some(Value::Bool(hidden)) = condition.get("hidden") {
257 element_hidden = element_hidden || *hidden;
258 }
259 if let Some(Value::Bool(disabled)) = condition.get("disabled") {
260 element_disabled = element_disabled || *disabled;
261 }
262 }
263
264 if let Some(Value::Object(hide_layout)) = map.get("hideLayout") {
266 if let Some(Value::Bool(all)) = hide_layout.get("all") {
267 if *all {
268 element_hidden = true;
269 }
270 }
271 }
272
273 if parent_hidden || parent_disabled {
275 if map.contains_key("condition")
277 || map.contains_key("$ref")
278 || map.contains_key("$fullpath")
279 {
280 let mut condition = if let Some(Value::Object(c)) = map.get("condition") {
281 c.clone()
282 } else {
283 serde_json::Map::new()
284 };
285
286 if parent_hidden {
287 condition.insert("hidden".to_string(), Value::Bool(true));
288 element_hidden = true;
289 }
290 if parent_disabled {
291 condition.insert("disabled".to_string(), Value::Bool(true));
292 element_disabled = true;
293 }
294
295 map.insert("condition".to_string(), Value::Object(condition));
296 }
297
298 if parent_hidden && (map.contains_key("hideLayout") || map.contains_key("type")) {
300 let mut hide_layout = if let Some(Value::Object(h)) = map.get("hideLayout") {
301 h.clone()
302 } else {
303 serde_json::Map::new()
304 };
305
306 hide_layout.insert("all".to_string(), Value::Bool(true));
308 map.insert("hideLayout".to_string(), Value::Object(hide_layout));
309 }
310 }
311
312 if map.contains_key("$parentHide") {
314 map.insert("$parentHide".to_string(), Value::Bool(parent_hidden));
315 }
316
317 if let Some(Value::Array(elements)) = map.get("elements") {
319 let mut updated_children = Vec::with_capacity(elements.len());
320 for child in elements {
321 updated_children.push(self.apply_parent_conditions(
322 child.clone(),
323 element_hidden,
324 element_disabled,
325 ));
326 }
327 map.insert("elements".to_string(), Value::Array(updated_children));
328 }
329
330 return Value::Object(map);
331 }
332
333 element
334 }
335
336 fn sync_layout_hidden_to_schema(&mut self, layout_paths: &[String]) {
338 let mut hidden_paths = Vec::new();
339
340 for layout_path in layout_paths {
342 let normalized_path = path_utils::normalize_to_json_pointer(layout_path);
343 if let Some(Value::Array(elements)) = self.evaluated_schema.pointer(&normalized_path) {
344 for element in elements {
345 self.collect_hidden_paths_recursive(element, &mut hidden_paths);
346 }
347 }
348 }
349
350 for schema_path_dot in hidden_paths {
352 let schema_pointer = path_utils::dot_notation_to_schema_pointer(&schema_path_dot);
353 let normalized_pointer = path_utils::normalize_to_json_pointer(&schema_pointer);
354
355 if let Some(schema_node) = self.evaluated_schema.pointer_mut(&normalized_pointer) {
356 if let Value::Object(map) = schema_node {
357 let mut condition = if let Some(Value::Object(c)) = map.get("condition") {
359 c.clone()
360 } else {
361 serde_json::Map::new()
362 };
363
364 condition.insert("hidden".to_string(), Value::Bool(true));
365 map.insert("condition".to_string(), Value::Object(condition));
366 }
367 } else {
368 let alt_pointer = format!("/properties{}", normalized_pointer);
370 if let Some(schema_node) = self.evaluated_schema.pointer_mut(&alt_pointer) {
371 if let Value::Object(map) = schema_node {
372 let mut condition = if let Some(Value::Object(c)) = map.get("condition") {
373 c.clone()
374 } else {
375 serde_json::Map::new()
376 };
377
378 condition.insert("hidden".to_string(), Value::Bool(true));
379 map.insert("condition".to_string(), Value::Object(condition));
380 }
381 }
382 }
383 }
384 }
385
386 fn collect_hidden_paths_recursive(&self, element: &Value, hidden_paths: &mut Vec<String>) {
387 if let Value::Object(map) = element {
388 let is_hidden = if let Some(Value::Object(condition)) = map.get("condition") {
390 condition
391 .get("hidden")
392 .and_then(|v| v.as_bool())
393 .unwrap_or(false)
394 } else {
395 false
396 };
397
398 if is_hidden {
399 if let Some(Value::String(fullpath)) = map.get("fullpath") {
401 hidden_paths.push(fullpath.clone());
402 } else if let Some(Value::String(fullpath)) = map.get("$fullpath") {
403 hidden_paths.push(fullpath.clone());
404 }
405 }
406
407 if let Some(Value::Array(elements)) = map.get("elements") {
409 for child in elements {
410 self.collect_hidden_paths_recursive(child, hidden_paths);
411 }
412 }
413 }
414 }
415}