1use super::JSONEval;
2use crate::jsoneval::path_utils;
3use crate::jsoneval::types::{ResolvedLayoutResult, ReturnFormat};
4use crate::utils::clean_float_noise_scalar;
5use crate::time_block;
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 mut end = schema_pointer.len();
14
15 loop {
16 let current_path = &schema_pointer[..end];
17
18 if let Some(schema_node) = self.evaluated_schema.pointer(current_path) {
19 if let Value::Object(map) = schema_node {
20 if let Some(Value::Object(condition)) = map.get("condition") {
21 if let Some(Value::Bool(true)) = condition.get("hidden") {
22 return true;
23 }
24 }
25
26 if let Some(Value::Object(layout)) = map.get("$layout") {
27 if let Some(Value::Object(hide_layout)) = layout.get("hideLayout") {
28 if let Some(Value::Bool(true)) = hide_layout.get("all") {
29 return true;
30 }
31 }
32 }
33 }
34 }
35
36 if end == 0 {
37 break;
38 }
39
40 match schema_pointer[..end].rfind('/') {
42 Some(0) | None => {
43 end = 0;
44 }
45 Some(last_slash) => {
46 end = last_slash;
47 let parent = &schema_pointer[..end];
48 if parent.ends_with("/properties") {
49 end -= "/properties".len();
50 } else if parent.ends_with("/items") {
51 end -= "/items".len();
52 }
53 }
54 }
55 }
56
57 false
58 }
59
60 fn prune_hidden_values(&self, data: &mut Value, current_path: &str) {
62 if let Value::Object(map) = data {
63 let mut keys_to_remove = Vec::new();
65
66 for (key, value) in map.iter_mut() {
67 if key == "$params" || key == "$context" {
69 continue;
70 }
71
72 let schema_path = if current_path.is_empty() {
76 format!("/properties/{}", key)
77 } else {
78 format!("{}/properties/{}", current_path, key)
79 };
80
81 if self.is_effective_hidden(&schema_path) {
83 keys_to_remove.push(key.clone());
84 } else {
85 if value.is_object() {
87 self.prune_hidden_values(value, &schema_path);
88 }
89 }
90 }
91
92 for key in keys_to_remove {
94 map.remove(&key);
95 }
96 }
97 }
98
99 fn resolve_static_markers_in_value(&self, schema_output: &mut Value) {
105 for (static_key, array_arc) in self.static_arrays.iter() {
106 let schema_path = if static_key.starts_with("/$table") {
108 &static_key["/$table".len()..] } else {
110 static_key.as_str() };
112
113 if let Some(target_val) = schema_output.pointer_mut(schema_path) {
115 *target_val = (**array_arc).clone();
117 }
118 }
119 }
120
121
122 pub fn get_evaluated_schema(&mut self) -> Value {
129 time_block!("get_evaluated_schema()", {
130 let mut schema = self.evaluated_schema.clone();
131 self.resolve_static_markers_in_value(&mut schema);
132 schema
133 })
134 }
135
136 pub fn get_resolved_layout(&mut self) -> ResolvedLayoutResult {
139 time_block!("get_resolved_layout()", {
140 if let Some(ref cached) = self.resolved_layout_cache {
142 return cached.as_ref().clone();
143 }
144 let result = match self.resolve_layout(false) {
146 Ok(entries) => entries,
147 Err(e) => {
148 eprintln!("Warning: Layout resolution failed: {}", e);
149 Vec::new()
150 }
151 };
152 self.resolved_layout_cache = Some(Arc::new(result.clone()));
153 result
154 })
155 }
156
157 pub fn get_evaluated_schema_resolved(&mut self) -> Value {
166 time_block!("get_evaluated_schema_resolved()", {
167 let mut schema = self.evaluated_schema.clone();
168 let overlays = self.get_resolved_layout();
169
170 struct ResolveEntry {
171 layout_path: String,
172 element_idx: usize,
173 overlay: indexmap::IndexMap<String, Value>,
174 }
175
176 let mut entries: Vec<ResolveEntry> = overlays.iter().map(|entry| {
177 let layout_path = path_utils::normalize_to_json_pointer(&entry.layout_path).into_owned();
178 ResolveEntry {
179 layout_path,
180 element_idx: entry.element_idx,
181 overlay: entry.overlay.clone(),
182 }
183 }).collect();
184 drop(overlays);
185
186 entries.sort_by(|a, b| {
190 let depth_a = a.layout_path.matches('/').count();
191 let depth_b = b.layout_path.matches('/').count();
192 depth_a.cmp(&depth_b).then_with(|| a.element_idx.cmp(&b.element_idx))
193 });
194
195 for entry in entries {
199 let resolved_value: Option<Value> = (|| -> Option<Value> {
202 let arr = schema.pointer(&entry.layout_path)?.as_array()?;
203 let element = arr.get(entry.element_idx)?;
204 let ref_str = element.get("$ref")?.as_str()?;
205
206 let ref_pointer = if ref_str.starts_with('#') || ref_str.starts_with('/') {
207 path_utils::normalize_to_json_pointer(ref_str).into_owned()
208 } else {
209 let schema_pointer = path_utils::dot_notation_to_schema_pointer(ref_str);
210 let normalized = path_utils::normalize_to_json_pointer(&schema_pointer).into_owned();
211 if schema.pointer(&normalized).is_some() {
212 normalized
213 } else {
214 format!("/properties/{}", ref_str.replace('.', "/properties/"))
215 }
216 };
217
218 let mut resolved = schema.pointer(&ref_pointer)?.clone();
219
220 if let Value::Object(ref mut resolved_map) = resolved {
222 if let Some(Value::Object(layout_obj)) = resolved_map.remove("$layout") {
223 let mut result = layout_obj;
224 for (key, value) in resolved_map.clone().into_iter() {
225 if key != "type" || !result.contains_key("type") {
226 result.insert(key, value);
227 }
228 }
229 resolved = Value::Object(result);
230 }
231 }
232
233 Some(resolved)
234 })();
235
236 if let Some(Value::Array(arr)) = schema.pointer_mut(&entry.layout_path) {
237 if entry.element_idx < arr.len() {
238 let element = &mut arr[entry.element_idx];
239
240 if let Some(resolved) = resolved_value {
242 if let Value::Object(mut resolved_map) = resolved {
243 if let Value::Object(mut map) = element.take() {
244 map.remove("$ref");
245 for (key, value) in map {
246 resolved_map.insert(key, value);
247 }
248 }
249 *element = Value::Object(resolved_map);
250 } else {
251 *element = resolved;
252 }
253 }
254
255 if let Value::Object(ref mut map) = element {
257 for (k, v) in &entry.overlay {
258 map.insert(k.clone(), v.clone());
259 }
260 }
261 }
262 }
263 }
264
265 self.resolve_static_markers_in_value(&mut schema);
266 schema
267 })
268 }
269
270 fn resolve_static_markers_at_path(&self, schema_prefix: &str) -> Option<Value> {
281 let mut subtree = self.evaluated_schema.pointer(schema_prefix)?.clone();
282
283 let prefix_slash = format!("{}/", schema_prefix);
285
286 for (static_key, array_arc) in self.static_arrays.iter() {
287 let schema_path: &str = if static_key.starts_with("/$table") {
289 &static_key["/$table".len()..]
290 } else {
291 static_key.as_str()
292 };
293
294 let relative: &str = if schema_path == schema_prefix {
296 ""
298 } else if schema_path.starts_with(&prefix_slash) {
299 &schema_path[schema_prefix.len()..]
301 } else {
302 continue; };
304
305 if relative.is_empty() {
306 subtree = (**array_arc).clone();
307 } else if let Some(target) = subtree.pointer_mut(relative) {
308 *target = (**array_arc).clone();
309 }
310 }
311
312 Some(subtree)
313 }
314
315 pub fn get_schema_value_by_path(&self, path: &str) -> Option<Value> {
318 let pointer_path = path_utils::dot_notation_to_schema_pointer(path);
319 self.resolve_static_markers_at_path(pointer_path.trim_start_matches('#'))
320 }
321
322 pub fn get_schema_value(&mut self) -> Value {
326 let mut current_data = self.eval_data.data().clone();
328
329 if !current_data.is_object() {
331 current_data = Value::Object(serde_json::Map::new());
332 }
333
334 if let Some(obj) = current_data.as_object_mut() {
336 obj.remove("$params");
337 obj.remove("$context");
338 }
339
340 self.prune_hidden_values(&mut current_data, "");
342
343 for eval_key in self.value_evaluations.iter() {
346 let clean_key = eval_key.strip_prefix('#').unwrap_or(eval_key);
347
348 if clean_key.starts_with("/$params")
350 || (clean_key.ends_with("/value")
351 && (clean_key.contains("/rules/") || clean_key.contains("/options/")))
352 {
353 continue;
354 }
355
356 let path = clean_key.replace("/properties", "").replace("/value", "");
357
358 let schema_path = clean_key.strip_suffix("/value").unwrap_or(&clean_key);
361 if self.is_effective_hidden(schema_path) {
362 continue;
363 }
364
365 let value = match self.resolve_static_markers_at_path(clean_key) {
367 Some(v) => v,
368 None => continue,
369 };
370
371 let path_parts: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
373
374 if path_parts.is_empty() {
375 continue;
376 }
377
378 let mut current = &mut current_data;
380 for (i, part) in path_parts.iter().enumerate() {
381 let is_last = i == path_parts.len() - 1;
382
383 if is_last {
384 if let Some(obj) = current.as_object_mut() {
386 let should_update = match obj.get(*part) {
387 Some(v) => v.is_null(),
388 None => true,
389 };
390
391 if should_update {
392 obj.insert(
393 (*part).to_string(),
394 crate::utils::clean_float_noise(value.clone()),
395 );
396 }
397 }
398 } else {
399 if let Some(obj) = current.as_object_mut() {
401 if !obj.contains_key(*part) {
402 obj.insert((*part).to_string(), Value::Object(serde_json::Map::new()));
403 }
404
405 current = obj.get_mut(*part).unwrap();
406 } else {
407 break;
409 }
410 }
411 }
412 }
413
414 self.data = current_data.clone();
416
417 crate::utils::clean_float_noise(current_data)
418 }
419
420 pub fn get_schema_value_array(&self) -> Value {
427 let mut result = Vec::new();
428
429 for eval_key in self.value_evaluations.iter() {
430 let clean_key = eval_key.strip_prefix('#').unwrap_or(eval_key);
431
432 if clean_key.starts_with("/$params")
434 || (clean_key.ends_with("/value")
435 && (clean_key.contains("/rules/") || clean_key.contains("/options/")))
436 {
437 continue;
438 }
439
440 let schema_path = clean_key.strip_suffix("/value").unwrap_or(&clean_key);
442 if self.is_effective_hidden(schema_path) {
443 continue;
444 }
445
446 let dotted_path = clean_key
448 .replace("/properties", "")
449 .replace("/value", "")
450 .trim_start_matches('/')
451 .replace('/', ".");
452
453 if dotted_path.is_empty() {
454 continue;
455 }
456
457 let value = match self.resolve_static_markers_at_path(clean_key) {
459 Some(v) => crate::utils::clean_float_noise(v),
460 None => continue,
461 };
462
463 let mut item = serde_json::Map::new();
465 item.insert("path".to_string(), Value::String(dotted_path));
466 item.insert("value".to_string(), value);
467 result.push(Value::Object(item));
468 }
469
470 Value::Array(result)
471 }
472
473 pub fn get_schema_value_object(&self) -> Value {
480 let mut result = serde_json::Map::new();
481
482 for eval_key in self.value_evaluations.iter() {
483 let clean_key = eval_key.strip_prefix('#').unwrap_or(eval_key);
484
485 if clean_key.starts_with("/$params")
487 || (clean_key.ends_with("/value")
488 && (clean_key.contains("/rules/") || clean_key.contains("/options/")))
489 {
490 continue;
491 }
492
493 let schema_path = clean_key.strip_suffix("/value").unwrap_or(&clean_key);
495 if self.is_effective_hidden(schema_path) {
496 continue;
497 }
498
499 let dotted_path = clean_key
501 .replace("/properties", "")
502 .replace("/value", "")
503 .trim_start_matches('/')
504 .replace('/', ".");
505
506 if dotted_path.is_empty() {
507 continue;
508 }
509
510 let value = match self.resolve_static_markers_at_path(clean_key) {
512 Some(v) => crate::utils::clean_float_noise(v),
513 None => continue,
514 };
515
516 result.insert(dotted_path, value);
517 }
518
519 Value::Object(result)
520 }
521
522 pub fn get_evaluated_schema_without_params(&mut self) -> Value {
524 let mut schema = self.get_evaluated_schema();
525 if let Value::Object(ref mut map) = schema {
526 map.remove("$params");
527 }
528 schema
529 }
530
531 pub fn get_evaluated_schema_msgpack(&mut self) -> Result<Vec<u8>, String> {
533 let schema = self.get_evaluated_schema();
534 rmp_serde::to_vec(&schema).map_err(|e| format!("MessagePack serialization failed: {}", e))
535 }
536
537 pub fn get_evaluated_schema_by_path(&mut self, path: &str) -> Option<Value> {
539 self.get_schema_value_by_path(path)
540 }
541
542 pub fn get_evaluated_schema_by_paths(
544 &mut self,
545 paths: &[String],
546 format: Option<ReturnFormat>,
547 ) -> Value {
548 match format.unwrap_or(ReturnFormat::Nested) {
549 ReturnFormat::Nested => {
550 let mut result = Value::Object(serde_json::Map::new());
551 for path in paths {
552 if let Some(val) = self.get_schema_value_by_path(path) {
553 Self::insert_at_path(&mut result, path, val);
555 }
556 }
557 result
558 }
559 ReturnFormat::Flat => {
560 let mut result = serde_json::Map::new();
561 for path in paths {
562 if let Some(val) = self.get_schema_value_by_path(path) {
563 result.insert(path.clone(), val);
564 }
565 }
566 Value::Object(result)
567 }
568 ReturnFormat::Array => {
569 let mut result = Vec::new();
570 for path in paths {
571 if let Some(val) = self.get_schema_value_by_path(path) {
572 result.push(val);
573 } else {
574 result.push(Value::Null);
575 }
576 }
577 Value::Array(result)
578 }
579 }
580 }
581
582 pub fn get_schema_by_path(&self, path: &str) -> Option<Value> {
584 let pointer_path = path_utils::dot_notation_to_schema_pointer(path);
585 self.schema
586 .pointer(&pointer_path.trim_start_matches('#'))
587 .cloned()
588 }
589
590 pub fn get_schema_by_paths(&self, paths: &[String], format: Option<ReturnFormat>) -> Value {
592 match format.unwrap_or(ReturnFormat::Nested) {
593 ReturnFormat::Nested => {
594 let mut result = Value::Object(serde_json::Map::new());
595 for path in paths {
596 if let Some(val) = self.get_schema_by_path(path) {
597 Self::insert_at_path(&mut result, path, val);
598 }
599 }
600 result
601 }
602 ReturnFormat::Flat => {
603 let mut result = serde_json::Map::new();
604 for path in paths {
605 if let Some(val) = self.get_schema_by_path(path) {
606 result.insert(path.clone(), val);
607 }
608 }
609 Value::Object(result)
610 }
611 ReturnFormat::Array => {
612 let mut result = Vec::new();
613 for path in paths {
614 if let Some(val) = self.get_schema_by_path(path) {
615 result.push(val);
616 } else {
617 result.push(Value::Null);
618 }
619 }
620 Value::Array(result)
621 }
622 }
623 }
624
625 pub(crate) fn insert_at_path(root: &mut Value, path: &str, value: Value) {
627 let parts: Vec<&str> = path.split('.').collect();
628 let mut current = root;
629
630 for (i, part) in parts.iter().enumerate() {
631 if i == parts.len() - 1 {
632 if let Value::Object(map) = current {
634 map.insert(part.to_string(), value);
635 return; }
637 } else {
638 if !current.is_object() {
643 *current = Value::Object(serde_json::Map::new());
644 }
645
646 if let Value::Object(map) = current {
647 if !map.contains_key(*part) {
648 map.insert(part.to_string(), Value::Object(serde_json::Map::new()));
649 }
650 current = map.get_mut(*part).unwrap();
651 }
652 }
653 }
654 }
655
656 pub fn flatten_object(
658 prefix: &str,
659 value: &Value,
660 result: &mut serde_json::Map<String, Value>,
661 ) {
662 match value {
663 Value::Object(map) => {
664 for (k, v) in map {
665 let new_key = if prefix.is_empty() {
666 k.clone()
667 } else {
668 format!("{}.{}", prefix, k)
669 };
670 Self::flatten_object(&new_key, v, result);
671 }
672 }
673 _ => {
674 result.insert(prefix.to_string(), value.clone());
675 }
676 }
677 }
678
679 pub fn convert_to_format(value: Value, format: ReturnFormat) -> Value {
680 match format {
681 ReturnFormat::Nested => value,
682 ReturnFormat::Flat => {
683 let mut result = serde_json::Map::new();
684 Self::flatten_object("", &value, &mut result);
685 Value::Object(result)
686 }
687 ReturnFormat::Array => {
688 if let Value::Object(map) = value {
689 Value::Array(map.values().cloned().collect())
690 } else if let Value::Array(arr) = value {
691 Value::Array(arr)
692 } else {
693 Value::Array(vec![value])
694 }
695 }
696 }
697 }
698
699 pub fn get_field_options(&mut self, field_path: &str) -> Option<Value> {
708 let schema_ptr = if field_path.starts_with('#') || field_path.starts_with('/') {
710 path_utils::normalize_to_json_pointer(field_path).into_owned()
711 } else {
712 path_utils::dot_notation_to_schema_pointer(field_path)
713 };
714
715 let options_schema_key = format!("{}/options", schema_ptr);
717 let options_pointer = path_utils::normalize_to_json_pointer(&options_schema_key).into_owned();
718
719 let options_node = self.evaluated_schema.pointer(&options_pointer)?.clone();
721
722 if let Value::Object(ref map) = options_node {
724 if map.contains_key("$evaluation") {
725 let eval_key = options_schema_key.clone();
726
727 if let Some(logic_id) = self.evaluations.get(&eval_key).copied() {
728 let snap = self.eval_data.snapshot_data();
729 if let Ok(result) = self.engine.run(&logic_id, &*snap) {
730 let cleaned = clean_float_noise_scalar(result);
731 if let Some(node) = self.evaluated_schema.pointer_mut(&options_pointer) {
732 *node = cleaned.clone();
733 }
734 return Some(cleaned);
735 }
736 }
737 return None;
739 }
740 }
741
742 let url_pointer = path_utils::normalize_to_json_pointer(
744 &format!("{}/options/url", schema_ptr)
745 ).into_owned();
746
747 let templates = self.options_templates.clone();
748 for (tmpl_url_path, tmpl_str, tmpl_params_path) in templates.iter() {
749 if *tmpl_url_path == url_pointer {
750 if let Some(params) = self.evaluated_schema.pointer(tmpl_params_path) {
751 let params = params.clone();
752 if let Ok(resolved_url) = self.evaluate_template(tmpl_str, ¶ms) {
753 if let Some(target) = self.evaluated_schema.pointer_mut(&url_pointer) {
754 *target = Value::String(resolved_url);
755 }
756 return self.evaluated_schema.pointer(&options_pointer).cloned();
757 }
758 }
759 break;
760 }
761 }
762
763 Some(options_node)
765 }
766}