1use super::JSONEval;
2use crate::jsoneval::cancellation::CancellationToken;
3use crate::jsoneval::json_parser;
4use crate::jsoneval::path_utils;
5use crate::jsoneval::path_utils::get_value_by_pointer_without_properties;
6use crate::jsoneval::path_utils::normalize_to_json_pointer;
7use crate::jsoneval::types::DependentItem;
8use crate::rlogic::{LogicId, RLogic};
9use crate::utils::clean_float_noise_scalar;
10use crate::EvalData;
11
12use indexmap::{IndexMap, IndexSet};
13use serde_json::Value;
14
15impl JSONEval {
16 pub fn evaluate_dependents(
20 &mut self,
21 changed_paths: &[String],
22 data: Option<&str>,
23 context: Option<&str>,
24 re_evaluate: bool,
25 token: Option<&CancellationToken>,
26 mut canceled_paths: Option<&mut Vec<String>>,
27 include_subforms: bool,
28 ) -> Result<Value, String> {
29 if let Some(t) = token {
30 if t.is_cancelled() {
31 return Err("Cancelled".to_string());
32 }
33 }
34 let _lock = self.eval_lock.lock().unwrap();
35
36 if let Some(data_str) = data {
38 let data_value = json_parser::parse_json_str(data_str)?;
39 let context_value = if let Some(ctx) = context {
40 json_parser::parse_json_str(ctx)?
41 } else {
42 Value::Object(serde_json::Map::new())
43 };
44 let old_data = self.eval_data.snapshot_data_clone();
45 self.eval_data
46 .replace_data_and_context(data_value, context_value);
47 let new_data = self.eval_data.snapshot_data_clone();
48 self.eval_cache
49 .store_snapshot_and_diff_versions(&old_data, &new_data);
50 }
51
52 let mut result = Vec::new();
53 let mut processed = IndexSet::new();
54 let mut to_process: Vec<(String, bool)> = changed_paths
55 .iter()
56 .map(|path| (path_utils::dot_notation_to_schema_pointer(path), false))
57 .collect();
58
59 Self::process_dependents_queue(
60 &self.engine,
61 &self.evaluations,
62 &mut self.eval_data,
63 &mut self.eval_cache,
64 &self.dependents_evaluations,
65 &self.evaluated_schema,
66 &mut to_process,
67 &mut processed,
68 &mut result,
69 token,
70 canceled_paths.as_mut().map(|v| &mut **v),
71 )?;
72
73 drop(_lock);
75
76 if re_evaluate {
77 self.run_re_evaluate_pass(
78 token,
79 &mut to_process,
80 &mut processed,
81 &mut result,
82 canceled_paths.as_mut().map(|v| &mut **v),
83 )?;
84 }
85
86 if include_subforms {
87 self.run_subform_pass(changed_paths, re_evaluate, token, &mut result)?;
88 }
89
90 let deduped = {
95 let mut seen: IndexMap<String, usize> = IndexMap::new();
96 for (i, item) in result.iter().enumerate() {
97 if let Some(r) = item.get("$ref").and_then(|v| v.as_str()) {
98 seen.insert(r.to_string(), i);
99 }
100 }
101 let last_indices: IndexSet<usize> = seen.values().copied().collect();
102 let out: Vec<Value> = result
103 .into_iter()
104 .enumerate()
105 .filter(|(i, _)| last_indices.contains(i))
106 .map(|(_, item)| item)
107 .collect();
108 out
109 };
110
111 if self.eval_cache.active_item_index.is_none() {
119 let current_snapshot = self.eval_data.snapshot_data_clone();
120 self.eval_cache.main_form_snapshot = Some(current_snapshot);
121 }
122
123 Ok(Value::Array(deduped))
124 }
125
126 fn run_re_evaluate_pass(
129 &mut self,
130 token: Option<&CancellationToken>,
131 to_process: &mut Vec<(String, bool)>,
132 processed: &mut IndexSet<String>,
133 result: &mut Vec<Value>,
134 mut canceled_paths: Option<&mut Vec<String>>,
135 ) -> Result<(), String> {
136 self.run_schema_default_value_pass(
138 token,
139 to_process,
140 processed,
141 result,
142 canceled_paths.as_mut().map(|v| &mut **v),
143 )?;
144
145 let pre_eval_versions = if let Some(idx) = self.eval_cache.active_item_index {
151 self.eval_cache
152 .subform_caches
153 .get(&idx)
154 .map(|c| c.data_versions.clone())
155 .unwrap_or_else(|| self.eval_cache.data_versions.clone())
156 } else {
157 self.eval_cache.data_versions.clone()
158 };
159
160 self.evaluate_internal(None, token)?;
161
162 self.run_schema_default_value_pass(
164 token,
165 to_process,
166 processed,
167 result,
168 canceled_paths.as_mut().map(|v| &mut **v),
169 )?;
170
171 let active_idx = self.eval_cache.active_item_index;
175 for eval_key in self.sorted_evaluations.iter().flatten() {
176 if eval_key.contains("/$params/") || eval_key.contains("/$") {
177 continue;
178 }
179
180 let schema_ptr = path_utils::normalize_to_json_pointer(eval_key);
181 let data_path = schema_ptr
182 .replace("/properties/", "/")
183 .trim_start_matches('#')
184 .trim_start_matches('/')
185 .to_string();
186
187 let version_path = format!("/{}", data_path);
188 let old_ver = pre_eval_versions.get(&version_path);
189 let new_ver = if let Some(idx) = active_idx {
190 self.eval_cache
191 .subform_caches
192 .get(&idx)
193 .map(|c| c.data_versions.get(&version_path))
194 .unwrap_or_else(|| self.eval_cache.data_versions.get(&version_path))
195 } else {
196 self.eval_cache.data_versions.get(&version_path)
197 };
198
199 if new_ver > old_ver {
200 if let Some(new_val) = self.evaluated_schema.pointer(&schema_ptr) {
201 let dot_path = data_path.trim_end_matches("/value").replace('/', ".");
202 let mut obj = serde_json::Map::new();
203 obj.insert("$ref".to_string(), Value::String(dot_path));
204 obj.insert("value".to_string(), new_val.clone());
205 result.push(Value::Object(obj));
206 }
207 }
208 }
209
210 let _lock = self.eval_lock.lock().unwrap();
212
213 let mut readonly_changes = Vec::new();
215 let mut readonly_values = Vec::new();
216 for path in self.conditional_readonly_fields.iter() {
217 let normalized = path_utils::normalize_to_json_pointer(path);
218 if let Some(schema_el) = self.evaluated_schema.pointer(&normalized) {
219 self.check_readonly_for_dependents(
220 schema_el,
221 path,
222 &mut readonly_changes,
223 &mut readonly_values,
224 );
225 }
226 }
227 for (path, schema_value) in readonly_changes {
228 let data_path = path_utils::normalize_to_json_pointer(&path)
229 .replace("/properties/", "/")
230 .trim_start_matches('#')
231 .to_string();
232 self.eval_data.set(&data_path, schema_value.clone());
233 self.eval_cache.bump_data_version(&data_path);
234 to_process.push((path, true));
235 }
236 for (path, schema_value) in readonly_values {
237 let data_path = path_utils::normalize_to_json_pointer(&path)
238 .replace("/properties/", "/")
239 .trim_start_matches('#')
240 .to_string();
241 let mut obj = serde_json::Map::new();
242 obj.insert(
243 "$ref".to_string(),
244 Value::String(path_utils::pointer_to_dot_notation(&data_path)),
245 );
246 obj.insert("$readonly".to_string(), Value::Bool(true));
247 obj.insert("value".to_string(), schema_value);
248 result.push(Value::Object(obj));
249 }
250
251 let had_readonly_changes = !to_process.is_empty();
258 if had_readonly_changes {
259 if let Some(active_idx) = self.eval_cache.active_item_index {
260 let params_table_keys: Vec<String> = self
261 .table_metadata
262 .keys()
263 .filter(|k| k.starts_with("#/$params"))
264 .cloned()
265 .collect();
266 if !params_table_keys.is_empty() {
267 self.eval_cache
268 .invalidate_params_tables_for_item(active_idx, ¶ms_table_keys);
269 }
270 drop(_lock);
271 self.evaluate_internal(None, token)?;
272 }
276 }
277
278 if !to_process.is_empty() {
279 Self::process_dependents_queue(
280 &self.engine,
281 &self.evaluations,
282 &mut self.eval_data,
283 &mut self.eval_cache,
284 &self.dependents_evaluations,
285 &self.evaluated_schema,
286 to_process,
287 processed,
288 result,
289 token,
290 canceled_paths.as_mut().map(|v| &mut **v),
291 )?;
292 }
293
294 let mut hidden_fields = Vec::new();
296 for path in self.conditional_hidden_fields.iter() {
297 let normalized = path_utils::normalize_to_json_pointer(path);
298 if let Some(schema_el) = self.evaluated_schema.pointer(&normalized) {
299 self.check_hidden_field(schema_el, path, &mut hidden_fields);
300 }
301 }
302 if !hidden_fields.is_empty() {
303 Self::recursive_hide_effect(
304 &self.engine,
305 &self.evaluations,
306 &self.reffed_by,
307 &mut self.eval_data,
308 &mut self.eval_cache,
309 hidden_fields,
310 to_process,
311 result,
312 );
313 }
314 if !to_process.is_empty() {
315 Self::process_dependents_queue(
316 &self.engine,
317 &self.evaluations,
318 &mut self.eval_data,
319 &mut self.eval_cache,
320 &self.dependents_evaluations,
321 &self.evaluated_schema,
322 to_process,
323 processed,
324 result,
325 token,
326 canceled_paths.as_mut().map(|v| &mut **v),
327 )?;
328 }
329
330 Ok(())
331 }
332
333 fn run_schema_default_value_pass(
336 &mut self,
337 token: Option<&CancellationToken>,
338 to_process: &mut Vec<(String, bool)>,
339 processed: &mut IndexSet<String>,
340 result: &mut Vec<Value>,
341 mut canceled_paths: Option<&mut Vec<String>>,
342 ) -> Result<(), String> {
343 let mut default_value_changes = Vec::new();
344 let schema_values = self.get_schema_value_array();
345
346 if let Value::Array(values) = schema_values {
347 for item in values {
348 if let Value::Object(map) = item {
349 if let (Some(Value::String(dot_path)), Some(schema_val)) =
350 (map.get("path"), map.get("value"))
351 {
352 let schema_ptr = path_utils::dot_notation_to_schema_pointer(dot_path);
353 if let Some(Value::Object(schema_node)) = self
354 .evaluated_schema
355 .pointer(schema_ptr.trim_start_matches('#'))
356 {
357 if let Some(Value::Object(condition)) = schema_node.get("condition") {
358 if let Some(hidden_val) = condition.get("hidden") {
359 if !hidden_val.is_boolean()
361 || hidden_val.as_bool() == Some(true)
362 {
363 continue;
364 }
365 }
366 }
367 }
368
369 let data_path = dot_path.replace('.', "/");
370 let current_data = self
371 .eval_data
372 .data()
373 .pointer(&format!("/{}", data_path))
374 .unwrap_or(&Value::Null);
375
376 let is_empty = match current_data {
377 Value::Null => true,
378 Value::String(s) if s.is_empty() => true,
379 _ => false,
380 };
381
382 let is_schema_val_empty = match schema_val {
383 Value::Null => true,
384 Value::String(s) if s.is_empty() => true,
385 Value::Object(map) if map.contains_key("$evaluation") => true,
386 _ => false,
387 };
388
389 if is_empty && !is_schema_val_empty && current_data != schema_val {
390 default_value_changes.push((
391 data_path,
392 schema_val.clone(),
393 dot_path.clone(),
394 ));
395 }
396 }
397 }
398 }
399 }
400
401 let mut has_changes = false;
402 for (data_path, schema_val, dot_path) in default_value_changes {
403 self.eval_data
404 .set(&format!("/{}", data_path), schema_val.clone());
405 self.eval_cache
406 .bump_data_version(&format!("/{}", data_path));
407
408 let mut change_obj = serde_json::Map::new();
409 change_obj.insert("$ref".to_string(), Value::String(dot_path));
410 change_obj.insert("value".to_string(), schema_val);
411 result.push(Value::Object(change_obj));
412
413 let schema_ptr = format!("#/{}", data_path.replace('/', "/properties/"));
414 to_process.push((schema_ptr, true));
415 has_changes = true;
416 }
417
418 if has_changes {
419 Self::process_dependents_queue(
420 &self.engine,
421 &self.evaluations,
422 &mut self.eval_data,
423 &mut self.eval_cache,
424 &self.dependents_evaluations,
425 &self.evaluated_schema,
426 to_process,
427 processed,
428 result,
429 token,
430 canceled_paths.as_mut().map(|v| &mut **v),
431 )?;
432 }
433
434 Ok(())
435 }
436
437 fn run_subform_pass(
447 &mut self,
448 changed_paths: &[String],
449 re_evaluate: bool,
450 token: Option<&CancellationToken>,
451 result: &mut Vec<Value>,
452 ) -> Result<(), String> {
453 let subform_paths: Vec<String> = self.subforms.keys().cloned().collect();
455
456 for subform_path in subform_paths {
457 let field_key = subform_field_key(&subform_path);
458 let subform_dot_path =
460 path_utils::pointer_to_dot_notation(&subform_path).replace(".properties.", ".");
461 let field_prefix = format!("{}.", field_key);
462 let subform_ptr = normalize_to_json_pointer(&subform_path);
463
464 let item_count =
466 get_value_by_pointer_without_properties(self.eval_data.data(), &subform_ptr)
467 .and_then(|v| v.as_array())
468 .map(|a| a.len())
469 .unwrap_or(0);
470
471 if item_count == 0 {
472 continue;
473 }
474
475 self.eval_cache.prune_subform_caches(item_count);
478
479 let global_sub_re_evaluate = re_evaluate;
483
484 let parent_data_versions_snapshot = self.eval_cache.data_versions.clone();
489 let parent_params_versions_snapshot = self.eval_cache.params_versions.clone();
490
491 for idx in 0..item_count {
492 let prefix_dot = format!("{}.{}.", subform_dot_path, idx);
494 let prefix_bracket = format!("{}[{}].", subform_dot_path, idx);
495 let prefix_field_bracket = format!("{}[{}].", field_key, idx);
496
497 let item_changed_paths: Vec<String> = changed_paths
498 .iter()
499 .filter_map(|p| {
500 if p.starts_with(&prefix_bracket) {
501 Some(p.replacen(&prefix_bracket, &field_prefix, 1))
502 } else if p.starts_with(&prefix_dot) {
503 Some(p.replacen(&prefix_dot, &field_prefix, 1))
504 } else if p.starts_with(&prefix_field_bracket) {
505 Some(p.replacen(&prefix_field_bracket, &field_prefix, 1))
506 } else {
507 None
508 }
509 })
510 .collect();
511
512 let sub_re_evaluate = global_sub_re_evaluate || !item_changed_paths.is_empty();
513
514 if !sub_re_evaluate && item_changed_paths.is_empty() {
516 continue;
517 }
518
519 let item_val =
522 get_value_by_pointer_without_properties(self.eval_data.data(), &subform_ptr)
523 .and_then(|v| v.as_array())
524 .and_then(|a| a.get(idx))
525 .cloned()
526 .unwrap_or(Value::Null);
527
528 let merged_data = {
532 let parent = self.eval_data.data();
533 let mut map = serde_json::Map::new();
534 if let Value::Object(parent_map) = parent {
535 for (k, v) in parent_map {
536 if k == &field_key {
537 continue;
539 }
540 if !v.is_array() {
543 map.insert(k.clone(), v.clone());
544 }
545 }
546 }
547 map.insert(field_key.clone(), item_val.clone());
548 Value::Object(map)
549 };
550
551 let Some(subform) = self.subforms.get_mut(&subform_path) else {
552 continue;
553 };
554
555 self.eval_cache.ensure_active_item_cache(idx);
557 let old_item_val = self
558 .eval_cache
559 .subform_caches
560 .get(&idx)
561 .map(|c| c.item_snapshot.clone())
562 .unwrap_or(Value::Null);
563
564 subform.eval_data.replace_data_and_context(
565 merged_data,
566 self.eval_data
567 .data()
568 .get("$context")
569 .cloned()
570 .unwrap_or(Value::Null),
571 );
572 let new_item_val = subform
573 .eval_data
574 .data()
575 .get(&field_key)
576 .cloned()
577 .unwrap_or(Value::Null);
578
579 let mut parent_cache = std::mem::take(&mut self.eval_cache);
581 parent_cache.ensure_active_item_cache(idx);
582 if let Some(c) = parent_cache.subform_caches.get_mut(&idx) {
583 c.data_versions.merge_from(&parent_data_versions_snapshot);
587 c.data_versions
589 .merge_from_params(&parent_params_versions_snapshot);
590 crate::jsoneval::eval_cache::diff_and_update_versions(
591 &mut c.data_versions,
592 &format!("/{}", field_key),
593 &old_item_val,
594 &new_item_val,
595 );
596 c.item_snapshot = new_item_val;
597 }
598 parent_cache.set_active_item(idx);
599 std::mem::swap(&mut subform.eval_cache, &mut parent_cache);
600
601 let subform_result = subform.evaluate_dependents(
602 &item_changed_paths,
603 None,
604 None,
605 sub_re_evaluate,
606 token,
607 None,
608 false,
609 );
610
611 std::mem::swap(&mut subform.eval_cache, &mut parent_cache);
613 parent_cache.clear_active_item();
614
615 if let Some(parent_item_cache) = self.eval_cache.subform_caches.get(&idx) {
620 let snapshot = parent_item_cache.item_snapshot.clone();
621 subform
622 .eval_cache
623 .ensure_active_item_cache(idx);
624 if let Some(sub_cache) = subform.eval_cache.subform_caches.get_mut(&idx) {
625 sub_cache.item_snapshot = snapshot;
626 }
627 }
628
629 self.eval_cache = parent_cache;
630
631 if let Ok(Value::Array(changes)) = subform_result {
632 for change in changes {
633 if let Some(obj) = change.as_object() {
634 if let Some(Value::String(ref_path)) = obj.get("$ref") {
635 let new_ref = if ref_path.starts_with(&field_prefix) {
637 format!(
638 "{}.{}.{}",
639 subform_dot_path,
640 idx,
641 &ref_path[field_prefix.len()..]
642 )
643 } else {
644 format!("{}.{}.{}", subform_dot_path, idx, ref_path)
645 };
646
647 if let Some(val) = obj.get("value") {
653 let data_ptr = format!(
654 "/{}",
655 new_ref.replace('.', "/")
656 );
657 self.eval_data.set(&data_ptr, val.clone());
658 } else if obj.get("clear").and_then(Value::as_bool) == Some(true) {
659 let data_ptr = format!(
660 "/{}",
661 new_ref.replace('.', "/")
662 );
663 self.eval_data.set(&data_ptr, Value::Null);
664 }
665
666 let mut new_obj = obj.clone();
667 new_obj.insert("$ref".to_string(), Value::String(new_ref));
668 result.push(Value::Object(new_obj));
669 } else {
670 result.push(change);
672 }
673 }
674 }
675 }
676 }
677 }
678 Ok(())
679 }
680
681 pub(crate) fn evaluate_dependent_value_static(
683 engine: &RLogic,
684 evaluations: &IndexMap<String, LogicId>,
685 eval_data: &EvalData,
686 value: &Value,
687 changed_field_value: &Value,
688 changed_field_ref_value: &Value,
689 ) -> Result<Value, String> {
690 match value {
691 Value::String(eval_key) => {
693 if let Some(logic_id) = evaluations.get(eval_key) {
694 let mut internal_context = serde_json::Map::new();
697 internal_context.insert("$value".to_string(), changed_field_value.clone());
698 internal_context.insert("$refValue".to_string(), changed_field_ref_value.clone());
699 let context_value = Value::Object(internal_context);
700
701 let result = engine.run_with_context(logic_id, eval_data.data(), &context_value)
702 .map_err(|e| format!("Failed to evaluate dependent logic '{}': {}", eval_key, e))?;
703 Ok(result)
704 } else {
705 Ok(value.clone())
707 }
708 }
709 Value::Object(map) if map.contains_key("$evaluation") => {
712 Err("Dependent evaluation contains unparsed $evaluation - schema was not properly parsed".to_string())
713 }
714 _ => Ok(value.clone()),
716 }
717 }
718
719 pub(crate) fn check_readonly_for_dependents(
721 &self,
722 schema_element: &Value,
723 path: &str,
724 changes: &mut Vec<(String, Value)>,
725 all_values: &mut Vec<(String, Value)>,
726 ) {
727 match schema_element {
728 Value::Object(map) => {
729 let mut is_disabled = false;
731 if let Some(Value::Object(condition)) = map.get("condition") {
732 if let Some(Value::Bool(d)) = condition.get("disabled") {
733 is_disabled = *d;
734 }
735 }
736
737 let mut skip_readonly = false;
739 if let Some(Value::Object(config)) = map.get("config") {
740 if let Some(Value::Object(all)) = config.get("all") {
741 if let Some(Value::Bool(skip)) = all.get("skipReadOnlyValue") {
742 skip_readonly = *skip;
743 }
744 }
745 }
746
747 if is_disabled && !skip_readonly {
748 if let Some(schema_value) = map.get("value") {
749 let data_path = path_utils::normalize_to_json_pointer(path)
750 .replace("/properties/", "/")
751 .trim_start_matches('#')
752 .to_string();
753
754 let current_data = self
755 .eval_data
756 .data()
757 .pointer(&data_path)
758 .unwrap_or(&Value::Null);
759
760 all_values.push((path.to_string(), schema_value.clone()));
762
763 if current_data != schema_value {
765 changes.push((path.to_string(), schema_value.clone()));
766 }
767 }
768 }
769 }
770 _ => {}
771 }
772 }
773
774 #[allow(dead_code)]
776 pub(crate) fn collect_readonly_fixes(
777 &self,
778 schema_element: &Value,
779 path: &str,
780 changes: &mut Vec<(String, Value)>,
781 ) {
782 match schema_element {
783 Value::Object(map) => {
784 let mut is_disabled = false;
786 if let Some(Value::Object(condition)) = map.get("condition") {
787 if let Some(Value::Bool(d)) = condition.get("disabled") {
788 is_disabled = *d;
789 }
790 }
791
792 let mut skip_readonly = false;
794 if let Some(Value::Object(config)) = map.get("config") {
795 if let Some(Value::Object(all)) = config.get("all") {
796 if let Some(Value::Bool(skip)) = all.get("skipReadOnlyValue") {
797 skip_readonly = *skip;
798 }
799 }
800 }
801
802 if is_disabled && !skip_readonly {
803 if let Some(schema_value) = map.get("value") {
807 let data_path = path_utils::normalize_to_json_pointer(path)
808 .replace("/properties/", "/")
809 .trim_start_matches('#')
810 .to_string();
811
812 let current_data = self
813 .eval_data
814 .data()
815 .pointer(&data_path)
816 .unwrap_or(&Value::Null);
817
818 if current_data != schema_value {
819 changes.push((path.to_string(), schema_value.clone()));
820 }
821 }
822 }
823
824 if let Some(Value::Object(props)) = map.get("properties") {
826 for (key, val) in props {
827 let next_path = if path == "#" {
828 format!("#/properties/{}", key)
829 } else {
830 format!("{}/properties/{}", path, key)
831 };
832 self.collect_readonly_fixes(val, &next_path, changes);
833 }
834 }
835 }
836 _ => {}
837 }
838 }
839
840 pub(crate) fn check_hidden_field(
842 &self,
843 schema_element: &Value,
844 path: &str,
845 hidden_fields: &mut Vec<String>,
846 ) {
847 match schema_element {
848 Value::Object(map) => {
849 let mut is_hidden = false;
851 if let Some(Value::Object(condition)) = map.get("condition") {
852 if let Some(Value::Bool(h)) = condition.get("hidden") {
853 is_hidden = *h;
854 }
855 }
856
857 let mut keep_hidden = false;
859 if let Some(Value::Object(config)) = map.get("config") {
860 if let Some(Value::Object(all)) = config.get("all") {
861 if let Some(Value::Bool(keep)) = all.get("keepHiddenValue") {
862 keep_hidden = *keep;
863 }
864 }
865 }
866
867 if is_hidden && !keep_hidden {
868 let data_path = path_utils::normalize_to_json_pointer(path)
869 .replace("/properties/", "/")
870 .trim_start_matches('#')
871 .to_string();
872
873 let current_data = self
874 .eval_data
875 .data()
876 .pointer(&data_path)
877 .unwrap_or(&Value::Null);
878
879 if current_data != &Value::Null && current_data != "" {
881 hidden_fields.push(path.to_string());
882 }
883 }
884 }
885 _ => {}
886 }
887 }
888
889 #[allow(dead_code)]
891 pub(crate) fn collect_hidden_fields(
892 &self,
893 schema_element: &Value,
894 path: &str,
895 hidden_fields: &mut Vec<String>,
896 ) {
897 match schema_element {
898 Value::Object(map) => {
899 let mut is_hidden = false;
901 if let Some(Value::Object(condition)) = map.get("condition") {
902 if let Some(Value::Bool(h)) = condition.get("hidden") {
903 is_hidden = *h;
904 }
905 }
906
907 let mut keep_hidden = false;
909 if let Some(Value::Object(config)) = map.get("config") {
910 if let Some(Value::Object(all)) = config.get("all") {
911 if let Some(Value::Bool(keep)) = all.get("keepHiddenValue") {
912 keep_hidden = *keep;
913 }
914 }
915 }
916
917 if is_hidden && !keep_hidden {
918 let data_path = path_utils::normalize_to_json_pointer(path)
919 .replace("/properties/", "/")
920 .trim_start_matches('#')
921 .to_string();
922
923 let current_data = self
924 .eval_data
925 .data()
926 .pointer(&data_path)
927 .unwrap_or(&Value::Null);
928
929 if current_data != &Value::Null && current_data != "" {
931 hidden_fields.push(path.to_string());
932 }
933 }
934
935 for (key, val) in map {
937 if key == "properties" {
938 if let Value::Object(props) = val {
939 for (p_key, p_val) in props {
940 let next_path = if path == "#" {
941 format!("#/properties/{}", p_key)
942 } else {
943 format!("{}/properties/{}", path, p_key)
944 };
945 self.collect_hidden_fields(p_val, &next_path, hidden_fields);
946 }
947 }
948 } else if let Value::Object(_) = val {
949 if key == "condition"
951 || key == "config"
952 || key == "rules"
953 || key == "dependents"
954 || key == "hideLayout"
955 || key == "$layout"
956 || key == "$params"
957 || key == "definitions"
958 || key == "$defs"
959 || key.starts_with('$')
960 {
961 continue;
962 }
963
964 let next_path = if path == "#" {
965 format!("#/{}", key)
966 } else {
967 format!("{}/{}", path, key)
968 };
969 self.collect_hidden_fields(val, &next_path, hidden_fields);
970 }
971 }
972 }
973 _ => {}
974 }
975 }
976
977 pub(crate) fn recursive_hide_effect(
980 engine: &RLogic,
981 evaluations: &IndexMap<String, LogicId>,
982 reffed_by: &IndexMap<String, Vec<String>>,
983 eval_data: &mut EvalData,
984 eval_cache: &mut crate::jsoneval::eval_cache::EvalCache,
985 mut hidden_fields: Vec<String>,
986 queue: &mut Vec<(String, bool)>,
987 result: &mut Vec<Value>,
988 ) {
989 while let Some(hf) = hidden_fields.pop() {
990 let data_path = path_utils::normalize_to_json_pointer(&hf)
991 .replace("/properties/", "/")
992 .trim_start_matches('#')
993 .to_string();
994
995 eval_data.set(&data_path, Value::Null);
997 eval_cache.bump_data_version(&data_path);
998
999 let mut change_obj = serde_json::Map::new();
1001 change_obj.insert(
1002 "$ref".to_string(),
1003 Value::String(path_utils::pointer_to_dot_notation(&data_path)),
1004 );
1005 change_obj.insert("$hidden".to_string(), Value::Bool(true));
1006 change_obj.insert("clear".to_string(), Value::Bool(true));
1007 result.push(Value::Object(change_obj));
1008
1009 queue.push((hf.clone(), true));
1011
1012 if let Some(referencing_fields) = reffed_by.get(&data_path) {
1014 for rb in referencing_fields {
1015 let hidden_eval_key = format!("{}/condition/hidden", rb);
1019
1020 if let Some(logic_id) = evaluations.get(&hidden_eval_key) {
1021 let rb_data_path = path_utils::normalize_to_json_pointer(rb)
1028 .replace("/properties/", "/")
1029 .trim_start_matches('#')
1030 .to_string();
1031 let rb_value = eval_data
1032 .data()
1033 .pointer(&rb_data_path)
1034 .cloned()
1035 .unwrap_or(Value::Null);
1036
1037 if let Ok(Value::Bool(is_hidden)) = engine.run(logic_id, eval_data.data()) {
1039 if is_hidden {
1040 if !hidden_fields.contains(rb) {
1043 let has_value = rb_value != Value::Null && rb_value != "";
1044 if has_value {
1045 hidden_fields.push(rb.clone());
1046 }
1047 }
1048 }
1049 }
1050 }
1051 }
1052 }
1053 }
1054 }
1055
1056 pub(crate) fn process_dependents_queue(
1059 engine: &RLogic,
1060 evaluations: &IndexMap<String, LogicId>,
1061 eval_data: &mut EvalData,
1062 eval_cache: &mut crate::jsoneval::eval_cache::EvalCache,
1063 dependents_evaluations: &IndexMap<String, Vec<DependentItem>>,
1064 evaluated_schema: &Value,
1065 queue: &mut Vec<(String, bool)>,
1066 processed: &mut IndexSet<String>,
1067 result: &mut Vec<Value>,
1068 token: Option<&CancellationToken>,
1069 canceled_paths: Option<&mut Vec<String>>,
1070 ) -> Result<(), String> {
1071 while let Some((current_path, is_transitive)) = queue.pop() {
1072 if let Some(t) = token {
1073 if t.is_cancelled() {
1074 if let Some(cp) = canceled_paths {
1076 cp.push(current_path.clone());
1077 for (path, _) in queue.iter() {
1083 cp.push(path.clone());
1084 }
1085 }
1086 return Err("Cancelled".to_string());
1087 }
1088 }
1089 if processed.contains(¤t_path) {
1090 continue;
1091 }
1092 processed.insert(current_path.clone());
1093
1094 let current_data_path = path_utils::normalize_to_json_pointer(¤t_path)
1096 .replace("/properties/", "/")
1097 .trim_start_matches('#')
1098 .to_string();
1099 let mut current_value = eval_data
1100 .data()
1101 .pointer(¤t_data_path)
1102 .cloned()
1103 .unwrap_or(Value::Null);
1104
1105 if let Some(dependent_items) = dependents_evaluations.get(¤t_path) {
1107 for dep_item in dependent_items {
1108 let ref_path = &dep_item.ref_path;
1109 let pointer_path = path_utils::normalize_to_json_pointer(ref_path);
1110 let data_path = pointer_path.replace("/properties/", "/");
1112
1113 let current_ref_value = eval_data
1114 .data()
1115 .pointer(&data_path)
1116 .cloned()
1117 .unwrap_or(Value::Null);
1118
1119 let field = evaluated_schema.pointer(&pointer_path).cloned();
1121
1122 let parent_path = if let Some(last_slash) = pointer_path.rfind("/properties") {
1124 &pointer_path[..last_slash]
1125 } else {
1126 "/"
1127 };
1128 let mut parent_field = if parent_path.is_empty() || parent_path == "/" {
1129 evaluated_schema.clone()
1130 } else {
1131 evaluated_schema
1132 .pointer(parent_path)
1133 .cloned()
1134 .unwrap_or_else(|| Value::Object(serde_json::Map::new()))
1135 };
1136
1137 if let Value::Object(ref mut map) = parent_field {
1139 map.remove("properties");
1140 map.remove("$layout");
1141 }
1142
1143 let mut change_obj = serde_json::Map::new();
1144 change_obj.insert(
1145 "$ref".to_string(),
1146 Value::String(path_utils::pointer_to_dot_notation(&data_path)),
1147 );
1148 if let Some(f) = field {
1149 change_obj.insert("$field".to_string(), f);
1150 }
1151 change_obj.insert("$parentField".to_string(), parent_field);
1152 change_obj.insert("transitive".to_string(), Value::Bool(is_transitive));
1153
1154 let mut add_transitive = false;
1155 let mut add_deps = false;
1156 if let Some(clear_val) = &dep_item.clear {
1158 let should_clear = Self::evaluate_dependent_value_static(
1159 engine,
1160 evaluations,
1161 eval_data,
1162 clear_val,
1163 ¤t_value,
1164 ¤t_ref_value,
1165 )?;
1166 let clear_bool = match should_clear {
1167 Value::Bool(b) => b,
1168 _ => false,
1169 };
1170
1171 if clear_bool {
1172 if data_path == current_data_path {
1173 current_value = Value::Null;
1174 }
1175 eval_data.set(&data_path, Value::Null);
1176 eval_cache.bump_data_version(&data_path);
1177 change_obj.insert("clear".to_string(), Value::Bool(true));
1178 add_transitive = true;
1179 add_deps = true;
1180 }
1181 }
1182
1183 if let Some(value_val) = &dep_item.value {
1185 let computed_value = Self::evaluate_dependent_value_static(
1186 engine,
1187 evaluations,
1188 eval_data,
1189 value_val,
1190 ¤t_value,
1191 ¤t_ref_value,
1192 )?;
1193 let cleaned_val = clean_float_noise_scalar(computed_value);
1194
1195 if cleaned_val != current_ref_value && cleaned_val != Value::Null {
1196 if data_path == current_data_path {
1197 current_value = cleaned_val.clone();
1198 }
1199 eval_data.set(&data_path, cleaned_val.clone());
1200 eval_cache.bump_data_version(&data_path);
1201 change_obj.insert("value".to_string(), cleaned_val);
1202 add_transitive = true;
1203 add_deps = true;
1204 }
1205 }
1206
1207 if add_deps {
1209 result.push(Value::Object(change_obj));
1210 }
1211
1212 if add_transitive {
1214 queue.push((ref_path.clone(), true));
1215 }
1216 }
1217 }
1218 }
1219 Ok(())
1220 }
1221}
1222
1223fn subform_field_key(subform_path: &str) -> String {
1230 let stripped = subform_path.trim_start_matches('#').trim_start_matches('/');
1232
1233 stripped
1235 .split('/')
1236 .filter(|seg| !seg.is_empty() && *seg != "properties")
1237 .last()
1238 .unwrap_or(stripped)
1239 .to_string()
1240}