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.evaluated_schema.clone();
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.resolve_static_markers_in_value(&mut schema);
284 schema
285 })
286 }
287
288 fn resolve_static_markers_at_path(&self, schema_prefix: &str) -> Option<Value> {
299 let mut subtree = self.evaluated_schema.pointer(schema_prefix)?.clone();
300
301 let prefix_slash = format!("{}/", schema_prefix);
303
304 for (static_key, array_arc) in self.static_arrays.iter() {
305 let schema_path: &str = if static_key.starts_with("/$table") {
307 &static_key["/$table".len()..]
308 } else {
309 static_key.as_str()
310 };
311
312 let relative: &str = if schema_path == schema_prefix {
314 ""
316 } else if schema_path.starts_with(&prefix_slash) {
317 &schema_path[schema_prefix.len()..]
319 } else {
320 continue; };
322
323 if relative.is_empty() {
324 subtree = (**array_arc).clone();
325 } else if let Some(target) = subtree.pointer_mut(relative) {
326 *target = (**array_arc).clone();
327 }
328 }
329
330 Some(subtree)
331 }
332
333 pub fn get_schema_value_by_path(&self, path: &str) -> Option<Value> {
336 let pointer_path = path_utils::dot_notation_to_schema_pointer(path);
337 self.resolve_static_markers_at_path(pointer_path.trim_start_matches('#'))
338 }
339
340 pub fn get_schema_value(&mut self) -> Value {
344 let mut current_data = self.eval_data.data().clone();
346
347 if !current_data.is_object() {
349 current_data = Value::Object(serde_json::Map::new());
350 }
351
352 if let Some(obj) = current_data.as_object_mut() {
354 obj.remove("$params");
355 obj.remove("$context");
356 }
357
358 self.prune_hidden_values(&mut current_data, "");
360
361 for eval_key in self.value_evaluations.iter() {
364 let clean_key = eval_key.strip_prefix('#').unwrap_or(eval_key);
365
366 if clean_key.starts_with("/$params")
368 || (clean_key.ends_with("/value")
369 && (clean_key.contains("/rules/") || clean_key.contains("/options/")))
370 {
371 continue;
372 }
373
374 let path = clean_key.replace("/properties", "").replace("/value", "");
375
376 let schema_path = clean_key.strip_suffix("/value").unwrap_or(&clean_key);
379 if self.is_effective_hidden(schema_path) {
380 continue;
381 }
382
383 let value = match self.resolve_static_markers_at_path(clean_key) {
385 Some(v) => v,
386 None => continue,
387 };
388
389 let path_parts: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
391
392 if path_parts.is_empty() {
393 continue;
394 }
395
396 let mut current = &mut current_data;
398 for (i, part) in path_parts.iter().enumerate() {
399 let is_last = i == path_parts.len() - 1;
400
401 if is_last {
402 if let Some(obj) = current.as_object_mut() {
404 let should_update = match obj.get(*part) {
405 Some(v) => v.is_null(),
406 None => true,
407 };
408
409 if should_update {
410 obj.insert(
411 (*part).to_string(),
412 crate::utils::clean_float_noise(value.clone()),
413 );
414 }
415 }
416 } else {
417 if let Some(obj) = current.as_object_mut() {
419 if !obj.contains_key(*part) {
420 obj.insert((*part).to_string(), Value::Object(serde_json::Map::new()));
421 }
422
423 current = obj.get_mut(*part).unwrap();
424 } else {
425 break;
427 }
428 }
429 }
430 }
431
432 self.data = current_data.clone();
434
435 crate::utils::clean_float_noise(current_data)
436 }
437
438 pub fn get_schema_value_array(&self) -> Value {
445 let mut result = Vec::new();
446
447 for eval_key in self.value_evaluations.iter() {
448 let clean_key = eval_key.strip_prefix('#').unwrap_or(eval_key);
449
450 if clean_key.starts_with("/$params")
452 || (clean_key.ends_with("/value")
453 && (clean_key.contains("/rules/") || clean_key.contains("/options/")))
454 {
455 continue;
456 }
457
458 let schema_path = clean_key.strip_suffix("/value").unwrap_or(&clean_key);
460 if self.is_effective_hidden(schema_path) {
461 continue;
462 }
463
464 let dotted_path = clean_key
466 .replace("/properties", "")
467 .replace("/value", "")
468 .trim_start_matches('/')
469 .replace('/', ".");
470
471 if dotted_path.is_empty() {
472 continue;
473 }
474
475 let value = match self.resolve_static_markers_at_path(clean_key) {
477 Some(v) => crate::utils::clean_float_noise(v),
478 None => continue,
479 };
480
481 let mut item = serde_json::Map::new();
483 item.insert("path".to_string(), Value::String(dotted_path));
484 item.insert("value".to_string(), value);
485 result.push(Value::Object(item));
486 }
487
488 Value::Array(result)
489 }
490
491 pub fn get_schema_value_object(&self) -> Value {
498 let mut result = serde_json::Map::new();
499
500 for eval_key in self.value_evaluations.iter() {
501 let clean_key = eval_key.strip_prefix('#').unwrap_or(eval_key);
502
503 if clean_key.starts_with("/$params")
505 || (clean_key.ends_with("/value")
506 && (clean_key.contains("/rules/") || clean_key.contains("/options/")))
507 {
508 continue;
509 }
510
511 let schema_path = clean_key.strip_suffix("/value").unwrap_or(&clean_key);
513 if self.is_effective_hidden(schema_path) {
514 continue;
515 }
516
517 let dotted_path = clean_key
519 .replace("/properties", "")
520 .replace("/value", "")
521 .trim_start_matches('/')
522 .replace('/', ".");
523
524 if dotted_path.is_empty() {
525 continue;
526 }
527
528 let value = match self.resolve_static_markers_at_path(clean_key) {
530 Some(v) => crate::utils::clean_float_noise(v),
531 None => continue,
532 };
533
534 result.insert(dotted_path, value);
535 }
536
537 Value::Object(result)
538 }
539
540 pub fn get_evaluated_schema_without_params(&mut self) -> Value {
542 let mut schema = self.get_evaluated_schema();
543 if let Value::Object(ref mut map) = schema {
544 map.remove("$params");
545 }
546 schema
547 }
548
549 pub fn get_evaluated_schema_msgpack(&mut self) -> Result<Vec<u8>, String> {
551 let schema = self.get_evaluated_schema();
552 rmp_serde::to_vec(&schema).map_err(|e| format!("MessagePack serialization failed: {}", e))
553 }
554
555 pub fn get_evaluated_schema_by_path(&mut self, path: &str) -> Option<Value> {
557 self.get_schema_value_by_path(path)
558 }
559
560 pub fn get_evaluated_schema_by_paths(
562 &mut self,
563 paths: &[String],
564 format: Option<ReturnFormat>,
565 ) -> Value {
566 match format.unwrap_or(ReturnFormat::Nested) {
567 ReturnFormat::Nested => {
568 let mut result = Value::Object(serde_json::Map::new());
569 for path in paths {
570 if let Some(val) = self.get_schema_value_by_path(path) {
571 Self::insert_at_path(&mut result, path, val);
573 }
574 }
575 result
576 }
577 ReturnFormat::Flat => {
578 let mut result = serde_json::Map::new();
579 for path in paths {
580 if let Some(val) = self.get_schema_value_by_path(path) {
581 result.insert(path.clone(), val);
582 }
583 }
584 Value::Object(result)
585 }
586 ReturnFormat::Array => {
587 let mut result = Vec::new();
588 for path in paths {
589 if let Some(val) = self.get_schema_value_by_path(path) {
590 result.push(val);
591 } else {
592 result.push(Value::Null);
593 }
594 }
595 Value::Array(result)
596 }
597 }
598 }
599
600 pub fn get_schema_by_path(&self, path: &str) -> Option<Value> {
602 let pointer_path = path_utils::dot_notation_to_schema_pointer(path);
603 self.schema
604 .pointer(&pointer_path.trim_start_matches('#'))
605 .cloned()
606 }
607
608 pub fn get_schema_by_paths(&self, paths: &[String], format: Option<ReturnFormat>) -> Value {
610 match format.unwrap_or(ReturnFormat::Nested) {
611 ReturnFormat::Nested => {
612 let mut result = Value::Object(serde_json::Map::new());
613 for path in paths {
614 if let Some(val) = self.get_schema_by_path(path) {
615 Self::insert_at_path(&mut result, path, val);
616 }
617 }
618 result
619 }
620 ReturnFormat::Flat => {
621 let mut result = serde_json::Map::new();
622 for path in paths {
623 if let Some(val) = self.get_schema_by_path(path) {
624 result.insert(path.clone(), val);
625 }
626 }
627 Value::Object(result)
628 }
629 ReturnFormat::Array => {
630 let mut result = Vec::new();
631 for path in paths {
632 if let Some(val) = self.get_schema_by_path(path) {
633 result.push(val);
634 } else {
635 result.push(Value::Null);
636 }
637 }
638 Value::Array(result)
639 }
640 }
641 }
642
643 pub(crate) fn insert_at_path(root: &mut Value, path: &str, value: Value) {
645 let parts: Vec<&str> = path.split('.').collect();
646 let mut current = root;
647
648 for (i, part) in parts.iter().enumerate() {
649 if i == parts.len() - 1 {
650 if let Value::Object(map) = current {
652 map.insert(part.to_string(), value);
653 return; }
655 } else {
656 if !current.is_object() {
661 *current = Value::Object(serde_json::Map::new());
662 }
663
664 if let Value::Object(map) = current {
665 if !map.contains_key(*part) {
666 map.insert(part.to_string(), Value::Object(serde_json::Map::new()));
667 }
668 current = map.get_mut(*part).unwrap();
669 }
670 }
671 }
672 }
673
674 pub fn flatten_object(
676 prefix: &str,
677 value: &Value,
678 result: &mut serde_json::Map<String, Value>,
679 ) {
680 match value {
681 Value::Object(map) => {
682 for (k, v) in map {
683 let new_key = if prefix.is_empty() {
684 k.clone()
685 } else {
686 format!("{}.{}", prefix, k)
687 };
688 Self::flatten_object(&new_key, v, result);
689 }
690 }
691 _ => {
692 result.insert(prefix.to_string(), value.clone());
693 }
694 }
695 }
696
697 pub fn convert_to_format(value: Value, format: ReturnFormat) -> Value {
698 match format {
699 ReturnFormat::Nested => value,
700 ReturnFormat::Flat => {
701 let mut result = serde_json::Map::new();
702 Self::flatten_object("", &value, &mut result);
703 Value::Object(result)
704 }
705 ReturnFormat::Array => {
706 if let Value::Object(map) = value {
707 Value::Array(map.values().cloned().collect())
708 } else if let Value::Array(arr) = value {
709 Value::Array(arr)
710 } else {
711 Value::Array(vec![value])
712 }
713 }
714 }
715 }
716
717 pub fn get_field_options(&mut self, field_path: &str) -> Option<Value> {
726 let schema_ptr = if field_path.starts_with('#') || field_path.starts_with('/') {
728 path_utils::normalize_to_json_pointer(field_path).into_owned()
729 } else {
730 path_utils::dot_notation_to_schema_pointer(field_path)
731 };
732
733 let options_schema_key = format!("{}/options", schema_ptr);
735 let options_pointer =
736 path_utils::normalize_to_json_pointer(&options_schema_key).into_owned();
737
738 let options_node = self.evaluated_schema.pointer(&options_pointer)?.clone();
740
741 if let Value::Object(ref map) = options_node {
743 if map.contains_key("$evaluation") {
744 let eval_key = options_schema_key.clone();
745
746 if let Some(logic_id) = self.evaluations.get(&eval_key).copied() {
747 let snap = self.eval_data.snapshot_data();
748 if let Ok(result) = self.engine.run(&logic_id, &*snap) {
749 let cleaned = clean_float_noise_scalar(result);
750 if let Some(node) = self.evaluated_schema.pointer_mut(&options_pointer) {
751 *node = cleaned.clone();
752 }
753 return Some(cleaned);
754 }
755 }
756 return None;
758 }
759 }
760
761 let url_pointer =
763 path_utils::normalize_to_json_pointer(&format!("{}/options/url", schema_ptr))
764 .into_owned();
765
766 let templates = self.options_templates.clone();
767 for (tmpl_url_path, tmpl_str, tmpl_params_path) in templates.iter() {
768 if *tmpl_url_path == url_pointer {
769 if let Some(params) = self.evaluated_schema.pointer(tmpl_params_path) {
770 let params = params.clone();
771 if let Ok(resolved_url) = self.evaluate_template(tmpl_str, ¶ms) {
772 if let Some(target) = self.evaluated_schema.pointer_mut(&url_pointer) {
773 *target = Value::String(resolved_url);
774 }
775 return self.evaluated_schema.pointer(&options_pointer).cloned();
776 }
777 }
778 break;
779 }
780 }
781
782 Some(options_node)
784 }
785}