1use super::JSONEval;
2use crate::jsoneval::path_utils;
3use crate::jsoneval::types::{ResolvedLayoutResult, ReturnFormat};
4use crate::time_block;
5use crate::utils::clean_float_noise_scalar;
6use serde_json::Value;
7use std::sync::Arc;
8
9impl JSONEval {
10 pub(crate) fn is_effective_hidden(&self, schema_pointer: &str) -> bool {
13 let schema_pointer = schema_pointer.trim_start_matches('#');
14 if self.layout_hidden_refs.iter().any(|hidden_ref| {
15 schema_pointer == hidden_ref
16 || schema_pointer
17 .strip_prefix(hidden_ref)
18 .is_some_and(|suffix| {
19 suffix.starts_with("/properties/") || suffix.starts_with("/items/")
20 })
21 }) {
22 return true;
23 }
24
25 let mut end = schema_pointer.len();
26
27 loop {
28 let current_path = &schema_pointer[..end];
29
30 if let Some(schema_node) = self.evaluated_schema.pointer(current_path) {
31 if let Value::Object(map) = schema_node {
32 if let Some(Value::Object(condition)) = map.get("condition") {
33 if let Some(Value::Bool(true)) = condition.get("hidden") {
34 return true;
35 }
36 }
37
38 if let Some(Value::Object(layout)) = map.get("$layout") {
39 if let Some(Value::Object(hide_layout)) = layout.get("hideLayout") {
40 if let Some(Value::Bool(true)) = hide_layout.get("all") {
41 return true;
42 }
43 }
44 }
45 }
46 }
47
48 if end == 0 {
49 break;
50 }
51
52 match schema_pointer[..end].rfind('/') {
54 Some(0) | None => {
55 end = 0;
56 }
57 Some(last_slash) => {
58 end = last_slash;
59 let parent = &schema_pointer[..end];
60 if parent.ends_with("/properties") {
61 end -= "/properties".len();
62 } else if parent.ends_with("/items") {
63 end -= "/items".len();
64 }
65 }
66 }
67 }
68
69 false
70 }
71
72 fn is_mapped_in_any_layout(&self, schema_path: &str) -> bool {
75 self.layout_field_refs
76 .contains(schema_path.trim_start_matches('#'))
77 }
78
79 fn prune_hidden_values(&self, data: &mut Value, current_path: &str) {
81 if let Value::Object(map) = data {
82 let mut keys_to_remove = Vec::new();
84
85 for (key, value) in map.iter_mut() {
86 if key == "$params" || key == "$context" {
88 continue;
89 }
90
91 let schema_path = if current_path.is_empty() {
95 format!("/properties/{}", key)
96 } else {
97 format!("{}/properties/{}", current_path, key)
98 };
99
100 if self.is_effective_hidden(&schema_path) {
102 keys_to_remove.push(key.clone());
103 } else {
104 if value.is_object() {
106 self.prune_hidden_values(value, &schema_path);
107 }
108 }
109 }
110
111 for key in keys_to_remove {
113 map.remove(&key);
114 }
115 }
116 }
117
118 fn resolve_static_markers_in_value(&self, schema_output: &mut Value) {
124 for (static_key, array_arc) in self.static_arrays.iter() {
125 let schema_path = if static_key.starts_with("/$table") {
127 &static_key["/$table".len()..] } else {
129 static_key.as_str() };
131
132 if let Some(target_val) = schema_output.pointer_mut(schema_path) {
134 *target_val = (**array_arc).clone();
136 }
137 }
138 }
139
140 pub fn get_evaluated_schema(&mut self) -> Value {
147 time_block!("get_evaluated_schema()", {
148 let mut schema = self.evaluated_schema.clone();
149 self.resolve_static_markers_in_value(&mut schema);
150 schema
151 })
152 }
153
154 pub fn get_resolved_layout(&mut self) -> ResolvedLayoutResult {
157 time_block!("get_resolved_layout()", {
158 if let Some(ref cached) = self.resolved_layout_cache {
160 return cached.as_ref().clone();
161 }
162 let result = match self.resolve_layout(false) {
164 Ok(entries) => entries,
165 Err(e) => {
166 eprintln!("Warning: Layout resolution failed: {}", e);
167 Vec::new()
168 }
169 };
170 self.resolved_layout_cache = Some(Arc::new(result.clone()));
171 result
172 })
173 }
174
175 pub fn get_evaluated_schema_resolved(&mut self) -> Value {
184 time_block!("get_evaluated_schema_resolved()", {
185 let mut schema = self.get_evaluated_schema_without_params();
186 let overlays = self.get_resolved_layout();
187
188 struct ResolveEntry {
189 layout_path: String,
190 element_idx: usize,
191 overlay: indexmap::IndexMap<String, Value>,
192 }
193
194 let mut entries: Vec<ResolveEntry> = overlays
195 .iter()
196 .map(|entry| {
197 let layout_path =
198 path_utils::normalize_to_json_pointer(&entry.layout_path).into_owned();
199 ResolveEntry {
200 layout_path,
201 element_idx: entry.element_idx,
202 overlay: entry.overlay.clone(),
203 }
204 })
205 .collect();
206 drop(overlays);
207
208 entries.sort_by(|a, b| {
212 let depth_a = a.layout_path.matches('/').count();
213 let depth_b = b.layout_path.matches('/').count();
214 depth_a
215 .cmp(&depth_b)
216 .then_with(|| a.element_idx.cmp(&b.element_idx))
217 });
218
219 for entry in entries {
223 let resolved_value: Option<Value> = (|| -> Option<Value> {
226 let arr = schema.pointer(&entry.layout_path)?.as_array()?;
227 let element = arr.get(entry.element_idx)?;
228 let ref_str = element.get("$ref")?.as_str()?;
229
230 let ref_pointer = if ref_str.starts_with('#') || ref_str.starts_with('/') {
231 path_utils::normalize_to_json_pointer(ref_str).into_owned()
232 } else {
233 let schema_pointer = path_utils::dot_notation_to_schema_pointer(ref_str);
234 let normalized =
235 path_utils::normalize_to_json_pointer(&schema_pointer).into_owned();
236 if schema.pointer(&normalized).is_some() {
237 normalized
238 } else {
239 format!("/properties/{}", ref_str.replace('.', "/properties/"))
240 }
241 };
242
243 let mut resolved = schema.pointer(&ref_pointer)?.clone();
244
245 if let Value::Object(ref mut resolved_map) = resolved {
247 if let Some(Value::Object(layout_obj)) = resolved_map.remove("$layout") {
248 let mut result = layout_obj;
249 for (key, value) in resolved_map.clone().into_iter() {
250 if key != "type" || !result.contains_key("type") {
251 result.insert(key, value);
252 }
253 }
254 resolved = Value::Object(result);
255 }
256 }
257
258 Some(resolved)
259 })();
260
261 if let Some(Value::Array(arr)) = schema.pointer_mut(&entry.layout_path) {
262 if entry.element_idx < arr.len() {
263 let element = &mut arr[entry.element_idx];
264
265 if let Some(resolved) = resolved_value {
267 if let Value::Object(mut resolved_map) = resolved {
268 if let Value::Object(mut map) = element.take() {
269 map.remove("$ref");
270 for (key, value) in map {
271 resolved_map.insert(key, value);
272 }
273 }
274 *element = Value::Object(resolved_map);
275 } else {
276 *element = resolved;
277 }
278 }
279
280 if let Value::Object(ref mut map) = element {
282 for (k, v) in &entry.overlay {
283 map.insert(k.clone(), v.clone());
284 }
285 }
286 }
287 }
288 }
289
290 Self::stamp_property_metadata(&mut schema);
291 schema
292 })
293 }
294
295 fn stamp_property_metadata(schema: &mut Value) {
297 fn walk(value: &mut Value, path: &str, parent_hidden: bool) {
298 let Some(map) = value.as_object_mut() else {
299 return;
300 };
301
302 let hidden = parent_hidden
303 || map
304 .get("condition")
305 .and_then(Value::as_object)
306 .and_then(|condition| condition.get("hidden"))
307 .is_some_and(|hidden| hidden == &Value::Bool(true));
308
309 if let Some(Value::Object(properties)) = map.get_mut("properties") {
310 for (name, property) in properties {
311 let property_path = if path.is_empty() {
312 format!("properties.{}", name)
313 } else {
314 format!("{}.properties.{}", path, name)
315 };
316 if let Value::Object(property_map) = property {
317 property_map.insert(
318 "$fullpath".to_string(),
319 Value::String(property_path.clone()),
320 );
321 property_map.insert("$path".to_string(), Value::String(name.clone()));
322 property_map.insert("$parentHide".to_string(), Value::Bool(hidden));
323 }
324 walk(property, &property_path, hidden);
325 }
326 }
327
328 for (name, child) in map {
329 if name != "properties" && !name.starts_with('$') && child.is_object() {
330 let child_path = if path.is_empty() {
331 name.clone()
332 } else {
333 format!("{}.{}", path, name)
334 };
335 walk(child, &child_path, hidden);
336 }
337 }
338 }
339
340 walk(schema, "", false);
341 }
342
343 fn resolve_static_markers_at_path(&self, schema_prefix: &str) -> Option<Value> {
354 let mut subtree = self.evaluated_schema.pointer(schema_prefix)?.clone();
355
356 let prefix_slash = format!("{}/", schema_prefix);
358
359 for (static_key, array_arc) in self.static_arrays.iter() {
360 let schema_path: &str = if static_key.starts_with("/$table") {
362 &static_key["/$table".len()..]
363 } else {
364 static_key.as_str()
365 };
366
367 let relative: &str = if schema_path == schema_prefix {
369 ""
371 } else if schema_path.starts_with(&prefix_slash) {
372 &schema_path[schema_prefix.len()..]
374 } else {
375 continue; };
377
378 if relative.is_empty() {
379 subtree = (**array_arc).clone();
380 } else if let Some(target) = subtree.pointer_mut(relative) {
381 *target = (**array_arc).clone();
382 }
383 }
384
385 Some(subtree)
386 }
387
388 pub fn get_schema_value_by_path(&self, path: &str) -> Option<Value> {
391 let pointer_path = path_utils::dot_notation_to_schema_pointer(path);
392 self.resolve_static_markers_at_path(pointer_path.trim_start_matches('#'))
393 }
394
395 pub fn get_schema_value(&mut self) -> Value {
401 let mut current_data = self.eval_data.data().clone();
403
404 if !current_data.is_object() {
406 current_data = Value::Object(serde_json::Map::new());
407 }
408
409 if let Some(obj) = current_data.as_object_mut() {
411 obj.remove("$params");
412 obj.remove("$context");
413 }
414
415 self.prune_hidden_values(&mut current_data, "");
417
418 for eval_key in self.value_evaluations.iter() {
421 let clean_key = eval_key.strip_prefix('#').unwrap_or(eval_key);
422
423 if clean_key.starts_with("/$params")
425 || (clean_key.ends_with("/value")
426 && (clean_key.contains("/rules/") || clean_key.contains("/options/")))
427 {
428 continue;
429 }
430
431 let path = clean_key.replace("/properties", "").replace("/value", "");
432
433 let schema_path = clean_key.strip_suffix("/value").unwrap_or(&clean_key);
436 if self.is_effective_hidden(schema_path) {
437 continue;
438 }
439
440 let value = match self.resolve_static_markers_at_path(clean_key) {
442 Some(v) => v,
443 None => continue,
444 };
445
446 let path_parts: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
448
449 if path_parts.is_empty() {
450 continue;
451 }
452
453 let mut current = &mut current_data;
455 for (i, part) in path_parts.iter().enumerate() {
456 let is_last = i == path_parts.len() - 1;
457
458 if is_last {
459 let schema_value = self.schema.pointer(clean_key);
462 let computed_value = schema_value
463 .and_then(Value::as_object)
464 .is_some_and(|value| value.contains_key("$evaluation"));
465 let disabled = self
466 .evaluated_schema
467 .pointer(schema_path)
468 .and_then(Value::as_object)
469 .and_then(|field| field.get("condition"))
470 .and_then(Value::as_object)
471 .and_then(|condition| condition.get("disabled"))
472 .is_some_and(|disabled| disabled == &Value::Bool(true));
473 let computed_disabled =
474 computed_value && (disabled || !self.is_mapped_in_any_layout(schema_path));
475 if let Some(obj) = current.as_object_mut() {
476 let should_update = computed_disabled
477 || match obj.get(*part) {
478 Some(v) => v.is_null(),
479 None => true,
480 };
481 if should_update {
482 obj.insert(
483 (*part).to_string(),
484 crate::utils::clean_float_noise(value.clone()),
485 );
486 }
487 }
488 } else {
489 if let Some(obj) = current.as_object_mut() {
491 if !obj.contains_key(*part) {
492 obj.insert((*part).to_string(), Value::Object(serde_json::Map::new()));
493 }
494
495 current = obj.get_mut(*part).unwrap();
496 } else {
497 break;
499 }
500 }
501 }
502 }
503
504 crate::utils::clean_float_noise(current_data)
505 }
506
507 pub fn get_schema_value_array(&self) -> Value {
514 let mut result = Vec::new();
515
516 for eval_key in self.value_evaluations.iter() {
517 let clean_key = eval_key.strip_prefix('#').unwrap_or(eval_key);
518
519 if clean_key.starts_with("/$params")
521 || (clean_key.ends_with("/value")
522 && (clean_key.contains("/rules/") || clean_key.contains("/options/")))
523 {
524 continue;
525 }
526
527 let schema_path = clean_key.strip_suffix("/value").unwrap_or(&clean_key);
529 if self.is_effective_hidden(schema_path) {
530 continue;
531 }
532
533 let dotted_path = clean_key
535 .replace("/properties", "")
536 .replace("/value", "")
537 .trim_start_matches('/')
538 .replace('/', ".");
539
540 if dotted_path.is_empty() {
541 continue;
542 }
543
544 let value = match self.resolve_static_markers_at_path(clean_key) {
546 Some(v) => crate::utils::clean_float_noise(v),
547 None => continue,
548 };
549
550 let mut item = serde_json::Map::new();
552 item.insert("path".to_string(), Value::String(dotted_path));
553 item.insert("value".to_string(), value);
554 result.push(Value::Object(item));
555 }
556
557 Value::Array(result)
558 }
559
560 pub fn get_schema_value_object(&self) -> Value {
567 let mut result = serde_json::Map::new();
568
569 for eval_key in self.value_evaluations.iter() {
570 let clean_key = eval_key.strip_prefix('#').unwrap_or(eval_key);
571
572 if clean_key.starts_with("/$params")
574 || (clean_key.ends_with("/value")
575 && (clean_key.contains("/rules/") || clean_key.contains("/options/")))
576 {
577 continue;
578 }
579
580 let schema_path = clean_key.strip_suffix("/value").unwrap_or(&clean_key);
582 if self.is_effective_hidden(schema_path) {
583 continue;
584 }
585
586 let dotted_path = clean_key
588 .replace("/properties", "")
589 .replace("/value", "")
590 .trim_start_matches('/')
591 .replace('/', ".");
592
593 if dotted_path.is_empty() {
594 continue;
595 }
596
597 let value = match self.resolve_static_markers_at_path(clean_key) {
599 Some(v) => crate::utils::clean_float_noise(v),
600 None => continue,
601 };
602
603 result.insert(dotted_path, value);
604 }
605
606 Value::Object(result)
607 }
608
609 pub fn get_evaluated_schema_without_params(&mut self) -> Value {
611 let mut schema = self.get_evaluated_schema();
612 if let Value::Object(ref mut map) = schema {
613 map.remove("$params");
614 }
615 schema
616 }
617
618 pub fn get_evaluated_schema_msgpack(&mut self) -> Result<Vec<u8>, String> {
620 let schema = self.get_evaluated_schema();
621 rmp_serde::to_vec(&schema).map_err(|e| format!("MessagePack serialization failed: {}", e))
622 }
623
624 pub fn get_evaluated_schema_resolved_msgpack(&mut self) -> Result<Vec<u8>, String> {
629 let schema = self.get_evaluated_schema_resolved();
630 rmp_serde::to_vec(&schema).map_err(|e| format!("MessagePack serialization failed: {}", e))
631 }
632
633 pub fn get_evaluated_schema_by_path(&mut self, path: &str) -> Option<Value> {
635 self.get_schema_value_by_path(path)
636 }
637
638 pub fn get_evaluated_schema_by_paths(
640 &mut self,
641 paths: &[String],
642 format: Option<ReturnFormat>,
643 ) -> Value {
644 match format.unwrap_or(ReturnFormat::Nested) {
645 ReturnFormat::Nested => {
646 let mut result = Value::Object(serde_json::Map::new());
647 for path in paths {
648 if let Some(val) = self.get_schema_value_by_path(path) {
649 Self::insert_at_path(&mut result, path, val);
651 }
652 }
653 result
654 }
655 ReturnFormat::Flat => {
656 let mut result = serde_json::Map::new();
657 for path in paths {
658 if let Some(val) = self.get_schema_value_by_path(path) {
659 result.insert(path.clone(), val);
660 }
661 }
662 Value::Object(result)
663 }
664 ReturnFormat::Array => {
665 let mut result = Vec::new();
666 for path in paths {
667 if let Some(val) = self.get_schema_value_by_path(path) {
668 result.push(val);
669 } else {
670 result.push(Value::Null);
671 }
672 }
673 Value::Array(result)
674 }
675 }
676 }
677
678 pub fn get_schema_by_path(&self, path: &str) -> Option<Value> {
680 let pointer_path = path_utils::dot_notation_to_schema_pointer(path);
681 self.schema
682 .pointer(&pointer_path.trim_start_matches('#'))
683 .cloned()
684 }
685
686 pub fn get_schema_by_paths(&self, paths: &[String], format: Option<ReturnFormat>) -> Value {
688 match format.unwrap_or(ReturnFormat::Nested) {
689 ReturnFormat::Nested => {
690 let mut result = Value::Object(serde_json::Map::new());
691 for path in paths {
692 if let Some(val) = self.get_schema_by_path(path) {
693 Self::insert_at_path(&mut result, path, val);
694 }
695 }
696 result
697 }
698 ReturnFormat::Flat => {
699 let mut result = serde_json::Map::new();
700 for path in paths {
701 if let Some(val) = self.get_schema_by_path(path) {
702 result.insert(path.clone(), val);
703 }
704 }
705 Value::Object(result)
706 }
707 ReturnFormat::Array => {
708 let mut result = Vec::new();
709 for path in paths {
710 if let Some(val) = self.get_schema_by_path(path) {
711 result.push(val);
712 } else {
713 result.push(Value::Null);
714 }
715 }
716 Value::Array(result)
717 }
718 }
719 }
720
721 pub(crate) fn insert_at_path(root: &mut Value, path: &str, value: Value) {
723 let parts: Vec<&str> = path.split('.').collect();
724 let mut current = root;
725
726 for (i, part) in parts.iter().enumerate() {
727 if i == parts.len() - 1 {
728 if let Value::Object(map) = current {
730 map.insert(part.to_string(), value);
731 return; }
733 } else {
734 if !current.is_object() {
739 *current = Value::Object(serde_json::Map::new());
740 }
741
742 if let Value::Object(map) = current {
743 if !map.contains_key(*part) {
744 map.insert(part.to_string(), Value::Object(serde_json::Map::new()));
745 }
746 current = map.get_mut(*part).unwrap();
747 }
748 }
749 }
750 }
751
752 pub fn flatten_object(
754 prefix: &str,
755 value: &Value,
756 result: &mut serde_json::Map<String, Value>,
757 ) {
758 match value {
759 Value::Object(map) => {
760 for (k, v) in map {
761 let new_key = if prefix.is_empty() {
762 k.clone()
763 } else {
764 format!("{}.{}", prefix, k)
765 };
766 Self::flatten_object(&new_key, v, result);
767 }
768 }
769 _ => {
770 result.insert(prefix.to_string(), value.clone());
771 }
772 }
773 }
774
775 pub fn convert_to_format(value: Value, format: ReturnFormat) -> Value {
776 match format {
777 ReturnFormat::Nested => value,
778 ReturnFormat::Flat => {
779 let mut result = serde_json::Map::new();
780 Self::flatten_object("", &value, &mut result);
781 Value::Object(result)
782 }
783 ReturnFormat::Array => {
784 if let Value::Object(map) = value {
785 Value::Array(map.values().cloned().collect())
786 } else if let Value::Array(arr) = value {
787 Value::Array(arr)
788 } else {
789 Value::Array(vec![value])
790 }
791 }
792 }
793 }
794
795 pub fn get_field_options(&mut self, field_path: &str) -> Option<Value> {
804 let schema_ptr = if field_path.starts_with('#') || field_path.starts_with('/') {
806 path_utils::normalize_to_json_pointer(field_path).into_owned()
807 } else {
808 path_utils::dot_notation_to_schema_pointer(field_path)
809 };
810
811 let options_schema_key = format!("{}/options", schema_ptr);
813 let options_pointer =
814 path_utils::normalize_to_json_pointer(&options_schema_key).into_owned();
815
816 let options_node = self.evaluated_schema.pointer(&options_pointer)?.clone();
818
819 if let Value::Object(ref map) = options_node {
821 if map.contains_key("$evaluation") {
822 let eval_key = options_schema_key.clone();
823
824 if let Some(logic_id) = self.evaluations.get(&eval_key).copied() {
825 let snap = self.eval_data.snapshot_data();
826 if let Ok(result) = self.engine.run(&logic_id, &*snap) {
827 let cleaned = clean_float_noise_scalar(result);
828 if let Some(node) = self.evaluated_schema.pointer_mut(&options_pointer) {
829 *node = cleaned.clone();
830 }
831 return Some(cleaned);
832 }
833 }
834 return None;
836 }
837 }
838
839 let url_pointer =
841 path_utils::normalize_to_json_pointer(&format!("{}/options/url", schema_ptr))
842 .into_owned();
843
844 let templates = self.options_templates.clone();
845 for (tmpl_url_path, tmpl_str, tmpl_params_path) in templates.iter() {
846 if *tmpl_url_path == url_pointer {
847 if let Some(params) = self.evaluated_schema.pointer(tmpl_params_path) {
848 let params = params.clone();
849 if let Ok(resolved_url) = self.evaluate_template(tmpl_str, ¶ms) {
850 if let Some(target) = self.evaluated_schema.pointer_mut(&url_pointer) {
851 *target = Value::String(resolved_url);
852 }
853 return self.evaluated_schema.pointer(&options_pointer).cloned();
854 }
855 }
856 break;
857 }
858 }
859
860 Some(options_node)
862 }
863}