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 prune_hidden_values(&self, data: &mut Value, current_path: &str) {
74 if let Value::Object(map) = data {
75 let mut keys_to_remove = Vec::new();
77
78 for (key, value) in map.iter_mut() {
79 if key == "$params" || key == "$context" {
81 continue;
82 }
83
84 let schema_path = if current_path.is_empty() {
88 format!("/properties/{}", key)
89 } else {
90 format!("{}/properties/{}", current_path, key)
91 };
92
93 if self.is_effective_hidden(&schema_path) {
95 keys_to_remove.push(key.clone());
96 } else {
97 if value.is_object() {
99 self.prune_hidden_values(value, &schema_path);
100 }
101 }
102 }
103
104 for key in keys_to_remove {
106 map.remove(&key);
107 }
108 }
109 }
110
111 fn resolve_static_markers_in_value(&self, schema_output: &mut Value) {
117 for (static_key, array_arc) in self.static_arrays.iter() {
118 let schema_path = if static_key.starts_with("/$table") {
120 &static_key["/$table".len()..] } else {
122 static_key.as_str() };
124
125 if let Some(target_val) = schema_output.pointer_mut(schema_path) {
127 *target_val = (**array_arc).clone();
129 }
130 }
131 }
132
133 pub fn get_evaluated_schema(&mut self) -> Value {
140 time_block!("get_evaluated_schema()", {
141 let mut schema = self.evaluated_schema.clone();
142 self.resolve_static_markers_in_value(&mut schema);
143 schema
144 })
145 }
146
147 pub fn get_resolved_layout(&mut self) -> ResolvedLayoutResult {
150 time_block!("get_resolved_layout()", {
151 if let Some(ref cached) = self.resolved_layout_cache {
153 return cached.as_ref().clone();
154 }
155 let result = match self.resolve_layout(false) {
157 Ok(entries) => entries,
158 Err(e) => {
159 eprintln!("Warning: Layout resolution failed: {}", e);
160 Vec::new()
161 }
162 };
163 self.resolved_layout_cache = Some(Arc::new(result.clone()));
164 result
165 })
166 }
167
168 pub fn get_evaluated_schema_resolved(&mut self) -> Value {
177 time_block!("get_evaluated_schema_resolved()", {
178 let mut schema = self.get_evaluated_schema_without_params();
179 let overlays = self.get_resolved_layout();
180
181 struct ResolveEntry {
182 layout_path: String,
183 element_idx: usize,
184 overlay: indexmap::IndexMap<String, Value>,
185 }
186
187 let mut entries: Vec<ResolveEntry> = overlays
188 .iter()
189 .map(|entry| {
190 let layout_path =
191 path_utils::normalize_to_json_pointer(&entry.layout_path).into_owned();
192 ResolveEntry {
193 layout_path,
194 element_idx: entry.element_idx,
195 overlay: entry.overlay.clone(),
196 }
197 })
198 .collect();
199 drop(overlays);
200
201 entries.sort_by(|a, b| {
205 let depth_a = a.layout_path.matches('/').count();
206 let depth_b = b.layout_path.matches('/').count();
207 depth_a
208 .cmp(&depth_b)
209 .then_with(|| a.element_idx.cmp(&b.element_idx))
210 });
211
212 for entry in entries {
216 let resolved_value: Option<Value> = (|| -> Option<Value> {
219 let arr = schema.pointer(&entry.layout_path)?.as_array()?;
220 let element = arr.get(entry.element_idx)?;
221 let ref_str = element.get("$ref")?.as_str()?;
222
223 let ref_pointer = if ref_str.starts_with('#') || ref_str.starts_with('/') {
224 path_utils::normalize_to_json_pointer(ref_str).into_owned()
225 } else {
226 let schema_pointer = path_utils::dot_notation_to_schema_pointer(ref_str);
227 let normalized =
228 path_utils::normalize_to_json_pointer(&schema_pointer).into_owned();
229 if schema.pointer(&normalized).is_some() {
230 normalized
231 } else {
232 format!("/properties/{}", ref_str.replace('.', "/properties/"))
233 }
234 };
235
236 let mut resolved = schema.pointer(&ref_pointer)?.clone();
237
238 if let Value::Object(ref mut resolved_map) = resolved {
240 if let Some(Value::Object(layout_obj)) = resolved_map.remove("$layout") {
241 let mut result = layout_obj;
242 for (key, value) in resolved_map.clone().into_iter() {
243 if key != "type" || !result.contains_key("type") {
244 result.insert(key, value);
245 }
246 }
247 resolved = Value::Object(result);
248 }
249 }
250
251 Some(resolved)
252 })();
253
254 if let Some(Value::Array(arr)) = schema.pointer_mut(&entry.layout_path) {
255 if entry.element_idx < arr.len() {
256 let element = &mut arr[entry.element_idx];
257
258 if let Some(resolved) = resolved_value {
260 if let Value::Object(mut resolved_map) = resolved {
261 if let Value::Object(mut map) = element.take() {
262 map.remove("$ref");
263 for (key, value) in map {
264 resolved_map.insert(key, value);
265 }
266 }
267 *element = Value::Object(resolved_map);
268 } else {
269 *element = resolved;
270 }
271 }
272
273 if let Value::Object(ref mut map) = element {
275 for (k, v) in &entry.overlay {
276 map.insert(k.clone(), v.clone());
277 }
278 }
279 }
280 }
281 }
282
283 Self::stamp_property_metadata(&mut schema);
284 schema
285 })
286 }
287
288 fn stamp_property_metadata(schema: &mut Value) {
290 fn walk(value: &mut Value, path: &str, parent_hidden: bool) {
291 let Some(map) = value.as_object_mut() else {
292 return;
293 };
294
295 let hidden = parent_hidden
296 || map
297 .get("condition")
298 .and_then(Value::as_object)
299 .and_then(|condition| condition.get("hidden"))
300 .is_some_and(|hidden| hidden == &Value::Bool(true));
301
302 if let Some(Value::Object(properties)) = map.get_mut("properties") {
303 for (name, property) in properties {
304 let property_path = if path.is_empty() {
305 format!("properties.{}", name)
306 } else {
307 format!("{}.properties.{}", path, name)
308 };
309 if let Value::Object(property_map) = property {
310 property_map.insert(
311 "$fullpath".to_string(),
312 Value::String(property_path.clone()),
313 );
314 property_map.insert("$path".to_string(), Value::String(name.clone()));
315 property_map.insert("$parentHide".to_string(), Value::Bool(hidden));
316 }
317 walk(property, &property_path, hidden);
318 }
319 }
320
321 for (name, child) in map {
322 if name != "properties" && !name.starts_with('$') && child.is_object() {
323 let child_path = if path.is_empty() {
324 name.clone()
325 } else {
326 format!("{}.{}", path, name)
327 };
328 walk(child, &child_path, hidden);
329 }
330 }
331 }
332
333 walk(schema, "", false);
334 }
335
336 fn resolve_static_markers_at_path(&self, schema_prefix: &str) -> Option<Value> {
347 let mut subtree = self.evaluated_schema.pointer(schema_prefix)?.clone();
348
349 let prefix_slash = format!("{}/", schema_prefix);
351
352 for (static_key, array_arc) in self.static_arrays.iter() {
353 let schema_path: &str = if static_key.starts_with("/$table") {
355 &static_key["/$table".len()..]
356 } else {
357 static_key.as_str()
358 };
359
360 let relative: &str = if schema_path == schema_prefix {
362 ""
364 } else if schema_path.starts_with(&prefix_slash) {
365 &schema_path[schema_prefix.len()..]
367 } else {
368 continue; };
370
371 if relative.is_empty() {
372 subtree = (**array_arc).clone();
373 } else if let Some(target) = subtree.pointer_mut(relative) {
374 *target = (**array_arc).clone();
375 }
376 }
377
378 Some(subtree)
379 }
380
381 pub fn get_schema_value_by_path(&self, path: &str) -> Option<Value> {
384 let pointer_path = path_utils::dot_notation_to_schema_pointer(path);
385 self.resolve_static_markers_at_path(pointer_path.trim_start_matches('#'))
386 }
387
388 pub fn get_schema_value(&mut self) -> Value {
392 let mut current_data = self.eval_data.data().clone();
394
395 if !current_data.is_object() {
397 current_data = Value::Object(serde_json::Map::new());
398 }
399
400 if let Some(obj) = current_data.as_object_mut() {
402 obj.remove("$params");
403 obj.remove("$context");
404 }
405
406 self.prune_hidden_values(&mut current_data, "");
408
409 for eval_key in self.value_evaluations.iter() {
412 let clean_key = eval_key.strip_prefix('#').unwrap_or(eval_key);
413
414 if clean_key.starts_with("/$params")
416 || (clean_key.ends_with("/value")
417 && (clean_key.contains("/rules/") || clean_key.contains("/options/")))
418 {
419 continue;
420 }
421
422 let path = clean_key.replace("/properties", "").replace("/value", "");
423
424 let schema_path = clean_key.strip_suffix("/value").unwrap_or(&clean_key);
427 if self.is_effective_hidden(schema_path) {
428 continue;
429 }
430
431 let value = match self.resolve_static_markers_at_path(clean_key) {
433 Some(v) => v,
434 None => continue,
435 };
436
437 let path_parts: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
439
440 if path_parts.is_empty() {
441 continue;
442 }
443
444 let mut current = &mut current_data;
446 for (i, part) in path_parts.iter().enumerate() {
447 let is_last = i == path_parts.len() - 1;
448
449 if is_last {
450 if let Some(obj) = current.as_object_mut() {
452 let should_update = match obj.get(*part) {
453 Some(v) => v.is_null(),
454 None => true,
455 };
456
457 if should_update {
458 obj.insert(
459 (*part).to_string(),
460 crate::utils::clean_float_noise(value.clone()),
461 );
462 }
463 }
464 } else {
465 if let Some(obj) = current.as_object_mut() {
467 if !obj.contains_key(*part) {
468 obj.insert((*part).to_string(), Value::Object(serde_json::Map::new()));
469 }
470
471 current = obj.get_mut(*part).unwrap();
472 } else {
473 break;
475 }
476 }
477 }
478 }
479
480 self.data = current_data.clone();
482
483 crate::utils::clean_float_noise(current_data)
484 }
485
486 pub fn get_schema_value_array(&self) -> Value {
493 let mut result = Vec::new();
494
495 for eval_key in self.value_evaluations.iter() {
496 let clean_key = eval_key.strip_prefix('#').unwrap_or(eval_key);
497
498 if clean_key.starts_with("/$params")
500 || (clean_key.ends_with("/value")
501 && (clean_key.contains("/rules/") || clean_key.contains("/options/")))
502 {
503 continue;
504 }
505
506 let schema_path = clean_key.strip_suffix("/value").unwrap_or(&clean_key);
508 if self.is_effective_hidden(schema_path) {
509 continue;
510 }
511
512 let dotted_path = clean_key
514 .replace("/properties", "")
515 .replace("/value", "")
516 .trim_start_matches('/')
517 .replace('/', ".");
518
519 if dotted_path.is_empty() {
520 continue;
521 }
522
523 let value = match self.resolve_static_markers_at_path(clean_key) {
525 Some(v) => crate::utils::clean_float_noise(v),
526 None => continue,
527 };
528
529 let mut item = serde_json::Map::new();
531 item.insert("path".to_string(), Value::String(dotted_path));
532 item.insert("value".to_string(), value);
533 result.push(Value::Object(item));
534 }
535
536 Value::Array(result)
537 }
538
539 pub fn get_schema_value_object(&self) -> Value {
546 let mut result = serde_json::Map::new();
547
548 for eval_key in self.value_evaluations.iter() {
549 let clean_key = eval_key.strip_prefix('#').unwrap_or(eval_key);
550
551 if clean_key.starts_with("/$params")
553 || (clean_key.ends_with("/value")
554 && (clean_key.contains("/rules/") || clean_key.contains("/options/")))
555 {
556 continue;
557 }
558
559 let schema_path = clean_key.strip_suffix("/value").unwrap_or(&clean_key);
561 if self.is_effective_hidden(schema_path) {
562 continue;
563 }
564
565 let dotted_path = clean_key
567 .replace("/properties", "")
568 .replace("/value", "")
569 .trim_start_matches('/')
570 .replace('/', ".");
571
572 if dotted_path.is_empty() {
573 continue;
574 }
575
576 let value = match self.resolve_static_markers_at_path(clean_key) {
578 Some(v) => crate::utils::clean_float_noise(v),
579 None => continue,
580 };
581
582 result.insert(dotted_path, value);
583 }
584
585 Value::Object(result)
586 }
587
588 pub fn get_evaluated_schema_without_params(&mut self) -> Value {
590 let mut schema = self.get_evaluated_schema();
591 if let Value::Object(ref mut map) = schema {
592 map.remove("$params");
593 }
594 schema
595 }
596
597 pub fn get_evaluated_schema_msgpack(&mut self) -> Result<Vec<u8>, String> {
599 let schema = self.get_evaluated_schema();
600 rmp_serde::to_vec(&schema).map_err(|e| format!("MessagePack serialization failed: {}", e))
601 }
602
603 pub fn get_evaluated_schema_by_path(&mut self, path: &str) -> Option<Value> {
605 self.get_schema_value_by_path(path)
606 }
607
608 pub fn get_evaluated_schema_by_paths(
610 &mut self,
611 paths: &[String],
612 format: Option<ReturnFormat>,
613 ) -> Value {
614 match format.unwrap_or(ReturnFormat::Nested) {
615 ReturnFormat::Nested => {
616 let mut result = Value::Object(serde_json::Map::new());
617 for path in paths {
618 if let Some(val) = self.get_schema_value_by_path(path) {
619 Self::insert_at_path(&mut result, path, val);
621 }
622 }
623 result
624 }
625 ReturnFormat::Flat => {
626 let mut result = serde_json::Map::new();
627 for path in paths {
628 if let Some(val) = self.get_schema_value_by_path(path) {
629 result.insert(path.clone(), val);
630 }
631 }
632 Value::Object(result)
633 }
634 ReturnFormat::Array => {
635 let mut result = Vec::new();
636 for path in paths {
637 if let Some(val) = self.get_schema_value_by_path(path) {
638 result.push(val);
639 } else {
640 result.push(Value::Null);
641 }
642 }
643 Value::Array(result)
644 }
645 }
646 }
647
648 pub fn get_schema_by_path(&self, path: &str) -> Option<Value> {
650 let pointer_path = path_utils::dot_notation_to_schema_pointer(path);
651 self.schema
652 .pointer(&pointer_path.trim_start_matches('#'))
653 .cloned()
654 }
655
656 pub fn get_schema_by_paths(&self, paths: &[String], format: Option<ReturnFormat>) -> Value {
658 match format.unwrap_or(ReturnFormat::Nested) {
659 ReturnFormat::Nested => {
660 let mut result = Value::Object(serde_json::Map::new());
661 for path in paths {
662 if let Some(val) = self.get_schema_by_path(path) {
663 Self::insert_at_path(&mut result, path, val);
664 }
665 }
666 result
667 }
668 ReturnFormat::Flat => {
669 let mut result = serde_json::Map::new();
670 for path in paths {
671 if let Some(val) = self.get_schema_by_path(path) {
672 result.insert(path.clone(), val);
673 }
674 }
675 Value::Object(result)
676 }
677 ReturnFormat::Array => {
678 let mut result = Vec::new();
679 for path in paths {
680 if let Some(val) = self.get_schema_by_path(path) {
681 result.push(val);
682 } else {
683 result.push(Value::Null);
684 }
685 }
686 Value::Array(result)
687 }
688 }
689 }
690
691 pub(crate) fn insert_at_path(root: &mut Value, path: &str, value: Value) {
693 let parts: Vec<&str> = path.split('.').collect();
694 let mut current = root;
695
696 for (i, part) in parts.iter().enumerate() {
697 if i == parts.len() - 1 {
698 if let Value::Object(map) = current {
700 map.insert(part.to_string(), value);
701 return; }
703 } else {
704 if !current.is_object() {
709 *current = Value::Object(serde_json::Map::new());
710 }
711
712 if let Value::Object(map) = current {
713 if !map.contains_key(*part) {
714 map.insert(part.to_string(), Value::Object(serde_json::Map::new()));
715 }
716 current = map.get_mut(*part).unwrap();
717 }
718 }
719 }
720 }
721
722 pub fn flatten_object(
724 prefix: &str,
725 value: &Value,
726 result: &mut serde_json::Map<String, Value>,
727 ) {
728 match value {
729 Value::Object(map) => {
730 for (k, v) in map {
731 let new_key = if prefix.is_empty() {
732 k.clone()
733 } else {
734 format!("{}.{}", prefix, k)
735 };
736 Self::flatten_object(&new_key, v, result);
737 }
738 }
739 _ => {
740 result.insert(prefix.to_string(), value.clone());
741 }
742 }
743 }
744
745 pub fn convert_to_format(value: Value, format: ReturnFormat) -> Value {
746 match format {
747 ReturnFormat::Nested => value,
748 ReturnFormat::Flat => {
749 let mut result = serde_json::Map::new();
750 Self::flatten_object("", &value, &mut result);
751 Value::Object(result)
752 }
753 ReturnFormat::Array => {
754 if let Value::Object(map) = value {
755 Value::Array(map.values().cloned().collect())
756 } else if let Value::Array(arr) = value {
757 Value::Array(arr)
758 } else {
759 Value::Array(vec![value])
760 }
761 }
762 }
763 }
764
765 pub fn get_field_options(&mut self, field_path: &str) -> Option<Value> {
774 let schema_ptr = if field_path.starts_with('#') || field_path.starts_with('/') {
776 path_utils::normalize_to_json_pointer(field_path).into_owned()
777 } else {
778 path_utils::dot_notation_to_schema_pointer(field_path)
779 };
780
781 let options_schema_key = format!("{}/options", schema_ptr);
783 let options_pointer =
784 path_utils::normalize_to_json_pointer(&options_schema_key).into_owned();
785
786 let options_node = self.evaluated_schema.pointer(&options_pointer)?.clone();
788
789 if let Value::Object(ref map) = options_node {
791 if map.contains_key("$evaluation") {
792 let eval_key = options_schema_key.clone();
793
794 if let Some(logic_id) = self.evaluations.get(&eval_key).copied() {
795 let snap = self.eval_data.snapshot_data();
796 if let Ok(result) = self.engine.run(&logic_id, &*snap) {
797 let cleaned = clean_float_noise_scalar(result);
798 if let Some(node) = self.evaluated_schema.pointer_mut(&options_pointer) {
799 *node = cleaned.clone();
800 }
801 return Some(cleaned);
802 }
803 }
804 return None;
806 }
807 }
808
809 let url_pointer =
811 path_utils::normalize_to_json_pointer(&format!("{}/options/url", schema_ptr))
812 .into_owned();
813
814 let templates = self.options_templates.clone();
815 for (tmpl_url_path, tmpl_str, tmpl_params_path) in templates.iter() {
816 if *tmpl_url_path == url_pointer {
817 if let Some(params) = self.evaluated_schema.pointer(tmpl_params_path) {
818 let params = params.clone();
819 if let Ok(resolved_url) = self.evaluate_template(tmpl_str, ¶ms) {
820 if let Some(target) = self.evaluated_schema.pointer_mut(&url_pointer) {
821 *target = Value::String(resolved_url);
822 }
823 return self.evaluated_schema.pointer(&options_pointer).cloned();
824 }
825 }
826 break;
827 }
828 }
829
830 Some(options_node)
832 }
833}