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 for (static_key, array_arc) in self.static_arrays.iter() {
356 let schema_path: &str = if static_key.starts_with("/$table") {
357 &static_key["/$table".len()..]
358 } else {
359 static_key.as_str()
360 };
361
362 if let Some(relative) = schema_prefix
363 .strip_prefix(schema_path)
364 .and_then(|relative| relative.strip_prefix('/'))
365 {
366 return array_arc.pointer(&format!("/{}", relative)).cloned();
367 }
368 }
369
370 let mut subtree = self.evaluated_schema.pointer(schema_prefix)?.clone();
371
372 let prefix_slash = format!("{}/", schema_prefix);
374
375 for (static_key, array_arc) in self.static_arrays.iter() {
376 let schema_path: &str = if static_key.starts_with("/$table") {
378 &static_key["/$table".len()..]
379 } else {
380 static_key.as_str()
381 };
382
383 let relative: &str = if schema_path == schema_prefix {
385 ""
387 } else if schema_path.starts_with(&prefix_slash) {
388 &schema_path[schema_prefix.len()..]
390 } else {
391 continue; };
393
394 if relative.is_empty() {
395 subtree = (**array_arc).clone();
396 } else if let Some(target) = subtree.pointer_mut(relative) {
397 *target = (**array_arc).clone();
398 }
399 }
400
401 Some(subtree)
402 }
403
404 pub fn get_schema_value_by_path(&self, path: &str) -> Option<Value> {
407 let pointer_path = path_utils::dot_notation_to_schema_pointer(path);
408 self.resolve_static_markers_at_path(pointer_path.trim_start_matches('#'))
409 }
410
411 pub fn get_schema_value(&mut self) -> Value {
417 let mut current_data = self.eval_data.data().clone();
419
420 if !current_data.is_object() {
422 current_data = Value::Object(serde_json::Map::new());
423 }
424
425 if let Some(obj) = current_data.as_object_mut() {
427 obj.remove("$params");
428 obj.remove("$context");
429 }
430
431 self.prune_hidden_values(&mut current_data, "");
433
434 for eval_key in self.value_evaluations.iter() {
437 let clean_key = eval_key.strip_prefix('#').unwrap_or(eval_key);
438
439 if clean_key.starts_with("/$params")
441 || (clean_key.ends_with("/value")
442 && (clean_key.contains("/rules/") || clean_key.contains("/options/")))
443 {
444 continue;
445 }
446
447 let path = clean_key.replace("/properties", "").replace("/value", "");
448
449 let schema_path = clean_key.strip_suffix("/value").unwrap_or(&clean_key);
452 if self.is_effective_hidden(schema_path) {
453 continue;
454 }
455
456 let value = match self.resolve_static_markers_at_path(clean_key) {
458 Some(v) => v,
459 None => continue,
460 };
461
462 let path_parts: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
464
465 if path_parts.is_empty() {
466 continue;
467 }
468
469 let mut current = &mut current_data;
471 for (i, part) in path_parts.iter().enumerate() {
472 let is_last = i == path_parts.len() - 1;
473
474 if is_last {
475 let schema_value = self.schema.pointer(clean_key);
478 let computed_value = schema_value
479 .and_then(Value::as_object)
480 .is_some_and(|value| value.contains_key("$evaluation"));
481 let disabled = self
482 .evaluated_schema
483 .pointer(schema_path)
484 .and_then(Value::as_object)
485 .and_then(|field| field.get("condition"))
486 .and_then(Value::as_object)
487 .and_then(|condition| condition.get("disabled"))
488 .is_some_and(|disabled| disabled == &Value::Bool(true));
489 let computed_disabled =
490 computed_value && (disabled || !self.is_mapped_in_any_layout(schema_path));
491 if let Some(obj) = current.as_object_mut() {
492 let should_update = computed_disabled
493 || match obj.get(*part) {
494 Some(v) => v.is_null(),
495 None => true,
496 };
497 if should_update {
498 obj.insert(
499 (*part).to_string(),
500 crate::utils::clean_float_noise(value.clone()),
501 );
502 }
503 }
504 } else {
505 if let Some(obj) = current.as_object_mut() {
507 if !obj.contains_key(*part) {
508 obj.insert((*part).to_string(), Value::Object(serde_json::Map::new()));
509 }
510
511 current = obj.get_mut(*part).unwrap();
512 } else {
513 break;
515 }
516 }
517 }
518 }
519
520 crate::utils::clean_float_noise(current_data)
521 }
522
523 pub fn get_schema_value_array(&self) -> Value {
530 let mut result = Vec::new();
531
532 for eval_key in self.value_evaluations.iter() {
533 let clean_key = eval_key.strip_prefix('#').unwrap_or(eval_key);
534
535 if clean_key.starts_with("/$params")
537 || (clean_key.ends_with("/value")
538 && (clean_key.contains("/rules/") || clean_key.contains("/options/")))
539 {
540 continue;
541 }
542
543 let schema_path = clean_key.strip_suffix("/value").unwrap_or(&clean_key);
545 if self.is_effective_hidden(schema_path) {
546 continue;
547 }
548
549 let dotted_path = clean_key
551 .replace("/properties", "")
552 .replace("/value", "")
553 .trim_start_matches('/')
554 .replace('/', ".");
555
556 if dotted_path.is_empty() {
557 continue;
558 }
559
560 let value = match self.resolve_static_markers_at_path(clean_key) {
562 Some(v) => crate::utils::clean_float_noise(v),
563 None => continue,
564 };
565
566 let mut item = serde_json::Map::new();
568 item.insert("path".to_string(), Value::String(dotted_path));
569 item.insert("value".to_string(), value);
570 result.push(Value::Object(item));
571 }
572
573 Value::Array(result)
574 }
575
576 pub fn get_schema_value_object(&self) -> Value {
583 let mut result = serde_json::Map::new();
584
585 for eval_key in self.value_evaluations.iter() {
586 let clean_key = eval_key.strip_prefix('#').unwrap_or(eval_key);
587
588 if clean_key.starts_with("/$params")
590 || (clean_key.ends_with("/value")
591 && (clean_key.contains("/rules/") || clean_key.contains("/options/")))
592 {
593 continue;
594 }
595
596 let schema_path = clean_key.strip_suffix("/value").unwrap_or(&clean_key);
598 if self.is_effective_hidden(schema_path) {
599 continue;
600 }
601
602 let dotted_path = clean_key
604 .replace("/properties", "")
605 .replace("/value", "")
606 .trim_start_matches('/')
607 .replace('/', ".");
608
609 if dotted_path.is_empty() {
610 continue;
611 }
612
613 let value = match self.resolve_static_markers_at_path(clean_key) {
615 Some(v) => crate::utils::clean_float_noise(v),
616 None => continue,
617 };
618
619 result.insert(dotted_path, value);
620 }
621
622 Value::Object(result)
623 }
624
625 pub fn get_evaluated_schema_without_params(&mut self) -> Value {
627 let mut schema = self.get_evaluated_schema();
628 if let Value::Object(ref mut map) = schema {
629 map.remove("$params");
630 }
631 schema
632 }
633
634 pub fn get_evaluated_schema_msgpack(&mut self) -> Result<Vec<u8>, String> {
636 let schema = self.get_evaluated_schema();
637 rmp_serde::to_vec(&schema).map_err(|e| format!("MessagePack serialization failed: {}", e))
638 }
639
640 pub fn get_evaluated_schema_resolved_msgpack(&mut self) -> Result<Vec<u8>, String> {
645 let schema = self.get_evaluated_schema_resolved();
646 rmp_serde::to_vec(&schema).map_err(|e| format!("MessagePack serialization failed: {}", e))
647 }
648
649 pub fn get_evaluated_schema_by_path(&mut self, path: &str) -> Option<Value> {
651 self.get_schema_value_by_path(path)
652 }
653
654 pub fn get_evaluated_schema_by_paths(
656 &mut self,
657 paths: &[String],
658 format: Option<ReturnFormat>,
659 ) -> Value {
660 match format.unwrap_or(ReturnFormat::Nested) {
661 ReturnFormat::Nested => {
662 let mut result = Value::Object(serde_json::Map::new());
663 for path in paths {
664 if let Some(val) = self.get_schema_value_by_path(path) {
665 Self::insert_at_path(&mut result, path, val);
667 }
668 }
669 result
670 }
671 ReturnFormat::Flat => {
672 let mut result = serde_json::Map::new();
673 for path in paths {
674 if let Some(val) = self.get_schema_value_by_path(path) {
675 result.insert(path.clone(), val);
676 }
677 }
678 Value::Object(result)
679 }
680 ReturnFormat::Array => {
681 let mut result = Vec::new();
682 for path in paths {
683 if let Some(val) = self.get_schema_value_by_path(path) {
684 result.push(val);
685 } else {
686 result.push(Value::Null);
687 }
688 }
689 Value::Array(result)
690 }
691 }
692 }
693
694 pub fn get_schema_by_path(&self, path: &str) -> Option<Value> {
696 let pointer_path = path_utils::dot_notation_to_schema_pointer(path);
697 self.schema
698 .pointer(&pointer_path.trim_start_matches('#'))
699 .cloned()
700 }
701
702 pub fn get_schema_by_paths(&self, paths: &[String], format: Option<ReturnFormat>) -> Value {
704 match format.unwrap_or(ReturnFormat::Nested) {
705 ReturnFormat::Nested => {
706 let mut result = Value::Object(serde_json::Map::new());
707 for path in paths {
708 if let Some(val) = self.get_schema_by_path(path) {
709 Self::insert_at_path(&mut result, path, val);
710 }
711 }
712 result
713 }
714 ReturnFormat::Flat => {
715 let mut result = serde_json::Map::new();
716 for path in paths {
717 if let Some(val) = self.get_schema_by_path(path) {
718 result.insert(path.clone(), val);
719 }
720 }
721 Value::Object(result)
722 }
723 ReturnFormat::Array => {
724 let mut result = Vec::new();
725 for path in paths {
726 if let Some(val) = self.get_schema_by_path(path) {
727 result.push(val);
728 } else {
729 result.push(Value::Null);
730 }
731 }
732 Value::Array(result)
733 }
734 }
735 }
736
737 pub(crate) fn insert_at_path(root: &mut Value, path: &str, value: Value) {
739 let parts: Vec<&str> = path.split('.').collect();
740 let mut current = root;
741
742 for (i, part) in parts.iter().enumerate() {
743 if i == parts.len() - 1 {
744 if let Value::Object(map) = current {
746 map.insert(part.to_string(), value);
747 return; }
749 } else {
750 if !current.is_object() {
755 *current = Value::Object(serde_json::Map::new());
756 }
757
758 if let Value::Object(map) = current {
759 if !map.contains_key(*part) {
760 map.insert(part.to_string(), Value::Object(serde_json::Map::new()));
761 }
762 current = map.get_mut(*part).unwrap();
763 }
764 }
765 }
766 }
767
768 pub fn flatten_object(
770 prefix: &str,
771 value: &Value,
772 result: &mut serde_json::Map<String, Value>,
773 ) {
774 match value {
775 Value::Object(map) => {
776 for (k, v) in map {
777 let new_key = if prefix.is_empty() {
778 k.clone()
779 } else {
780 format!("{}.{}", prefix, k)
781 };
782 Self::flatten_object(&new_key, v, result);
783 }
784 }
785 _ => {
786 result.insert(prefix.to_string(), value.clone());
787 }
788 }
789 }
790
791 pub fn convert_to_format(value: Value, format: ReturnFormat) -> Value {
792 match format {
793 ReturnFormat::Nested => value,
794 ReturnFormat::Flat => {
795 let mut result = serde_json::Map::new();
796 Self::flatten_object("", &value, &mut result);
797 Value::Object(result)
798 }
799 ReturnFormat::Array => {
800 if let Value::Object(map) = value {
801 Value::Array(map.values().cloned().collect())
802 } else if let Value::Array(arr) = value {
803 Value::Array(arr)
804 } else {
805 Value::Array(vec![value])
806 }
807 }
808 }
809 }
810
811 pub fn get_field_options(&mut self, field_path: &str) -> Option<Value> {
820 let schema_ptr = if field_path.starts_with('#') || field_path.starts_with('/') {
822 path_utils::normalize_to_json_pointer(field_path).into_owned()
823 } else {
824 path_utils::dot_notation_to_schema_pointer(field_path)
825 };
826
827 let options_schema_key = format!("{}/options", schema_ptr);
829 let options_pointer =
830 path_utils::normalize_to_json_pointer(&options_schema_key).into_owned();
831
832 let options_node = self.evaluated_schema.pointer(&options_pointer)?.clone();
834
835 if let Value::Object(ref map) = options_node {
837 if map.contains_key("$evaluation") {
838 let eval_key = options_schema_key.clone();
839
840 if let Some(logic_id) = self.evaluations.get(&eval_key).copied() {
841 let snap = self.eval_data.snapshot_data();
842 if let Ok(result) = self.engine.run(&logic_id, &*snap) {
843 let cleaned = clean_float_noise_scalar(result);
844 if let Some(node) = self.evaluated_schema.pointer_mut(&options_pointer) {
845 *node = cleaned.clone();
846 }
847 return Some(cleaned);
848 }
849 }
850 return None;
852 }
853 }
854
855 let url_pointer =
857 path_utils::normalize_to_json_pointer(&format!("{}/options/url", schema_ptr))
858 .into_owned();
859
860 let templates = self.options_templates.clone();
861 for (tmpl_url_path, tmpl_str, tmpl_params_path) in templates.iter() {
862 if *tmpl_url_path == url_pointer {
863 if let Some(params) = self.evaluated_schema.pointer(tmpl_params_path) {
864 let params = params.clone();
865 if let Ok(resolved_url) = self.evaluate_template(tmpl_str, ¶ms) {
866 if let Some(target) = self.evaluated_schema.pointer_mut(&url_pointer) {
867 *target = Value::String(resolved_url);
868 }
869 return self.evaluated_schema.pointer(&options_pointer).cloned();
870 }
871 }
872 break;
873 }
874 }
875
876 Some(options_node)
878 }
879}