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::time_block;
10use crate::utils::clean_float_noise_scalar;
11use crate::EvalData;
12
13use indexmap::{IndexMap, IndexSet};
14use serde_json::Value;
15
16impl JSONEval {
17 pub fn evaluate_dependents(
21 &mut self,
22 changed_paths: &[String],
23 data: Option<&str>,
24 context: Option<&str>,
25 re_evaluate: bool,
26 token: Option<&CancellationToken>,
27 mut canceled_paths: Option<&mut Vec<String>>,
28 include_subforms: bool,
29 ) -> Result<Value, String> {
30 if let Some(t) = token {
31 if t.is_cancelled() {
32 return Err("Cancelled".to_string());
33 }
34 }
35 let _lock = self.eval_lock.lock().unwrap();
36 let mut structural_change_data = None;
37
38 if let Some(data_str) = data {
40 let data_value = json_parser::parse_json_str(data_str)?;
41 let context_value = if let Some(ctx) = context {
42 json_parser::parse_json_str(ctx)?
43 } else {
44 Value::Object(serde_json::Map::new())
45 };
46 let old_data = self.eval_data.snapshot_data_clone();
47 time_block!(" [dep] data_replace_and_context", {
48 self.eval_data
49 .replace_data_and_context(data_value, context_value);
50 });
51 let new_data = self.eval_data.snapshot_data_clone();
52 time_block!(" [dep] data_diff_versions", {
53 self.eval_cache
54 .store_snapshot_and_diff_versions(&old_data, &new_data);
55 });
56 structural_change_data = Some((old_data, new_data));
57 }
58
59 drop(_lock);
61
62 if let Some((old_data, new_data)) = structural_change_data {
66 time_block!(" [dep] invalidate_subform_structural", {
67 self.invalidate_subform_caches_on_structural_change(&old_data, &new_data);
68 });
69 }
70
71 let mut result = Vec::new();
72 let mut processed = std::collections::HashMap::new();
73 let mut to_process: Vec<(String, bool, Option<Vec<usize>>)> = changed_paths
74 .iter()
75 .map(|path| {
76 (
77 path_utils::dot_notation_to_schema_pointer(path),
78 false,
79 None,
80 )
81 })
82 .collect();
83
84 time_block!(" [dep] process_dependents_queue", {
85 Self::process_dependents_queue(
86 &self.engine,
87 &self.evaluations,
88 &mut self.eval_data,
89 &mut self.eval_cache,
90 &self.dependents_evaluations,
91 &self.dep_formula_triggers,
92 &self.evaluated_schema,
93 &mut to_process,
94 &mut processed,
95 &mut result,
96 token,
97 canceled_paths.as_mut().map(|v| &mut **v),
98 )?;
99 });
100
101 if re_evaluate {
102 time_block!(" [dep] run_re_evaluate_pass", {
103 self.run_re_evaluate_pass(
104 token,
105 &mut to_process,
106 &mut processed,
107 &mut result,
108 canceled_paths.as_mut().map(|v| &mut **v),
109 )?;
110 });
111 }
112
113 if include_subforms {
114 let collection_refresh_paths: Vec<String> = self
118 .subforms
119 .keys()
120 .filter_map(|subform_path| {
121 let dot_path = path_utils::pointer_to_dot_notation(subform_path)
122 .replace(".properties.", ".");
123 let collection_changed = changed_paths
124 .iter()
125 .any(|path| path == &dot_path || path == &subform_field_key(subform_path));
126 collection_changed.then(|| {
127 let data_path = path_utils::schema_path_to_data_pointer(subform_path);
128 self.eval_data
129 .data()
130 .pointer(&data_path)
131 .and_then(Value::as_array)
132 .map(|items| {
133 (0..items.len())
134 .map(|idx| format!("{dot_path}.{idx}"))
135 .collect::<Vec<_>>()
136 })
137 })
138 })
139 .flatten()
140 .flatten()
141 .collect();
142
143 let extended_paths: Vec<String> = {
149 let mut paths = changed_paths.to_vec();
150 for item in &result {
151 if let Some(ref_val) = item.get("$ref").and_then(|v| v.as_str()) {
152 let s = ref_val.to_string();
153 if !paths.contains(&s) {
154 paths.push(s);
155 }
156 }
157 }
158 paths.extend(collection_refresh_paths);
159 paths
160 };
161 let subform_invalidated_tables = time_block!(" [dep] run_subform_pass", {
162 self.run_subform_pass(
163 &extended_paths,
164 changed_paths,
165 re_evaluate,
166 token,
167 &mut result,
168 )
169 })?;
170
171 if subform_invalidated_tables {
179 let _lock2 = self.eval_lock.lock().unwrap();
180 drop(_lock2);
181 self.evaluate_internal(None, token)?;
182
183 self.run_subform_pass(&[], &[], true, token, &mut result)?;
188
189 for (subform_path, _) in &self.subforms {
191 let data_ptr = path_utils::schema_path_to_data_pointer(subform_path);
192 let data_ptr_str = data_ptr.to_string();
193 let dot_path = data_ptr_str.trim_start_matches('/').replace('/', ".");
194
195 if let Some(fresh_val) = self.eval_data.get(&data_ptr_str) {
196 let mut patched = false;
197 for item in result.iter_mut() {
198 if item
199 .get("$ref")
200 .and_then(|r| r.as_str())
201 .map(|r| r == dot_path)
202 .unwrap_or(false)
203 {
204 if let Some(map) = item.as_object_mut() {
205 map.remove("clear");
206 map.insert("value".to_string(), fresh_val.clone());
207 }
208 patched = true;
209 break;
210 }
211 }
212 if !patched {
213 let mut obj = serde_json::Map::new();
214 obj.insert("$ref".to_string(), serde_json::Value::String(dot_path));
215 obj.insert("value".to_string(), fresh_val.clone());
216 result.push(serde_json::Value::Object(obj));
217 }
218 }
219 }
220 }
221 }
222
223 let deduped = {
228 let mut seen: IndexMap<String, usize> = IndexMap::new();
229 for (i, item) in result.iter().enumerate() {
230 if let Some(r) = item.get("$ref").and_then(|v| v.as_str()) {
231 seen.insert(r.to_string(), i);
232 }
233 }
234 let last_indices: IndexSet<usize> = seen.values().copied().collect();
235 let out: Vec<Value> = result
236 .into_iter()
237 .enumerate()
238 .filter(|(i, _)| last_indices.contains(i))
239 .map(|(_, item)| item)
240 .collect();
241 out
242 };
243
244 if self.eval_cache.active_item_index.is_none() {
252 let current_snapshot = self.eval_data.snapshot_data_clone();
253 self.eval_cache.main_form_snapshot = Some(current_snapshot);
254 }
255
256 Ok(Value::Array(deduped))
257 }
258
259 fn run_re_evaluate_pass(
262 &mut self,
263 token: Option<&CancellationToken>,
264 to_process: &mut Vec<(String, bool, Option<Vec<usize>>)>,
265 processed: &mut std::collections::HashMap<String, Option<std::collections::HashSet<usize>>>,
266 result: &mut Vec<Value>,
267 mut canceled_paths: Option<&mut Vec<String>>,
268 ) -> Result<(), String> {
269 self.run_schema_default_value_pass(
271 token,
272 to_process,
273 processed,
274 result,
275 canceled_paths.as_mut().map(|v| &mut **v),
276 )?;
277
278 let pre_eval_versions = if let Some(idx) = self.eval_cache.active_item_index {
284 self.eval_cache
285 .subform_caches
286 .get(&idx)
287 .map(|c| c.data_versions.clone())
288 .unwrap_or_else(|| self.eval_cache.data_versions.clone())
289 } else {
290 self.eval_cache.data_versions.clone()
291 };
292
293 self.evaluate_internal(None, token)?;
294
295 let active_idx = self.eval_cache.active_item_index;
297 for eval_key in self.sorted_evaluations.iter().flatten() {
298 if eval_key.contains("/$params/") || eval_key.contains("/$") {
299 continue;
300 }
301
302 let schema_ptr = path_utils::schema_path_to_data_pointer(eval_key);
303 let data_path = schema_ptr.trim_start_matches('/').to_string();
304
305 let version_path = format!("/{}", data_path);
306 let old_ver = pre_eval_versions.get(&version_path);
307 let new_ver = if let Some(idx) = active_idx {
308 self.eval_cache
309 .subform_caches
310 .get(&idx)
311 .map(|c| c.data_versions.get(&version_path))
312 .unwrap_or_else(|| self.eval_cache.data_versions.get(&version_path))
313 } else {
314 self.eval_cache.data_versions.get(&version_path)
315 };
316
317 if new_ver > old_ver {
318 if let Some(new_val) = self.evaluated_schema.pointer(&schema_ptr) {
319 let dot_path = data_path.trim_end_matches("/value").replace('/', ".");
320 let mut obj = serde_json::Map::new();
321 obj.insert("$ref".to_string(), Value::String(dot_path));
322 let is_clear = new_val == &Value::Null || new_val.as_str() == Some("");
323 if is_clear {
324 obj.insert("clear".to_string(), Value::Bool(true));
325 } else {
326 obj.insert("value".to_string(), new_val.clone());
327 }
328 result.push(Value::Object(obj));
329 }
330 }
331 }
332
333 let mut readonly_changes = Vec::new();
335 let mut readonly_values = Vec::new();
336 for path in self.conditional_readonly_fields.iter() {
337 let normalized = path_utils::normalize_to_json_pointer(path);
338 if let Some(schema_el) = self.evaluated_schema.pointer(&normalized) {
339 self.check_readonly_for_dependents(
340 schema_el,
341 path,
342 &mut readonly_changes,
343 &mut readonly_values,
344 );
345 }
346 }
347 let had_actual_readonly_changes = !readonly_changes.is_empty();
350
351 let subform_data_paths: std::collections::HashSet<String> = self
357 .subforms
358 .keys()
359 .map(|p| {
360 path_utils::schema_path_to_data_pointer(p)
361 .replace("/value/", "/")
362 .to_string()
363 })
364 .collect();
365
366 for (path, schema_value) in readonly_changes {
367 let data_path = path_utils::schema_path_to_data_pointer(&path).replace("/value/", "/");
368
369 if subform_data_paths.contains(&data_path) {
370 if let (Value::Array(schema_items), Some(Value::Array(existing_items))) = (
372 &schema_value,
373 self.eval_data.data().pointer(&data_path).cloned().as_ref(),
374 ) {
375 let mut merged_items = existing_items.clone();
376 for (i, schema_item) in schema_items.iter().enumerate() {
377 if let (Some(existing), Value::Object(schema_map)) =
378 (merged_items.get_mut(i), schema_item)
379 {
380 if let Some(existing_map) = existing.as_object_mut() {
381 for (k, v) in schema_map {
382 if !v.is_object() {
383 existing_map.insert(k.clone(), v.clone());
384 }
385 }
386 }
387 } else if i >= merged_items.len() {
388 merged_items.push(schema_item.clone());
389 }
390 }
391 self.eval_data.set(&data_path, Value::Array(merged_items));
392 } else {
393 self.eval_data.set(&data_path, schema_value.clone());
394 }
395 self.eval_cache.bump_data_version(&data_path);
396 to_process.push((path, true, None));
397 continue;
398 }
399
400 self.eval_data.set(&data_path, schema_value.clone());
401 self.eval_cache.bump_data_version(&data_path);
402 to_process.push((path, true, None));
403 }
404 if had_actual_readonly_changes {
410 let readonly_dep_prefixes: Vec<String> = to_process
411 .iter()
412 .map(|(path, _, _)| path.trim_start_matches('#').to_string())
413 .collect();
414 let params_table_keys: Vec<String> = self
415 .table_metadata
416 .keys()
417 .filter(|key| key.starts_with("#/$params"))
418 .filter(|key| {
419 self.dependencies
420 .get(*key)
421 .map(|deps| {
422 deps.iter().any(|dep| {
423 readonly_dep_prefixes.iter().any(|readonly| {
424 dep == readonly
425 || dep
426 .strip_prefix(readonly)
427 .is_some_and(|suffix| suffix.starts_with('/'))
428 })
429 })
430 })
431 .unwrap_or(false)
432 })
433 .cloned()
434 .collect();
435
436 if !params_table_keys.is_empty() {
437 if let Some(active_idx) = self.eval_cache.active_item_index {
438 self.eval_cache
439 .invalidate_params_tables_for_item(active_idx, ¶ms_table_keys);
440 }
441 self.evaluate_internal(None, token)?;
442
443 readonly_values.clear();
447 for path in self.conditional_readonly_fields.iter() {
448 let normalized = path_utils::normalize_to_json_pointer(path);
449 if let Some(schema_el) = self.evaluated_schema.pointer(&normalized) {
450 self.check_readonly_for_dependents(
451 schema_el,
452 path,
453 &mut Vec::new(),
454 &mut readonly_values,
455 );
456 }
457 }
458 }
459 }
460
461 for (path, schema_value) in readonly_values {
462 let data_path = path_utils::schema_path_to_data_pointer(&path).replace("/value/", "/");
463 let mut obj = serde_json::Map::new();
464 obj.insert(
465 "$ref".to_string(),
466 Value::String(path_utils::pointer_to_dot_notation(&data_path)),
467 );
468 obj.insert("$readonly".to_string(), Value::Bool(true));
469 let is_clear = schema_value == Value::Null || schema_value.as_str() == Some("");
470 if is_clear {
471 obj.insert("clear".to_string(), Value::Bool(true));
472 } else {
473 obj.insert("value".to_string(), schema_value);
474 }
475 result.push(Value::Object(obj));
476 }
477
478 if !to_process.is_empty() {
479 Self::process_dependents_queue(
480 &self.engine,
481 &self.evaluations,
482 &mut self.eval_data,
483 &mut self.eval_cache,
484 &self.dependents_evaluations,
485 &self.dep_formula_triggers,
486 &self.evaluated_schema,
487 to_process,
488 processed,
489 result,
490 token,
491 canceled_paths.as_mut().map(|v| &mut **v),
492 )?;
493 }
494
495 self.resolve_layout(false)?;
500
501 let mut hidden_fields = Vec::new();
502 for path in self.conditional_hidden_fields.iter() {
503 let normalized = path_utils::normalize_to_json_pointer(path);
504 if let Some(schema_el) = self.evaluated_schema.pointer(&normalized) {
505 self.check_hidden_field(schema_el, path, &mut hidden_fields);
506 }
507 }
508 for path in self.layout_condition_hidden_refs.iter() {
509 if let Some(schema_el) = self.evaluated_schema.pointer(path) {
510 self.check_effectively_hidden_field(schema_el, path, &mut hidden_fields);
511 }
512 }
513 hidden_fields.sort();
514 hidden_fields.dedup();
515 if !hidden_fields.is_empty() {
516 Self::recursive_hide_effect(
517 &self.engine,
518 &self.evaluations,
519 &self.reffed_by,
520 &mut self.eval_data,
521 &mut self.eval_cache,
522 hidden_fields,
523 to_process,
524 result,
525 );
526 }
527 if !to_process.is_empty() {
528 Self::process_dependents_queue(
529 &self.engine,
530 &self.evaluations,
531 &mut self.eval_data,
532 &mut self.eval_cache,
533 &self.dependents_evaluations,
534 &self.dep_formula_triggers,
535 &self.evaluated_schema,
536 to_process,
537 processed,
538 result,
539 token,
540 canceled_paths.as_mut().map(|v| &mut **v),
541 )?;
542 }
543
544 Ok(())
545 }
546
547 fn collect_visible_static_defaults(&self) -> Vec<(String, Value, String)> {
549 let mut defaults = Vec::new();
550 let schema_values = self.get_schema_value_array();
551
552 if let Value::Array(values) = schema_values {
553 for item in values {
554 let Value::Object(map) = item else {
555 continue;
556 };
557 let Some(Value::String(dot_path)) = map.get("path") else {
558 continue;
559 };
560 let Some(schema_val) = map.get("value") else {
561 continue;
562 };
563
564 let schema_ptr = path_utils::dot_notation_to_schema_pointer(dot_path);
565 if let Some(Value::Object(schema_node)) = self
566 .evaluated_schema
567 .pointer(schema_ptr.trim_start_matches('#'))
568 {
569 if let Some(Value::Object(condition)) = schema_node.get("condition") {
570 if let Some(hidden_val) = condition.get("hidden") {
571 if !hidden_val.is_boolean() || hidden_val.as_bool() == Some(true) {
572 continue;
573 }
574 }
575 }
576 }
577
578 let data_path = dot_path.replace('.', "/");
579 let current_data = self
580 .eval_data
581 .data()
582 .pointer(&format!("/{}", data_path))
583 .unwrap_or(&Value::Null);
584 let is_empty = matches!(current_data, Value::Null)
585 || matches!(current_data, Value::String(s) if s.is_empty());
586 let is_schema_val_empty = matches!(schema_val, Value::Null)
587 || matches!(schema_val, Value::String(s) if s.is_empty())
588 || matches!(schema_val, Value::Object(map) if map.contains_key("$evaluation"));
589
590 if is_empty && !is_schema_val_empty && current_data != schema_val {
591 defaults.push((data_path, schema_val.clone(), dot_path.clone()));
592 }
593 }
594 }
595
596 defaults
597 }
598
599 pub(crate) fn apply_visible_static_defaults(&mut self) -> bool {
600 let defaults = self.collect_visible_static_defaults();
601 for (data_path, schema_val, _) in &defaults {
602 self.eval_data
603 .set(&format!("/{}", data_path), schema_val.clone());
604 self.eval_cache
605 .bump_data_version(&format!("/{}", data_path));
606 }
607 !defaults.is_empty()
608 }
609
610 pub(crate) fn apply_visible_static_defaults_with_dependents(
616 &mut self,
617 token: Option<&CancellationToken>,
618 ) -> Result<bool, String> {
619 if self.collect_visible_static_defaults().is_empty() {
620 return Ok(false);
621 }
622
623 let mut to_process = Vec::new();
624 let mut processed = std::collections::HashMap::new();
625 let mut result = Vec::new();
626 self.run_schema_default_value_pass(
627 token,
628 &mut to_process,
629 &mut processed,
630 &mut result,
631 None,
632 )?;
633 Ok(true)
634 }
635
636 fn run_schema_default_value_pass(
639 &mut self,
640 _token: Option<&CancellationToken>,
641 _to_process: &mut Vec<(String, bool, Option<Vec<usize>>)>,
642 _processed: &mut std::collections::HashMap<
643 String,
644 Option<std::collections::HashSet<usize>>,
645 >,
646 result: &mut Vec<Value>,
647 _canceled_paths: Option<&mut Vec<String>>,
648 ) -> Result<(), String> {
649 let mut default_value_changes = Vec::new();
650 let schema_values = self.get_schema_value_array();
651
652 if let Value::Array(values) = schema_values {
653 for item in values {
654 if let Value::Object(map) = item {
655 if let (Some(Value::String(dot_path)), Some(schema_val)) =
656 (map.get("path"), map.get("value"))
657 {
658 let schema_ptr = path_utils::dot_notation_to_schema_pointer(dot_path);
659 if let Some(Value::Object(schema_node)) = self
660 .evaluated_schema
661 .pointer(schema_ptr.trim_start_matches('#'))
662 {
663 if let Some(Value::Object(condition)) = schema_node.get("condition") {
664 if let Some(hidden_val) = condition.get("hidden") {
665 if !hidden_val.is_boolean()
667 || hidden_val.as_bool() == Some(true)
668 {
669 continue;
670 }
671 }
672 }
673 }
674
675 let data_path = dot_path.replace('.', "/");
676 let current_data = self
677 .eval_data
678 .data()
679 .pointer(&format!("/{}", data_path))
680 .unwrap_or(&Value::Null);
681
682 let is_empty = match current_data {
683 Value::Null => true,
684 Value::String(s) if s.is_empty() => true,
685 _ => false,
686 };
687
688 let is_schema_val_empty = match schema_val {
689 Value::Null => true,
690 Value::String(s) if s.is_empty() => true,
691 Value::Object(map) if map.contains_key("$evaluation") => true,
692 _ => false,
693 };
694
695 if is_empty && !is_schema_val_empty && current_data != schema_val {
696 default_value_changes.push((
697 data_path,
698 schema_val.clone(),
699 dot_path.clone(),
700 ));
701 }
702 }
703 }
704 }
705 }
706
707 for (data_path, schema_val, dot_path) in default_value_changes {
708 self.eval_data
709 .set(&format!("/{}", data_path), schema_val.clone());
710 self.eval_cache
711 .bump_data_version(&format!("/{}", data_path));
712
713 let mut change_obj = serde_json::Map::new();
714 change_obj.insert("$ref".to_string(), Value::String(dot_path));
715 let is_clear = schema_val == Value::Null || schema_val.as_str() == Some("");
716 if is_clear {
717 change_obj.insert("clear".to_string(), Value::Bool(true));
718 } else {
719 change_obj.insert("value".to_string(), schema_val);
720 }
721 result.push(Value::Object(change_obj));
722
723 }
728
729 Ok(())
730 }
731
732 fn run_subform_pass(
742 &mut self,
743 changed_paths: &[String],
744 parent_changed_paths: &[String],
745 _re_evaluate: bool,
746 token: Option<&CancellationToken>,
747 result: &mut Vec<Value>,
748 ) -> Result<bool, String> {
749 let mut any_table_invalidated = false;
750 let subform_paths: Vec<String> = self.subforms.keys().cloned().collect();
752
753 for subform_path in subform_paths {
754 let field_key = subform_field_key(&subform_path);
755 let subform_dot_path =
757 path_utils::pointer_to_dot_notation(&subform_path).replace(".properties.", ".");
758 let field_prefix = format!("{}.", field_key);
759 let subform_ptr = normalize_to_json_pointer(&subform_path);
760
761 let item_count =
763 get_value_by_pointer_without_properties(self.eval_data.data(), &subform_ptr)
764 .and_then(|v| v.as_array())
765 .map(|a| a.len())
766 .unwrap_or(0);
767
768 if item_count == 0 {
769 continue;
770 }
771
772 self.eval_cache.prune_subform_caches(item_count);
775
776 let parent_data_versions_snapshot = self.eval_cache.data_versions.clone();
781 let parent_params_versions_snapshot = self.eval_cache.params_versions.clone();
782
783 for idx in 0..item_count {
784 let prefix_dot = format!("{}.{}.", subform_dot_path, idx);
786 let prefix_bracket = format!("{}[{}].", subform_dot_path, idx);
787 let prefix_field_bracket = format!("{}[{}].", field_key, idx);
788
789 let is_collection_refresh = changed_paths
790 .iter()
791 .any(|path| path == &format!("{subform_dot_path}.{idx}"));
792 let item_changed_paths: Vec<String> = changed_paths
793 .iter()
794 .filter_map(|p| {
795 if p == &format!("{subform_dot_path}.{idx}") {
796 Some(field_key.clone())
797 } else if p.starts_with(&prefix_bracket) {
798 Some(p.replacen(&prefix_bracket, &field_prefix, 1))
799 } else if p.starts_with(&prefix_dot) {
800 Some(p.replacen(&prefix_dot, &field_prefix, 1))
801 } else if p.starts_with(&prefix_field_bracket) {
802 Some(p.replacen(&prefix_field_bracket, &field_prefix, 1))
803 } else {
804 None
805 }
806 })
807 .collect();
808
809 let item_val =
812 get_value_by_pointer_without_properties(self.eval_data.data(), &subform_ptr)
813 .and_then(|v| v.as_array())
814 .and_then(|a| a.get(idx))
815 .cloned()
816 .unwrap_or(Value::Null);
817
818 let parent_dependency_paths: Vec<String> = parent_changed_paths
823 .iter()
824 .map(|path| {
825 path_utils::dot_notation_to_schema_pointer(path)
826 .trim_start_matches('#')
827 .to_string()
828 })
829 .collect();
830 let dependent_value_paths: Vec<String> = self
831 .subforms
832 .get(&subform_path)
833 .map(|subform| {
834 subform
835 .dependencies
836 .iter()
837 .filter(|(key, deps)| {
838 !subform.table_metadata.contains_key(*key)
839 && !key.starts_with("#/$params/")
840 && key.ends_with("/value")
841 && parent_dependency_paths
842 .iter()
843 .any(|dependency| deps.contains(dependency))
844 })
845 .map(|(key, _)| key.clone())
846 .collect()
847 })
848 .unwrap_or_default();
849
850 if item_changed_paths.is_empty() && !dependent_value_paths.is_empty() {
851 let parent_cache = std::mem::take(&mut self.eval_cache);
856 let mut overlay_cache = parent_cache.clone();
857 overlay_cache.ensure_active_item_cache(idx);
858 if let Some(item_cache) = overlay_cache.subform_caches.get_mut(&idx) {
859 item_cache
863 .data_versions
864 .merge_from(&parent_data_versions_snapshot);
865 item_cache
866 .data_versions
867 .merge_from_params(&parent_params_versions_snapshot);
868 }
869 overlay_cache.set_active_item(idx);
870 let canonical_root =
871 path_utils::schema_path_to_data_pointer(&subform_path).into_owned();
872 let scope = crate::jsoneval::subform_scope::SubformScope::new(
873 &subform_path,
874 &canonical_root,
875 Some(idx),
876 );
877 let mut scoped_view = scope.evaluation_view(self.eval_data.data());
878 if let Some(view) = scoped_view.as_object_mut() {
879 view.insert(
880 "$context".to_string(),
881 self.eval_data
882 .data()
883 .get("$context")
884 .cloned()
885 .unwrap_or(Value::Null),
886 );
887 }
888 let subform = self
889 .subforms
890 .get_mut(&subform_path)
891 .expect("subform exists");
892 subform.eval_data = EvalData::new(scoped_view);
893 std::mem::swap(&mut subform.eval_cache, &mut overlay_cache);
894
895 let refresh_table_outputs = dependent_value_paths.iter().any(|source| {
900 let source = source.trim_end_matches("/value").trim_start_matches('#');
901 let affected_tables: Vec<&String> = subform
902 .table_metadata
903 .keys()
904 .filter(|table| table.starts_with("#/$params"))
905 .filter(|table| {
906 subform.dependencies.get(*table).is_some_and(|deps| {
907 deps.iter().any(|dep| dep.trim_start_matches('#') == source)
908 })
909 })
910 .collect();
911
912 !affected_tables.is_empty()
913 && subform.evaluations.iter().any(|(target, _)| {
914 target.ends_with("/value")
915 && subform.dependencies.get(target).is_some_and(|deps| {
916 deps.iter().any(|dep| {
917 affected_tables.iter().any(|table| {
918 dep.trim_start_matches('#')
919 == table.trim_start_matches('#')
920 })
921 })
922 })
923 })
924 });
925 subform.evaluate_internal(Some(&dependent_value_paths), token)?;
926
927 let mut overlay_result = Vec::new();
928 let mut overlay_queue = Vec::new();
929 let mut overlay_processed = std::collections::HashMap::new();
930 for formula_path in &dependent_value_paths {
931 let schema_path = path_utils::normalize_to_json_pointer(formula_path);
932 let data_path = path_utils::schema_path_to_data_pointer(formula_path)
933 .replace("/value", "");
934 let Some(value) = subform.evaluated_schema.pointer(&schema_path).cloned()
935 else {
936 continue;
937 };
938
939 let source_path = formula_path.trim_end_matches("/value").to_string();
944 if subform.eval_data.get(&data_path) != Some(&value) {
945 subform.eval_data.set(&data_path, value.clone());
946 subform
947 .eval_data
948 .set(&scope.canonical_path(&data_path), value.clone());
949 subform.eval_cache.bump_data_version(&data_path);
950 }
951 overlay_queue.push((source_path, true, None));
952
953 let mut change = serde_json::Map::new();
954 let field = data_path
955 .trim_start_matches('/')
956 .trim_end_matches("/value")
957 .replace('/', ".");
958 let field = field.strip_prefix(&field_prefix).unwrap_or(&field);
959 change.insert(
960 "$ref".to_string(),
961 Value::String(format!("{}.{}.{}", subform_dot_path, idx, field)),
962 );
963 if value == Value::Null || value.as_str() == Some("") {
964 change.insert("clear".to_string(), Value::Bool(true));
965 } else {
966 change.insert("value".to_string(), value);
967 }
968 result.push(Value::Object(change));
969 }
970
971 Self::process_dependents_queue(
972 &subform.engine,
973 &subform.evaluations,
974 &mut subform.eval_data,
975 &mut subform.eval_cache,
976 &subform.dependents_evaluations,
977 &subform.dep_formula_triggers,
978 &subform.evaluated_schema,
979 &mut overlay_queue,
980 &mut overlay_processed,
981 &mut overlay_result,
982 token,
983 None,
984 )?;
985
986 let local_item_path = format!("/{field_key}");
989 if let Some(local_item) = subform.eval_data.get(&local_item_path).cloned() {
990 subform
991 .eval_data
992 .set(&scope.canonical_path(&local_item_path), local_item);
993 }
994
995 if refresh_table_outputs {
996 subform.run_re_evaluate_pass(
1000 token,
1001 &mut overlay_queue,
1002 &mut overlay_processed,
1003 &mut overlay_result,
1004 None,
1005 )?;
1006 }
1007
1008 for change in overlay_result {
1009 let Some(object) = change.as_object() else {
1010 continue;
1011 };
1012 let Some(Value::String(ref_path)) = object.get("$ref") else {
1013 continue;
1014 };
1015 let local_ref = ref_path.strip_prefix(&field_prefix).unwrap_or(ref_path);
1016 let mut mapped = object.clone();
1017 mapped.insert(
1018 "$ref".to_string(),
1019 Value::String(format!("{}.{}.{}", subform_dot_path, idx, local_ref)),
1020 );
1021 result.push(Value::Object(mapped));
1022 }
1023
1024 std::mem::swap(&mut subform.eval_cache, &mut overlay_cache);
1027 self.eval_cache = parent_cache;
1028 continue;
1029 }
1030
1031 let canonical_root =
1032 path_utils::schema_path_to_data_pointer(&subform_path).into_owned();
1033 let scope = crate::jsoneval::subform_scope::SubformScope::new(
1034 &subform_path,
1035 &canonical_root,
1036 Some(idx),
1037 );
1038 let mut scoped_view = scope.evaluation_view(self.eval_data.data());
1039 if let Some(view) = scoped_view.as_object_mut() {
1040 view.insert(
1041 "$context".to_string(),
1042 self.eval_data
1043 .data()
1044 .get("$context")
1045 .cloned()
1046 .unwrap_or(Value::Null),
1047 );
1048 }
1049 let Some(subform) = self.subforms.get_mut(&subform_path) else {
1050 continue;
1051 };
1052
1053 let sub_re_evaluate = !item_changed_paths.is_empty();
1058 if !sub_re_evaluate && item_changed_paths.is_empty() {
1059 continue;
1060 }
1061
1062 self.eval_cache.ensure_active_item_cache(idx);
1064 let old_item_val = {
1065 let snapshot = self
1066 .eval_cache
1067 .subform_caches
1068 .get(&idx)
1069 .map(|c| c.item_snapshot.clone())
1070 .unwrap_or(Value::Null);
1071
1072 if snapshot == Value::Null {
1073 if let Some(main_snap) = &self.eval_cache.main_form_snapshot {
1074 get_value_by_pointer_without_properties(main_snap, &subform_ptr)
1075 .and_then(|v| v.as_array())
1076 .and_then(|a| a.get(idx))
1077 .cloned()
1078 .unwrap_or(Value::Null)
1079 } else {
1080 Value::Null
1081 }
1082 } else {
1083 snapshot
1084 }
1085 };
1086
1087 subform.eval_data = EvalData::new(scoped_view);
1088 let new_item_val = item_val.clone();
1089
1090 let mut parent_cache = std::mem::take(&mut self.eval_cache);
1092 parent_cache.ensure_active_item_cache(idx);
1093
1094 let pre_diff_item_versions = parent_cache
1097 .subform_caches
1098 .get(&idx)
1099 .map(|c| c.data_versions.clone());
1100
1101 if let Some(c) = parent_cache.subform_caches.get_mut(&idx) {
1102 c.data_versions.merge_from(&parent_data_versions_snapshot);
1106 c.data_versions
1108 .merge_from_params(&parent_params_versions_snapshot);
1109 if !is_collection_refresh {
1110 crate::jsoneval::eval_cache::diff_and_update_versions(
1111 &mut c.data_versions,
1112 &format!("/{}", field_key),
1113 &old_item_val,
1114 &new_item_val,
1115 "run_subform_pass_diff_and_update_versions",
1116 );
1117 }
1118 c.item_snapshot = new_item_val;
1121 }
1122
1123 if let (Some(ref pre), Some(c)) = (
1127 &pre_diff_item_versions,
1128 parent_cache.subform_caches.get(&idx),
1129 ) {
1130 let field_prefix_slash = format!("/{}/", field_key);
1131 let newly_bumped: Vec<String> = c
1132 .data_versions
1133 .versions()
1134 .filter(|(k, &v)| k.starts_with(&field_prefix_slash) && v > pre.get(k))
1135 .map(|(k, _)| k.to_string())
1136 .collect();
1137 if !newly_bumped.is_empty() {
1138 for k in newly_bumped {
1139 parent_cache
1140 .data_versions
1141 .bump(&k, "propagate_newly_bumped");
1142 }
1143 parent_cache.eval_generation += 1;
1144 }
1145 }
1146
1147 {
1153 let field_prefix_slash = format!("/{}/", field_key);
1154 let newly_bumped_schema_paths: Vec<String> = if let (Some(ref pre), Some(c)) = (
1155 &pre_diff_item_versions,
1156 parent_cache.subform_caches.get(&idx),
1157 ) {
1158 c.data_versions
1159 .versions()
1160 .filter(|(k, &v)| k.starts_with(&field_prefix_slash) && v > pre.get(k))
1161 .map(|(k, _)| {
1162 let sub = k.trim_start_matches(&field_prefix_slash);
1166 format!(
1167 "/{}/properties/{}",
1168 field_key,
1169 sub.replace('/', "/properties/")
1170 )
1171 })
1172 .collect()
1173 } else {
1174 Vec::new()
1175 };
1176
1177 if !newly_bumped_schema_paths.is_empty() {
1178 let params_table_keys: Vec<String> = self
1179 .table_metadata
1180 .keys()
1181 .filter(|k| k.starts_with("#/$params"))
1182 .filter(|k| {
1183 self.dependencies
1184 .get(*k)
1185 .map(|deps| {
1186 deps.iter().any(|dep| {
1187 newly_bumped_schema_paths
1188 .iter()
1189 .any(|b| dep == b || dep.starts_with(b.as_str()))
1190 })
1191 })
1192 .unwrap_or(false)
1193 })
1194 .cloned()
1195 .collect();
1196
1197 if !params_table_keys.is_empty() {
1198 parent_cache.invalidate_params_tables_for_item(idx, ¶ms_table_keys);
1199 any_table_invalidated = true;
1200 }
1201 }
1202 }
1203
1204 parent_cache.set_active_item(idx);
1205 std::mem::swap(&mut subform.eval_cache, &mut parent_cache);
1206
1207 let subform_result = time_block!(" [subform_pass] rider evaluate_dependents", {
1208 subform.evaluate_dependents(
1209 &item_changed_paths,
1210 None,
1211 None,
1212 sub_re_evaluate,
1213 token,
1214 None,
1215 false,
1216 )
1217 });
1218
1219 std::mem::swap(&mut subform.eval_cache, &mut parent_cache);
1221 parent_cache.clear_active_item();
1222
1223 if let Some(parent_item_cache) = self.eval_cache.subform_caches.get(&idx) {
1228 let snapshot = parent_item_cache.item_snapshot.clone();
1229 subform.eval_cache.ensure_active_item_cache(idx);
1230 if let Some(sub_cache) = subform.eval_cache.subform_caches.get_mut(&idx) {
1231 sub_cache.item_snapshot = snapshot;
1232 }
1233 }
1234
1235 self.eval_cache = parent_cache;
1236
1237 if let Ok(Value::Array(changes)) = subform_result {
1238 let mut had_any_change = false;
1239 for change in changes {
1240 if let Some(obj) = change.as_object() {
1241 if let Some(Value::String(ref_path)) = obj.get("$ref") {
1242 let new_ref = if ref_path.starts_with(&field_prefix) {
1244 format!(
1245 "{}.{}.{}",
1246 subform_dot_path,
1247 idx,
1248 &ref_path[field_prefix.len()..]
1249 )
1250 } else {
1251 format!("{}.{}.{}", subform_dot_path, idx, ref_path)
1252 };
1253
1254 if let Some(val) = obj.get("value") {
1260 let data_ptr = format!("/{}", new_ref.replace('.', "/"));
1261 self.eval_data.set(&data_ptr, val.clone());
1262 had_any_change = true;
1263 } else if obj.get("clear").and_then(Value::as_bool) == Some(true) {
1264 let data_ptr = format!("/{}", new_ref.replace('.', "/"));
1265 self.eval_data.set(&data_ptr, Value::Null);
1266 had_any_change = true;
1267 }
1268
1269 let mut new_obj = obj.clone();
1270 new_obj.insert("$ref".to_string(), Value::String(new_ref));
1271 result.push(Value::Object(new_obj));
1272 } else {
1273 result.push(change);
1275 }
1276 }
1277 }
1278
1279 if had_any_change {
1287 let item_path = format!("{}/{}", subform_ptr, idx);
1288 let updated_item = self
1289 .eval_data
1290 .get(&item_path)
1291 .cloned()
1292 .unwrap_or(Value::Null);
1293 if let Some(c) = self.eval_cache.subform_caches.get_mut(&idx) {
1295 c.item_snapshot = updated_item.clone();
1296 }
1297 subform.eval_cache.ensure_active_item_cache(idx);
1300 if let Some(sub_cache) = subform.eval_cache.subform_caches.get_mut(&idx) {
1301 sub_cache.item_snapshot = updated_item;
1302 }
1303 }
1304 }
1305 }
1306 }
1307 Ok(any_table_invalidated)
1308 }
1309
1310 pub(crate) fn evaluate_dependent_value_static(
1312 engine: &RLogic,
1313 evaluations: &IndexMap<String, LogicId>,
1314 eval_data: &EvalData,
1315 value: &Value,
1316 changed_field_value: &Value,
1317 changed_field_ref_value: &Value,
1318 ) -> Result<Value, String> {
1319 match value {
1320 Value::String(eval_key) => {
1322 if let Some(logic_id) = evaluations.get(eval_key) {
1323 let mut internal_context = serde_json::Map::new();
1326 internal_context.insert("$value".to_string(), changed_field_value.clone());
1327 internal_context.insert("$refValue".to_string(), changed_field_ref_value.clone());
1328 let context_value = Value::Object(internal_context);
1329
1330 let result = engine.run_with_context(logic_id, eval_data.data(), &context_value)
1331 .map_err(|e| format!("Failed to evaluate dependent logic '{}': {}", eval_key, e))?;
1332 Ok(result)
1333 } else {
1334 Ok(value.clone())
1336 }
1337 }
1338 Value::Object(map) if map.contains_key("$evaluation") => {
1341 Err("Dependent evaluation contains unparsed $evaluation - schema was not properly parsed".to_string())
1342 }
1343 _ => Ok(value.clone()),
1345 }
1346 }
1347
1348 pub(crate) fn check_readonly_for_dependents(
1350 &self,
1351 schema_element: &Value,
1352 path: &str,
1353 changes: &mut Vec<(String, Value)>,
1354 all_values: &mut Vec<(String, Value)>,
1355 ) {
1356 match schema_element {
1357 Value::Object(map) => {
1358 let mut is_disabled = false;
1360 if let Some(Value::Object(condition)) = map.get("condition") {
1361 if let Some(Value::Bool(d)) = condition.get("disabled") {
1362 is_disabled = *d;
1363 }
1364 }
1365
1366 let mut skip_readonly = false;
1368 if let Some(Value::Object(config)) = map.get("config") {
1369 if let Some(Value::Object(all)) = config.get("all") {
1370 if let Some(Value::Bool(skip)) = all.get("skipReadOnlyValue") {
1371 skip_readonly = *skip;
1372 }
1373 }
1374 }
1375
1376 if is_disabled && !skip_readonly {
1377 if let Some(schema_value) = map.get("value") {
1378 let data_path = path_utils::schema_path_to_data_pointer(path)
1379 .replace("/value/", "/");
1382
1383 let current_data = self
1384 .eval_data
1385 .data()
1386 .pointer(&data_path)
1387 .unwrap_or(&Value::Null);
1388
1389 all_values.push((path.to_string(), schema_value.clone()));
1392 if current_data != schema_value {
1393 changes.push((path.to_string(), schema_value.clone()));
1394 }
1395 }
1396 }
1397 }
1398 _ => {}
1399 }
1400 }
1401
1402 #[allow(dead_code)]
1404 pub(crate) fn collect_readonly_fixes(
1405 &self,
1406 schema_element: &Value,
1407 path: &str,
1408 changes: &mut Vec<(String, Value)>,
1409 ) {
1410 match schema_element {
1411 Value::Object(map) => {
1412 let mut is_disabled = false;
1414 if let Some(Value::Object(condition)) = map.get("condition") {
1415 if let Some(Value::Bool(d)) = condition.get("disabled") {
1416 is_disabled = *d;
1417 }
1418 }
1419
1420 let mut skip_readonly = false;
1422 if let Some(Value::Object(config)) = map.get("config") {
1423 if let Some(Value::Object(all)) = config.get("all") {
1424 if let Some(Value::Bool(skip)) = all.get("skipReadOnlyValue") {
1425 skip_readonly = *skip;
1426 }
1427 }
1428 }
1429
1430 if is_disabled && !skip_readonly {
1431 if let Some(schema_value) = map.get("value") {
1435 let data_path = path_utils::schema_path_to_data_pointer(path).into_owned();
1436
1437 let current_data = self
1438 .eval_data
1439 .data()
1440 .pointer(&data_path)
1441 .unwrap_or(&Value::Null);
1442
1443 if current_data != schema_value {
1444 changes.push((path.to_string(), schema_value.clone()));
1445 }
1446 }
1447 }
1448
1449 if let Some(Value::Object(props)) = map.get("properties") {
1451 for (key, val) in props {
1452 let next_path = if path == "#" {
1453 format!("#/properties/{}", key)
1454 } else {
1455 format!("{}/properties/{}", path, key)
1456 };
1457 self.collect_readonly_fixes(val, &next_path, changes);
1458 }
1459 }
1460 }
1461 _ => {}
1462 }
1463 }
1464
1465 pub(crate) fn check_hidden_field(
1467 &self,
1468 schema_element: &Value,
1469 path: &str,
1470 hidden_fields: &mut Vec<String>,
1471 ) {
1472 match schema_element {
1473 Value::Object(map) => {
1474 let mut is_hidden = false;
1476 if let Some(Value::Object(condition)) = map.get("condition") {
1477 if let Some(Value::Bool(h)) = condition.get("hidden") {
1478 is_hidden = *h;
1479 }
1480 }
1481
1482 let mut keep_hidden = false;
1484 if let Some(Value::Object(config)) = map.get("config") {
1485 if let Some(Value::Object(all)) = config.get("all") {
1486 if let Some(Value::Bool(keep)) = all.get("keepHiddenValue") {
1487 keep_hidden = *keep;
1488 }
1489 }
1490 }
1491
1492 if is_hidden && !keep_hidden {
1493 let data_path = path_utils::schema_path_to_data_pointer(path).into_owned();
1494
1495 let current_data = self
1496 .eval_data
1497 .data()
1498 .pointer(&data_path)
1499 .unwrap_or(&Value::Null);
1500
1501 if current_data != &Value::Null && current_data != "" {
1503 hidden_fields.push(path.to_string());
1504 }
1505 }
1506 }
1507 _ => {}
1508 }
1509 }
1510
1511 fn check_effectively_hidden_field(
1513 &self,
1514 schema_element: &Value,
1515 path: &str,
1516 hidden_fields: &mut Vec<String>,
1517 ) {
1518 let Value::Object(map) = schema_element else {
1519 return;
1520 };
1521
1522 let keep_hidden = map
1523 .get("config")
1524 .and_then(Value::as_object)
1525 .and_then(|config| config.get("all"))
1526 .and_then(Value::as_object)
1527 .and_then(|all| all.get("keepHiddenValue"))
1528 .and_then(Value::as_bool)
1529 .unwrap_or(false);
1530 if keep_hidden {
1531 return;
1532 }
1533
1534 let current_data = self
1535 .eval_data
1536 .data()
1537 .pointer(&path_utils::schema_path_to_data_pointer(path))
1538 .unwrap_or(&Value::Null);
1539 if current_data != &Value::Null && current_data != "" {
1540 hidden_fields.push(path.to_string());
1541 }
1542 }
1543
1544 #[allow(dead_code)]
1546 pub(crate) fn collect_hidden_fields(
1547 &self,
1548 schema_element: &Value,
1549 path: &str,
1550 hidden_fields: &mut Vec<String>,
1551 ) {
1552 match schema_element {
1553 Value::Object(map) => {
1554 let mut is_hidden = false;
1556 if let Some(Value::Object(condition)) = map.get("condition") {
1557 if let Some(Value::Bool(h)) = condition.get("hidden") {
1558 is_hidden = *h;
1559 }
1560 }
1561
1562 let mut keep_hidden = false;
1564 if let Some(Value::Object(config)) = map.get("config") {
1565 if let Some(Value::Object(all)) = config.get("all") {
1566 if let Some(Value::Bool(keep)) = all.get("keepHiddenValue") {
1567 keep_hidden = *keep;
1568 }
1569 }
1570 }
1571
1572 if is_hidden && !keep_hidden {
1573 let data_path = path_utils::schema_path_to_data_pointer(path).into_owned();
1574
1575 let current_data = self
1576 .eval_data
1577 .data()
1578 .pointer(&data_path)
1579 .unwrap_or(&Value::Null);
1580
1581 if current_data != &Value::Null && current_data != "" {
1583 hidden_fields.push(path.to_string());
1584 }
1585 }
1586
1587 for (key, val) in map {
1589 if key == "properties" {
1590 if let Value::Object(props) = val {
1591 for (p_key, p_val) in props {
1592 let next_path = if path == "#" {
1593 format!("#/properties/{}", p_key)
1594 } else {
1595 format!("{}/properties/{}", path, p_key)
1596 };
1597 self.collect_hidden_fields(p_val, &next_path, hidden_fields);
1598 }
1599 }
1600 } else if let Value::Object(_) = val {
1601 if key == "condition"
1603 || key == "config"
1604 || key == "rules"
1605 || key == "dependents"
1606 || key == "hideLayout"
1607 || key == "$layout"
1608 || key == "$params"
1609 || key == "definitions"
1610 || key == "$defs"
1611 || key.starts_with('$')
1612 {
1613 continue;
1614 }
1615
1616 let next_path = if path == "#" {
1617 format!("#/{}", key)
1618 } else {
1619 format!("{}/{}", path, key)
1620 };
1621 self.collect_hidden_fields(val, &next_path, hidden_fields);
1622 }
1623 }
1624 }
1625 _ => {}
1626 }
1627 }
1628
1629 pub(crate) fn recursive_hide_effect(
1632 engine: &RLogic,
1633 evaluations: &IndexMap<String, LogicId>,
1634 reffed_by: &IndexMap<String, Vec<String>>,
1635 eval_data: &mut EvalData,
1636 eval_cache: &mut crate::jsoneval::eval_cache::EvalCache,
1637 mut hidden_fields: Vec<String>,
1638 queue: &mut Vec<(String, bool, Option<Vec<usize>>)>,
1639 result: &mut Vec<Value>,
1640 ) {
1641 while let Some(hf) = hidden_fields.pop() {
1642 let data_path = path_utils::schema_path_to_data_pointer(&hf).into_owned();
1643
1644 eval_data.set(&data_path, Value::Null);
1646 eval_cache.bump_data_version(&data_path);
1647
1648 let mut change_obj = serde_json::Map::new();
1650 change_obj.insert(
1651 "$ref".to_string(),
1652 Value::String(path_utils::pointer_to_dot_notation(&data_path)),
1653 );
1654 change_obj.insert("$hidden".to_string(), Value::Bool(true));
1655 change_obj.insert("clear".to_string(), Value::Bool(true));
1656 result.push(Value::Object(change_obj));
1657
1658 queue.push((hf.clone(), true, None));
1660
1661 if let Some(referencing_fields) = reffed_by.get(&data_path) {
1663 for rb in referencing_fields {
1664 let hidden_eval_key = format!("{}/condition/hidden", rb);
1668
1669 if let Some(logic_id) = evaluations.get(&hidden_eval_key) {
1670 let rb_data_path = path_utils::schema_path_to_data_pointer(rb).into_owned();
1677 let rb_value = eval_data
1678 .data()
1679 .pointer(&rb_data_path)
1680 .cloned()
1681 .unwrap_or(Value::Null);
1682
1683 if let Ok(Value::Bool(is_hidden)) = engine.run(logic_id, eval_data.data()) {
1685 if is_hidden {
1686 if !hidden_fields.contains(rb) {
1689 let has_value = rb_value != Value::Null && rb_value != "";
1690 if has_value {
1691 hidden_fields.push(rb.clone());
1692 }
1693 }
1694 }
1695 }
1696 }
1697 }
1698 }
1699 }
1700 }
1701
1702 pub(crate) fn process_dependents_queue(
1705 engine: &RLogic,
1706 evaluations: &IndexMap<String, LogicId>,
1707 eval_data: &mut EvalData,
1708 eval_cache: &mut crate::jsoneval::eval_cache::EvalCache,
1709 dependents_evaluations: &IndexMap<String, Vec<DependentItem>>,
1710 dep_formula_triggers: &IndexMap<String, Vec<(String, usize)>>,
1711 evaluated_schema: &Value,
1712 queue: &mut Vec<(String, bool, Option<Vec<usize>>)>,
1713 processed: &mut std::collections::HashMap<String, Option<std::collections::HashSet<usize>>>,
1714 result: &mut Vec<Value>,
1715 token: Option<&CancellationToken>,
1716 canceled_paths: Option<&mut Vec<String>>,
1717 ) -> Result<(), String> {
1718 while let Some((current_path, is_transitive, target_indices)) = queue.pop() {
1719 if let Some(t) = token {
1720 if t.is_cancelled() {
1721 if let Some(cp) = canceled_paths {
1722 cp.push(current_path.clone());
1723 for (path, _, _) in queue.iter() {
1724 cp.push(path.clone());
1725 }
1726 }
1727 return Err("Cancelled".to_string());
1728 }
1729 }
1730
1731 let (should_run, indices_to_run) = match processed.get(¤t_path) {
1732 Some(None) => {
1733 continue;
1735 }
1736 Some(Some(already_processed_indices)) => {
1737 if let Some(targets) = &target_indices {
1738 let new_targets: std::collections::HashSet<usize> = targets
1739 .iter()
1740 .copied()
1741 .filter(|i| !already_processed_indices.contains(i))
1742 .collect();
1743 if new_targets.is_empty() {
1744 continue;
1745 }
1746 (true, Some(new_targets))
1747 } else {
1748 (true, None)
1749 }
1750 }
1751 None => (
1752 true,
1753 target_indices.clone().map(|t| t.into_iter().collect()),
1754 ),
1755 };
1756
1757 if !should_run {
1758 continue;
1759 }
1760
1761 let new_processed_state = if let Some(targets_to_run) = &indices_to_run {
1762 match processed.get(¤t_path) {
1763 Some(Some(existing_targets)) => {
1764 let mut copy = existing_targets.clone();
1765 for t in targets_to_run {
1766 copy.insert(*t);
1767 }
1768 Some(copy)
1769 }
1770 _ => Some(targets_to_run.clone()),
1771 }
1772 } else {
1773 None
1774 };
1775 processed.insert(current_path.clone(), new_processed_state);
1776
1777 let current_data_path =
1779 path_utils::schema_path_to_data_pointer(¤t_path).into_owned();
1780 let mut current_value = eval_data
1781 .data()
1782 .pointer(¤t_data_path)
1783 .cloned()
1784 .unwrap_or(Value::Null);
1785
1786 if let Some(formula_sources) = dep_formula_triggers.get(¤t_data_path) {
1791 let mut targets_by_source: std::collections::HashMap<String, Vec<usize>> =
1792 std::collections::HashMap::new();
1793 for (source_schema_path, dep_idx) in formula_sources {
1794 let source_ptr = path_utils::dot_notation_to_schema_pointer(source_schema_path);
1795 targets_by_source
1796 .entry(source_ptr)
1797 .or_default()
1798 .push(*dep_idx);
1799 }
1800 for (source_ptr, targets) in targets_by_source {
1801 if let Some(None) = processed.get(&source_ptr) {
1803 continue;
1804 }
1805 queue.push((source_ptr, true, Some(targets)));
1806 }
1807 }
1808
1809 if let Some(dependent_items) = dependents_evaluations.get(¤t_path) {
1811 for (dep_idx, dep_item) in dependent_items.iter().enumerate() {
1812 if let Some(targets) = &indices_to_run {
1813 if !targets.contains(&dep_idx) {
1814 continue;
1815 }
1816 }
1817 let ref_path = &dep_item.ref_path;
1818 let pointer_path = path_utils::normalize_to_json_pointer(ref_path);
1819 let data_path =
1821 crate::jsoneval::path_utils::schema_path_to_data_pointer(&pointer_path)
1822 .into_owned();
1823
1824 let current_ref_value = eval_data
1825 .data()
1826 .pointer(&data_path)
1827 .cloned()
1828 .unwrap_or(Value::Null);
1829
1830 let field = evaluated_schema.pointer(&pointer_path).cloned();
1832
1833 let parent_path = if let Some(last_slash) = pointer_path.rfind("/properties") {
1835 &pointer_path[..last_slash]
1836 } else {
1837 "/"
1838 };
1839 let mut parent_field = if parent_path.is_empty() || parent_path == "/" {
1840 evaluated_schema.clone()
1841 } else {
1842 evaluated_schema
1843 .pointer(parent_path)
1844 .cloned()
1845 .unwrap_or_else(|| Value::Object(serde_json::Map::new()))
1846 };
1847
1848 if let Value::Object(ref mut map) = parent_field {
1850 map.remove("properties");
1851 map.remove("$layout");
1852 }
1853
1854 let mut change_obj = serde_json::Map::new();
1855 change_obj.insert(
1856 "$ref".to_string(),
1857 Value::String(path_utils::pointer_to_dot_notation(&data_path)),
1858 );
1859 if let Some(f) = field {
1860 change_obj.insert("$field".to_string(), f);
1861 }
1862 change_obj.insert("$parentField".to_string(), parent_field);
1863 change_obj.insert("transitive".to_string(), Value::Bool(is_transitive));
1864
1865 if processed.contains_key(ref_path) {
1870 continue;
1871 }
1872
1873 let mut add_transitive = false;
1874 let mut add_deps = false;
1875 if let Some(clear_val) = &dep_item.clear {
1877 let should_clear = Self::evaluate_dependent_value_static(
1878 engine,
1879 evaluations,
1880 eval_data,
1881 clear_val,
1882 ¤t_value,
1883 ¤t_ref_value,
1884 )?;
1885 let clear_bool = match should_clear {
1886 Value::Bool(b) => b,
1887 _ => false,
1888 };
1889
1890 if clear_bool {
1891 if data_path == current_data_path {
1892 current_value = Value::Null;
1893 }
1894 eval_data.set(&data_path, Value::Null);
1895 eval_cache.bump_data_version(&data_path);
1896 change_obj.insert("clear".to_string(), Value::Bool(true));
1897 add_transitive = true;
1898 add_deps = true;
1899 }
1900 }
1901
1902 if let Some(value_val) = &dep_item.value {
1904 let computed_value = Self::evaluate_dependent_value_static(
1905 engine,
1906 evaluations,
1907 eval_data,
1908 value_val,
1909 ¤t_value,
1910 ¤t_ref_value,
1911 )?;
1912 let cleaned_val = clean_float_noise_scalar(computed_value);
1913
1914 let is_clear =
1915 cleaned_val == Value::Null || cleaned_val.as_str() == Some("");
1916
1917 if cleaned_val != current_ref_value && !is_clear {
1918 if data_path == current_data_path {
1919 current_value = cleaned_val.clone();
1920 }
1921 eval_data.set(&data_path, cleaned_val.clone());
1922 eval_cache.bump_data_version(&data_path);
1923 change_obj.insert("value".to_string(), cleaned_val);
1924 add_transitive = true;
1925 add_deps = true;
1926 }
1927 }
1928
1929 if add_deps {
1931 result.push(Value::Object(change_obj));
1932 }
1933
1934 if add_transitive {
1936 queue.push((ref_path.clone(), true, None));
1937 }
1938 }
1939 }
1940 }
1941 Ok(())
1942 }
1943}
1944
1945fn subform_field_key(subform_path: &str) -> String {
1952 let stripped = subform_path.trim_start_matches('#').trim_start_matches('/');
1954
1955 stripped
1957 .split('/')
1958 .filter(|seg| !seg.is_empty() && *seg != "properties")
1959 .last()
1960 .unwrap_or(stripped)
1961 .to_string()
1962}