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_by_path(&mut self, path: &str) -> Option<Value> {
626 self.get_schema_value_by_path(path)
627 }
628
629 pub fn get_evaluated_schema_by_paths(
631 &mut self,
632 paths: &[String],
633 format: Option<ReturnFormat>,
634 ) -> Value {
635 match format.unwrap_or(ReturnFormat::Nested) {
636 ReturnFormat::Nested => {
637 let mut result = Value::Object(serde_json::Map::new());
638 for path in paths {
639 if let Some(val) = self.get_schema_value_by_path(path) {
640 Self::insert_at_path(&mut result, path, val);
642 }
643 }
644 result
645 }
646 ReturnFormat::Flat => {
647 let mut result = serde_json::Map::new();
648 for path in paths {
649 if let Some(val) = self.get_schema_value_by_path(path) {
650 result.insert(path.clone(), val);
651 }
652 }
653 Value::Object(result)
654 }
655 ReturnFormat::Array => {
656 let mut result = Vec::new();
657 for path in paths {
658 if let Some(val) = self.get_schema_value_by_path(path) {
659 result.push(val);
660 } else {
661 result.push(Value::Null);
662 }
663 }
664 Value::Array(result)
665 }
666 }
667 }
668
669 pub fn get_schema_by_path(&self, path: &str) -> Option<Value> {
671 let pointer_path = path_utils::dot_notation_to_schema_pointer(path);
672 self.schema
673 .pointer(&pointer_path.trim_start_matches('#'))
674 .cloned()
675 }
676
677 pub fn get_schema_by_paths(&self, paths: &[String], format: Option<ReturnFormat>) -> Value {
679 match format.unwrap_or(ReturnFormat::Nested) {
680 ReturnFormat::Nested => {
681 let mut result = Value::Object(serde_json::Map::new());
682 for path in paths {
683 if let Some(val) = self.get_schema_by_path(path) {
684 Self::insert_at_path(&mut result, path, val);
685 }
686 }
687 result
688 }
689 ReturnFormat::Flat => {
690 let mut result = serde_json::Map::new();
691 for path in paths {
692 if let Some(val) = self.get_schema_by_path(path) {
693 result.insert(path.clone(), val);
694 }
695 }
696 Value::Object(result)
697 }
698 ReturnFormat::Array => {
699 let mut result = Vec::new();
700 for path in paths {
701 if let Some(val) = self.get_schema_by_path(path) {
702 result.push(val);
703 } else {
704 result.push(Value::Null);
705 }
706 }
707 Value::Array(result)
708 }
709 }
710 }
711
712 pub(crate) fn insert_at_path(root: &mut Value, path: &str, value: Value) {
714 let parts: Vec<&str> = path.split('.').collect();
715 let mut current = root;
716
717 for (i, part) in parts.iter().enumerate() {
718 if i == parts.len() - 1 {
719 if let Value::Object(map) = current {
721 map.insert(part.to_string(), value);
722 return; }
724 } else {
725 if !current.is_object() {
730 *current = Value::Object(serde_json::Map::new());
731 }
732
733 if let Value::Object(map) = current {
734 if !map.contains_key(*part) {
735 map.insert(part.to_string(), Value::Object(serde_json::Map::new()));
736 }
737 current = map.get_mut(*part).unwrap();
738 }
739 }
740 }
741 }
742
743 pub fn flatten_object(
745 prefix: &str,
746 value: &Value,
747 result: &mut serde_json::Map<String, Value>,
748 ) {
749 match value {
750 Value::Object(map) => {
751 for (k, v) in map {
752 let new_key = if prefix.is_empty() {
753 k.clone()
754 } else {
755 format!("{}.{}", prefix, k)
756 };
757 Self::flatten_object(&new_key, v, result);
758 }
759 }
760 _ => {
761 result.insert(prefix.to_string(), value.clone());
762 }
763 }
764 }
765
766 pub fn convert_to_format(value: Value, format: ReturnFormat) -> Value {
767 match format {
768 ReturnFormat::Nested => value,
769 ReturnFormat::Flat => {
770 let mut result = serde_json::Map::new();
771 Self::flatten_object("", &value, &mut result);
772 Value::Object(result)
773 }
774 ReturnFormat::Array => {
775 if let Value::Object(map) = value {
776 Value::Array(map.values().cloned().collect())
777 } else if let Value::Array(arr) = value {
778 Value::Array(arr)
779 } else {
780 Value::Array(vec![value])
781 }
782 }
783 }
784 }
785
786 pub fn get_field_options(&mut self, field_path: &str) -> Option<Value> {
795 let schema_ptr = if field_path.starts_with('#') || field_path.starts_with('/') {
797 path_utils::normalize_to_json_pointer(field_path).into_owned()
798 } else {
799 path_utils::dot_notation_to_schema_pointer(field_path)
800 };
801
802 let options_schema_key = format!("{}/options", schema_ptr);
804 let options_pointer =
805 path_utils::normalize_to_json_pointer(&options_schema_key).into_owned();
806
807 let options_node = self.evaluated_schema.pointer(&options_pointer)?.clone();
809
810 if let Value::Object(ref map) = options_node {
812 if map.contains_key("$evaluation") {
813 let eval_key = options_schema_key.clone();
814
815 if let Some(logic_id) = self.evaluations.get(&eval_key).copied() {
816 let snap = self.eval_data.snapshot_data();
817 if let Ok(result) = self.engine.run(&logic_id, &*snap) {
818 let cleaned = clean_float_noise_scalar(result);
819 if let Some(node) = self.evaluated_schema.pointer_mut(&options_pointer) {
820 *node = cleaned.clone();
821 }
822 return Some(cleaned);
823 }
824 }
825 return None;
827 }
828 }
829
830 let url_pointer =
832 path_utils::normalize_to_json_pointer(&format!("{}/options/url", schema_ptr))
833 .into_owned();
834
835 let templates = self.options_templates.clone();
836 for (tmpl_url_path, tmpl_str, tmpl_params_path) in templates.iter() {
837 if *tmpl_url_path == url_pointer {
838 if let Some(params) = self.evaluated_schema.pointer(tmpl_params_path) {
839 let params = params.clone();
840 if let Ok(resolved_url) = self.evaluate_template(tmpl_str, ¶ms) {
841 if let Some(target) = self.evaluated_schema.pointer_mut(&url_pointer) {
842 *target = Value::String(resolved_url);
843 }
844 return self.evaluated_schema.pointer(&options_pointer).cloned();
845 }
846 }
847 break;
848 }
849 }
850
851 Some(options_node)
853 }
854}