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 extended_paths: Vec<String> = {
120 let mut paths = changed_paths.to_vec();
121 for item in &result {
122 if let Some(ref_val) = item.get("$ref").and_then(|v| v.as_str()) {
123 let s = ref_val.to_string();
124 if !paths.contains(&s) {
125 paths.push(s);
126 }
127 }
128 }
129 paths
130 };
131 let subform_invalidated_tables = time_block!(" [dep] run_subform_pass", {
132 self.run_subform_pass(
133 &extended_paths,
134 changed_paths,
135 re_evaluate,
136 token,
137 &mut result,
138 )
139 })?;
140
141 if subform_invalidated_tables {
149 let _lock2 = self.eval_lock.lock().unwrap();
150 drop(_lock2);
151 self.evaluate_internal(None, token)?;
152
153 self.run_subform_pass(&[], &[], true, token, &mut result)?;
158
159 for (subform_path, _) in &self.subforms {
161 let data_ptr = path_utils::schema_path_to_data_pointer(subform_path);
162 let data_ptr_str = data_ptr.to_string();
163 let dot_path = data_ptr_str.trim_start_matches('/').replace('/', ".");
164
165 if let Some(fresh_val) = self.eval_data.get(&data_ptr_str) {
166 let mut patched = false;
167 for item in result.iter_mut() {
168 if item
169 .get("$ref")
170 .and_then(|r| r.as_str())
171 .map(|r| r == dot_path)
172 .unwrap_or(false)
173 {
174 if let Some(map) = item.as_object_mut() {
175 map.remove("clear");
176 map.insert("value".to_string(), fresh_val.clone());
177 }
178 patched = true;
179 break;
180 }
181 }
182 if !patched {
183 let mut obj = serde_json::Map::new();
184 obj.insert("$ref".to_string(), serde_json::Value::String(dot_path));
185 obj.insert("value".to_string(), fresh_val.clone());
186 result.push(serde_json::Value::Object(obj));
187 }
188 }
189 }
190 }
191 }
192
193 let deduped = {
198 let mut seen: IndexMap<String, usize> = IndexMap::new();
199 for (i, item) in result.iter().enumerate() {
200 if let Some(r) = item.get("$ref").and_then(|v| v.as_str()) {
201 seen.insert(r.to_string(), i);
202 }
203 }
204 let last_indices: IndexSet<usize> = seen.values().copied().collect();
205 let out: Vec<Value> = result
206 .into_iter()
207 .enumerate()
208 .filter(|(i, _)| last_indices.contains(i))
209 .map(|(_, item)| item)
210 .collect();
211 out
212 };
213
214 if self.eval_cache.active_item_index.is_none() {
222 let current_snapshot = self.eval_data.snapshot_data_clone();
223 self.eval_cache.main_form_snapshot = Some(current_snapshot);
224 }
225
226 Ok(Value::Array(deduped))
227 }
228
229 fn run_re_evaluate_pass(
232 &mut self,
233 token: Option<&CancellationToken>,
234 to_process: &mut Vec<(String, bool, Option<Vec<usize>>)>,
235 processed: &mut std::collections::HashMap<String, Option<std::collections::HashSet<usize>>>,
236 result: &mut Vec<Value>,
237 mut canceled_paths: Option<&mut Vec<String>>,
238 ) -> Result<(), String> {
239 self.run_schema_default_value_pass(
241 token,
242 to_process,
243 processed,
244 result,
245 canceled_paths.as_mut().map(|v| &mut **v),
246 )?;
247
248 let pre_eval_versions = if let Some(idx) = self.eval_cache.active_item_index {
254 self.eval_cache
255 .subform_caches
256 .get(&idx)
257 .map(|c| c.data_versions.clone())
258 .unwrap_or_else(|| self.eval_cache.data_versions.clone())
259 } else {
260 self.eval_cache.data_versions.clone()
261 };
262
263 self.evaluate_internal(None, token)?;
264
265 let active_idx = self.eval_cache.active_item_index;
267 for eval_key in self.sorted_evaluations.iter().flatten() {
268 if eval_key.contains("/$params/") || eval_key.contains("/$") {
269 continue;
270 }
271
272 let schema_ptr = path_utils::schema_path_to_data_pointer(eval_key);
273 let data_path = schema_ptr.trim_start_matches('/').to_string();
274
275 let version_path = format!("/{}", data_path);
276 let old_ver = pre_eval_versions.get(&version_path);
277 let new_ver = if let Some(idx) = active_idx {
278 self.eval_cache
279 .subform_caches
280 .get(&idx)
281 .map(|c| c.data_versions.get(&version_path))
282 .unwrap_or_else(|| self.eval_cache.data_versions.get(&version_path))
283 } else {
284 self.eval_cache.data_versions.get(&version_path)
285 };
286
287 if new_ver > old_ver {
288 if let Some(new_val) = self.evaluated_schema.pointer(&schema_ptr) {
289 let dot_path = data_path.trim_end_matches("/value").replace('/', ".");
290 let mut obj = serde_json::Map::new();
291 obj.insert("$ref".to_string(), Value::String(dot_path));
292 let is_clear = new_val == &Value::Null || new_val.as_str() == Some("");
293 if is_clear {
294 obj.insert("clear".to_string(), Value::Bool(true));
295 } else {
296 obj.insert("value".to_string(), new_val.clone());
297 }
298 result.push(Value::Object(obj));
299 }
300 }
301 }
302
303 let mut readonly_changes = Vec::new();
305 let mut readonly_values = Vec::new();
306 for path in self.conditional_readonly_fields.iter() {
307 let normalized = path_utils::normalize_to_json_pointer(path);
308 if let Some(schema_el) = self.evaluated_schema.pointer(&normalized) {
309 self.check_readonly_for_dependents(
310 schema_el,
311 path,
312 &mut readonly_changes,
313 &mut readonly_values,
314 );
315 }
316 }
317 let had_actual_readonly_changes = !readonly_changes.is_empty();
320
321 let subform_data_paths: std::collections::HashSet<String> = self
327 .subforms
328 .keys()
329 .map(|p| {
330 path_utils::schema_path_to_data_pointer(p)
331 .replace("/value/", "/")
332 .to_string()
333 })
334 .collect();
335
336 for (path, schema_value) in readonly_changes {
337 let data_path = path_utils::schema_path_to_data_pointer(&path).replace("/value/", "/");
338
339 if subform_data_paths.contains(&data_path) {
340 if let (Value::Array(schema_items), Some(Value::Array(existing_items))) = (
342 &schema_value,
343 self.eval_data.data().pointer(&data_path).cloned().as_ref(),
344 ) {
345 let mut merged_items = existing_items.clone();
346 for (i, schema_item) in schema_items.iter().enumerate() {
347 if let (Some(existing), Value::Object(schema_map)) =
348 (merged_items.get_mut(i), schema_item)
349 {
350 if let Some(existing_map) = existing.as_object_mut() {
351 for (k, v) in schema_map {
352 if !v.is_object() {
353 existing_map.insert(k.clone(), v.clone());
354 }
355 }
356 }
357 } else if i >= merged_items.len() {
358 merged_items.push(schema_item.clone());
359 }
360 }
361 self.eval_data.set(&data_path, Value::Array(merged_items));
362 } else {
363 self.eval_data.set(&data_path, schema_value.clone());
364 }
365 self.eval_cache.bump_data_version(&data_path);
366 to_process.push((path, true, None));
367 continue;
368 }
369
370 self.eval_data.set(&data_path, schema_value.clone());
371 self.eval_cache.bump_data_version(&data_path);
372 to_process.push((path, true, None));
373 }
374 if had_actual_readonly_changes {
380 let readonly_dep_prefixes: Vec<String> = to_process
381 .iter()
382 .map(|(path, _, _)| path.trim_start_matches('#').to_string())
383 .collect();
384 let params_table_keys: Vec<String> = self
385 .table_metadata
386 .keys()
387 .filter(|key| key.starts_with("#/$params"))
388 .filter(|key| {
389 self.dependencies
390 .get(*key)
391 .map(|deps| {
392 deps.iter().any(|dep| {
393 readonly_dep_prefixes.iter().any(|readonly| {
394 dep == readonly
395 || dep
396 .strip_prefix(readonly)
397 .is_some_and(|suffix| suffix.starts_with('/'))
398 })
399 })
400 })
401 .unwrap_or(false)
402 })
403 .cloned()
404 .collect();
405
406 if !params_table_keys.is_empty() {
407 if let Some(active_idx) = self.eval_cache.active_item_index {
408 self.eval_cache
409 .invalidate_params_tables_for_item(active_idx, ¶ms_table_keys);
410 }
411 self.evaluate_internal(None, token)?;
412
413 readonly_values.clear();
417 for path in self.conditional_readonly_fields.iter() {
418 let normalized = path_utils::normalize_to_json_pointer(path);
419 if let Some(schema_el) = self.evaluated_schema.pointer(&normalized) {
420 self.check_readonly_for_dependents(
421 schema_el,
422 path,
423 &mut Vec::new(),
424 &mut readonly_values,
425 );
426 }
427 }
428 }
429 }
430
431 for (path, schema_value) in readonly_values {
432 let data_path = path_utils::schema_path_to_data_pointer(&path).replace("/value/", "/");
433 let mut obj = serde_json::Map::new();
434 obj.insert(
435 "$ref".to_string(),
436 Value::String(path_utils::pointer_to_dot_notation(&data_path)),
437 );
438 obj.insert("$readonly".to_string(), Value::Bool(true));
439 let is_clear = schema_value == Value::Null || schema_value.as_str() == Some("");
440 if is_clear {
441 obj.insert("clear".to_string(), Value::Bool(true));
442 } else {
443 obj.insert("value".to_string(), schema_value);
444 }
445 result.push(Value::Object(obj));
446 }
447
448 if !to_process.is_empty() {
449 Self::process_dependents_queue(
450 &self.engine,
451 &self.evaluations,
452 &mut self.eval_data,
453 &mut self.eval_cache,
454 &self.dependents_evaluations,
455 &self.dep_formula_triggers,
456 &self.evaluated_schema,
457 to_process,
458 processed,
459 result,
460 token,
461 canceled_paths.as_mut().map(|v| &mut **v),
462 )?;
463 }
464
465 self.resolve_layout(false)?;
470
471 let mut hidden_fields = Vec::new();
472 for path in self.conditional_hidden_fields.iter() {
473 let normalized = path_utils::normalize_to_json_pointer(path);
474 if let Some(schema_el) = self.evaluated_schema.pointer(&normalized) {
475 self.check_hidden_field(schema_el, path, &mut hidden_fields);
476 }
477 }
478 for path in self.layout_condition_hidden_refs.iter() {
479 if let Some(schema_el) = self.evaluated_schema.pointer(path) {
480 self.check_effectively_hidden_field(schema_el, path, &mut hidden_fields);
481 }
482 }
483 hidden_fields.sort();
484 hidden_fields.dedup();
485 if !hidden_fields.is_empty() {
486 Self::recursive_hide_effect(
487 &self.engine,
488 &self.evaluations,
489 &self.reffed_by,
490 &mut self.eval_data,
491 &mut self.eval_cache,
492 hidden_fields,
493 to_process,
494 result,
495 );
496 }
497 if !to_process.is_empty() {
498 Self::process_dependents_queue(
499 &self.engine,
500 &self.evaluations,
501 &mut self.eval_data,
502 &mut self.eval_cache,
503 &self.dependents_evaluations,
504 &self.dep_formula_triggers,
505 &self.evaluated_schema,
506 to_process,
507 processed,
508 result,
509 token,
510 canceled_paths.as_mut().map(|v| &mut **v),
511 )?;
512 }
513
514 Ok(())
515 }
516
517 fn collect_visible_static_defaults(&self) -> Vec<(String, Value, String)> {
519 let mut defaults = Vec::new();
520 let schema_values = self.get_schema_value_array();
521
522 if let Value::Array(values) = schema_values {
523 for item in values {
524 let Value::Object(map) = item else {
525 continue;
526 };
527 let Some(Value::String(dot_path)) = map.get("path") else {
528 continue;
529 };
530 let Some(schema_val) = map.get("value") else {
531 continue;
532 };
533
534 let schema_ptr = path_utils::dot_notation_to_schema_pointer(dot_path);
535 if let Some(Value::Object(schema_node)) = self
536 .evaluated_schema
537 .pointer(schema_ptr.trim_start_matches('#'))
538 {
539 if let Some(Value::Object(condition)) = schema_node.get("condition") {
540 if let Some(hidden_val) = condition.get("hidden") {
541 if !hidden_val.is_boolean() || hidden_val.as_bool() == Some(true) {
542 continue;
543 }
544 }
545 }
546 }
547
548 let data_path = dot_path.replace('.', "/");
549 let current_data = self
550 .eval_data
551 .data()
552 .pointer(&format!("/{}", data_path))
553 .unwrap_or(&Value::Null);
554 let is_empty = matches!(current_data, Value::Null)
555 || matches!(current_data, Value::String(s) if s.is_empty());
556 let is_schema_val_empty = matches!(schema_val, Value::Null)
557 || matches!(schema_val, Value::String(s) if s.is_empty())
558 || matches!(schema_val, Value::Object(map) if map.contains_key("$evaluation"));
559
560 if is_empty && !is_schema_val_empty && current_data != schema_val {
561 defaults.push((data_path, schema_val.clone(), dot_path.clone()));
562 }
563 }
564 }
565
566 defaults
567 }
568
569 pub(crate) fn apply_visible_static_defaults(&mut self) -> bool {
570 let defaults = self.collect_visible_static_defaults();
571 for (data_path, schema_val, _) in &defaults {
572 self.eval_data
573 .set(&format!("/{}", data_path), schema_val.clone());
574 self.eval_cache
575 .bump_data_version(&format!("/{}", data_path));
576 }
577 !defaults.is_empty()
578 }
579
580 pub(crate) fn apply_visible_static_defaults_with_dependents(
586 &mut self,
587 token: Option<&CancellationToken>,
588 ) -> Result<bool, String> {
589 if self.collect_visible_static_defaults().is_empty() {
590 return Ok(false);
591 }
592
593 let mut to_process = Vec::new();
594 let mut processed = std::collections::HashMap::new();
595 let mut result = Vec::new();
596 self.run_schema_default_value_pass(
597 token,
598 &mut to_process,
599 &mut processed,
600 &mut result,
601 None,
602 )?;
603 Ok(true)
604 }
605
606 fn run_schema_default_value_pass(
609 &mut self,
610 token: Option<&CancellationToken>,
611 to_process: &mut Vec<(String, bool, Option<Vec<usize>>)>,
612 processed: &mut std::collections::HashMap<String, Option<std::collections::HashSet<usize>>>,
613 result: &mut Vec<Value>,
614 mut canceled_paths: Option<&mut Vec<String>>,
615 ) -> Result<(), String> {
616 let mut default_value_changes = Vec::new();
617 let schema_values = self.get_schema_value_array();
618
619 if let Value::Array(values) = schema_values {
620 for item in values {
621 if let Value::Object(map) = item {
622 if let (Some(Value::String(dot_path)), Some(schema_val)) =
623 (map.get("path"), map.get("value"))
624 {
625 let schema_ptr = path_utils::dot_notation_to_schema_pointer(dot_path);
626 if let Some(Value::Object(schema_node)) = self
627 .evaluated_schema
628 .pointer(schema_ptr.trim_start_matches('#'))
629 {
630 if let Some(Value::Object(condition)) = schema_node.get("condition") {
631 if let Some(hidden_val) = condition.get("hidden") {
632 if !hidden_val.is_boolean()
634 || hidden_val.as_bool() == Some(true)
635 {
636 continue;
637 }
638 }
639 }
640 }
641
642 let data_path = dot_path.replace('.', "/");
643 let current_data = self
644 .eval_data
645 .data()
646 .pointer(&format!("/{}", data_path))
647 .unwrap_or(&Value::Null);
648
649 let is_empty = match current_data {
650 Value::Null => true,
651 Value::String(s) if s.is_empty() => true,
652 _ => false,
653 };
654
655 let is_schema_val_empty = match schema_val {
656 Value::Null => true,
657 Value::String(s) if s.is_empty() => true,
658 Value::Object(map) if map.contains_key("$evaluation") => true,
659 _ => false,
660 };
661
662 if is_empty && !is_schema_val_empty && current_data != schema_val {
663 default_value_changes.push((
664 data_path,
665 schema_val.clone(),
666 dot_path.clone(),
667 ));
668 }
669 }
670 }
671 }
672 }
673
674 let mut has_changes = false;
675 for (data_path, schema_val, dot_path) in default_value_changes {
676 self.eval_data
677 .set(&format!("/{}", data_path), schema_val.clone());
678 self.eval_cache
679 .bump_data_version(&format!("/{}", data_path));
680
681 let mut change_obj = serde_json::Map::new();
682 change_obj.insert("$ref".to_string(), Value::String(dot_path));
683 let is_clear = schema_val == Value::Null || schema_val.as_str() == Some("");
684 if is_clear {
685 change_obj.insert("clear".to_string(), Value::Bool(true));
686 } else {
687 change_obj.insert("value".to_string(), schema_val);
688 }
689 result.push(Value::Object(change_obj));
690
691 let schema_ptr = format!("#/{}", data_path.replace('/', "/properties/"));
692 to_process.push((schema_ptr, true, None));
693 has_changes = true;
694 }
695
696 if has_changes {
697 Self::process_dependents_queue(
698 &self.engine,
699 &self.evaluations,
700 &mut self.eval_data,
701 &mut self.eval_cache,
702 &self.dependents_evaluations,
703 &self.dep_formula_triggers,
704 &self.evaluated_schema,
705 to_process,
706 processed,
707 result,
708 token,
709 canceled_paths.as_mut().map(|v| &mut **v),
710 )?;
711 }
712
713 Ok(())
714 }
715
716 fn run_subform_pass(
726 &mut self,
727 changed_paths: &[String],
728 parent_changed_paths: &[String],
729 _re_evaluate: bool,
730 token: Option<&CancellationToken>,
731 result: &mut Vec<Value>,
732 ) -> Result<bool, String> {
733 let mut any_table_invalidated = false;
734 let subform_paths: Vec<String> = self.subforms.keys().cloned().collect();
736
737 for subform_path in subform_paths {
738 let field_key = subform_field_key(&subform_path);
739 let subform_dot_path =
741 path_utils::pointer_to_dot_notation(&subform_path).replace(".properties.", ".");
742 let field_prefix = format!("{}.", field_key);
743 let subform_ptr = normalize_to_json_pointer(&subform_path);
744
745 let item_count =
747 get_value_by_pointer_without_properties(self.eval_data.data(), &subform_ptr)
748 .and_then(|v| v.as_array())
749 .map(|a| a.len())
750 .unwrap_or(0);
751
752 if item_count == 0 {
753 continue;
754 }
755
756 self.eval_cache.prune_subform_caches(item_count);
759
760 let parent_data_versions_snapshot = self.eval_cache.data_versions.clone();
765 let parent_params_versions_snapshot = self.eval_cache.params_versions.clone();
766
767 for idx in 0..item_count {
768 let prefix_dot = format!("{}.{}.", subform_dot_path, idx);
770 let prefix_bracket = format!("{}[{}].", subform_dot_path, idx);
771 let prefix_field_bracket = format!("{}[{}].", field_key, idx);
772
773 let item_changed_paths: Vec<String> = changed_paths
774 .iter()
775 .filter_map(|p| {
776 if p.starts_with(&prefix_bracket) {
777 Some(p.replacen(&prefix_bracket, &field_prefix, 1))
778 } else if p.starts_with(&prefix_dot) {
779 Some(p.replacen(&prefix_dot, &field_prefix, 1))
780 } else if p.starts_with(&prefix_field_bracket) {
781 Some(p.replacen(&prefix_field_bracket, &field_prefix, 1))
782 } else {
783 None
784 }
785 })
786 .collect();
787
788 let item_val =
791 get_value_by_pointer_without_properties(self.eval_data.data(), &subform_ptr)
792 .and_then(|v| v.as_array())
793 .and_then(|a| a.get(idx))
794 .cloned()
795 .unwrap_or(Value::Null);
796
797 let merged_data = {
801 let parent = self.eval_data.data();
802 let mut map = serde_json::Map::new();
803 if let Value::Object(parent_map) = parent {
804 for (k, v) in parent_map {
805 if k == &field_key {
806 continue;
808 }
809 if !v.is_array() {
812 map.insert(k.clone(), v.clone());
813 }
814 }
815 }
816 map.insert(field_key.clone(), item_val.clone());
817 Value::Object(map)
818 };
819
820 let parent_dependency_paths: Vec<String> = parent_changed_paths
825 .iter()
826 .map(|path| {
827 path_utils::dot_notation_to_schema_pointer(path)
828 .trim_start_matches('#')
829 .to_string()
830 })
831 .collect();
832 let dependent_value_paths: Vec<String> = self
833 .subforms
834 .get(&subform_path)
835 .map(|subform| {
836 subform
837 .dependencies
838 .iter()
839 .filter(|(key, deps)| {
840 !subform.table_metadata.contains_key(*key)
841 && !key.starts_with("#/$params/")
842 && key.ends_with("/value")
843 && parent_dependency_paths
844 .iter()
845 .any(|dependency| deps.contains(dependency))
846 })
847 .map(|(key, _)| key.clone())
848 .collect()
849 })
850 .unwrap_or_default();
851
852 if item_changed_paths.is_empty() && !dependent_value_paths.is_empty() {
853 let parent_cache = std::mem::take(&mut self.eval_cache);
858 let mut overlay_cache = parent_cache.clone();
859 overlay_cache.ensure_active_item_cache(idx);
860 if let Some(item_cache) = overlay_cache.subform_caches.get_mut(&idx) {
861 item_cache
865 .data_versions
866 .merge_from(&parent_data_versions_snapshot);
867 item_cache
868 .data_versions
869 .merge_from_params(&parent_params_versions_snapshot);
870 }
871 overlay_cache.set_active_item(idx);
872 let subform = self
873 .subforms
874 .get_mut(&subform_path)
875 .expect("subform exists");
876 subform.eval_data.replace_data_and_context(
877 merged_data,
878 self.eval_data
879 .data()
880 .get("$context")
881 .cloned()
882 .unwrap_or(Value::Null),
883 );
884 std::mem::swap(&mut subform.eval_cache, &mut overlay_cache);
885
886 let refresh_table_outputs = dependent_value_paths.iter().any(|source| {
891 let source = source.trim_end_matches("/value").trim_start_matches('#');
892 let affected_tables: Vec<&String> = subform
893 .table_metadata
894 .keys()
895 .filter(|table| table.starts_with("#/$params"))
896 .filter(|table| {
897 subform.dependencies.get(*table).is_some_and(|deps| {
898 deps.iter().any(|dep| dep.trim_start_matches('#') == source)
899 })
900 })
901 .collect();
902
903 !affected_tables.is_empty()
904 && subform.evaluations.iter().any(|(target, _)| {
905 target.ends_with("/value")
906 && subform.dependencies.get(target).is_some_and(|deps| {
907 deps.iter().any(|dep| {
908 affected_tables.iter().any(|table| {
909 dep.trim_start_matches('#')
910 == table.trim_start_matches('#')
911 })
912 })
913 })
914 })
915 });
916 subform.evaluate_internal(Some(&dependent_value_paths), token)?;
917
918 let mut overlay_result = Vec::new();
919 let mut overlay_queue = Vec::new();
920 let mut overlay_processed = std::collections::HashMap::new();
921 for formula_path in &dependent_value_paths {
922 let schema_path = path_utils::normalize_to_json_pointer(formula_path);
923 let data_path = path_utils::schema_path_to_data_pointer(formula_path)
924 .replace("/value", "");
925 let Some(value) = subform.evaluated_schema.pointer(&schema_path).cloned()
926 else {
927 continue;
928 };
929
930 let source_path = formula_path.trim_end_matches("/value").to_string();
935 if subform.eval_data.get(&data_path) != Some(&value) {
936 subform.eval_data.set(&data_path, value.clone());
937 subform.eval_cache.bump_data_version(&data_path);
938 }
939 overlay_queue.push((source_path, true, None));
940
941 let mut change = serde_json::Map::new();
942 let field = data_path
943 .trim_start_matches('/')
944 .trim_end_matches("/value")
945 .replace('/', ".");
946 let field = field.strip_prefix(&field_prefix).unwrap_or(&field);
947 change.insert(
948 "$ref".to_string(),
949 Value::String(format!("{}.{}.{}", subform_dot_path, idx, field)),
950 );
951 if value == Value::Null || value.as_str() == Some("") {
952 change.insert("clear".to_string(), Value::Bool(true));
953 } else {
954 change.insert("value".to_string(), value);
955 }
956 result.push(Value::Object(change));
957 }
958
959 Self::process_dependents_queue(
960 &subform.engine,
961 &subform.evaluations,
962 &mut subform.eval_data,
963 &mut subform.eval_cache,
964 &subform.dependents_evaluations,
965 &subform.dep_formula_triggers,
966 &subform.evaluated_schema,
967 &mut overlay_queue,
968 &mut overlay_processed,
969 &mut overlay_result,
970 token,
971 None,
972 )?;
973
974 if refresh_table_outputs {
975 subform.run_re_evaluate_pass(
979 token,
980 &mut overlay_queue,
981 &mut overlay_processed,
982 &mut overlay_result,
983 None,
984 )?;
985 }
986
987 for change in overlay_result {
988 let Some(object) = change.as_object() else {
989 continue;
990 };
991 let Some(Value::String(ref_path)) = object.get("$ref") else {
992 continue;
993 };
994 let local_ref = ref_path.strip_prefix(&field_prefix).unwrap_or(ref_path);
995 let mut mapped = object.clone();
996 mapped.insert(
997 "$ref".to_string(),
998 Value::String(format!("{}.{}.{}", subform_dot_path, idx, local_ref)),
999 );
1000 result.push(Value::Object(mapped));
1001 }
1002
1003 std::mem::swap(&mut subform.eval_cache, &mut overlay_cache);
1006 self.eval_cache = parent_cache;
1007 continue;
1008 }
1009
1010 let Some(subform) = self.subforms.get_mut(&subform_path) else {
1011 continue;
1012 };
1013
1014 let sub_re_evaluate = !item_changed_paths.is_empty();
1019 if !sub_re_evaluate && item_changed_paths.is_empty() {
1020 continue;
1021 }
1022
1023 self.eval_cache.ensure_active_item_cache(idx);
1025 let old_item_val = {
1026 let snapshot = self
1027 .eval_cache
1028 .subform_caches
1029 .get(&idx)
1030 .map(|c| c.item_snapshot.clone())
1031 .unwrap_or(Value::Null);
1032
1033 if snapshot == Value::Null {
1034 if let Some(main_snap) = &self.eval_cache.main_form_snapshot {
1035 get_value_by_pointer_without_properties(main_snap, &subform_ptr)
1036 .and_then(|v| v.as_array())
1037 .and_then(|a| a.get(idx))
1038 .cloned()
1039 .unwrap_or(Value::Null)
1040 } else {
1041 Value::Null
1042 }
1043 } else {
1044 snapshot
1045 }
1046 };
1047
1048 subform.eval_data.replace_data_and_context(
1049 merged_data,
1050 self.eval_data
1051 .data()
1052 .get("$context")
1053 .cloned()
1054 .unwrap_or(Value::Null),
1055 );
1056 let new_item_val = subform
1057 .eval_data
1058 .data()
1059 .get(&field_key)
1060 .cloned()
1061 .unwrap_or(Value::Null);
1062
1063 let mut parent_cache = std::mem::take(&mut self.eval_cache);
1065 parent_cache.ensure_active_item_cache(idx);
1066
1067 let pre_diff_item_versions = parent_cache
1070 .subform_caches
1071 .get(&idx)
1072 .map(|c| c.data_versions.clone());
1073
1074 if let Some(c) = parent_cache.subform_caches.get_mut(&idx) {
1075 c.data_versions.merge_from(&parent_data_versions_snapshot);
1079 c.data_versions
1081 .merge_from_params(&parent_params_versions_snapshot);
1082 crate::jsoneval::eval_cache::diff_and_update_versions(
1083 &mut c.data_versions,
1084 &format!("/{}", field_key),
1085 &old_item_val,
1086 &new_item_val,
1087 "run_subform_pass_diff_and_update_versions",
1088 );
1089 c.item_snapshot = new_item_val;
1090 }
1091
1092 if let (Some(ref pre), Some(c)) = (
1096 &pre_diff_item_versions,
1097 parent_cache.subform_caches.get(&idx),
1098 ) {
1099 let field_prefix_slash = format!("/{}/", field_key);
1100 let newly_bumped: Vec<String> = c
1101 .data_versions
1102 .versions()
1103 .filter(|(k, &v)| k.starts_with(&field_prefix_slash) && v > pre.get(k))
1104 .map(|(k, _)| k.to_string())
1105 .collect();
1106 if !newly_bumped.is_empty() {
1107 for k in newly_bumped {
1108 parent_cache
1109 .data_versions
1110 .bump(&k, "propagate_newly_bumped");
1111 }
1112 parent_cache.eval_generation += 1;
1113 }
1114 }
1115
1116 {
1122 let field_prefix_slash = format!("/{}/", field_key);
1123 let newly_bumped_schema_paths: Vec<String> = if let (Some(ref pre), Some(c)) = (
1124 &pre_diff_item_versions,
1125 parent_cache.subform_caches.get(&idx),
1126 ) {
1127 c.data_versions
1128 .versions()
1129 .filter(|(k, &v)| k.starts_with(&field_prefix_slash) && v > pre.get(k))
1130 .map(|(k, _)| {
1131 let sub = k.trim_start_matches(&field_prefix_slash);
1135 format!(
1136 "/{}/properties/{}",
1137 field_key,
1138 sub.replace('/', "/properties/")
1139 )
1140 })
1141 .collect()
1142 } else {
1143 Vec::new()
1144 };
1145
1146 if !newly_bumped_schema_paths.is_empty() {
1147 let params_table_keys: Vec<String> = self
1148 .table_metadata
1149 .keys()
1150 .filter(|k| k.starts_with("#/$params"))
1151 .filter(|k| {
1152 self.dependencies
1153 .get(*k)
1154 .map(|deps| {
1155 deps.iter().any(|dep| {
1156 newly_bumped_schema_paths
1157 .iter()
1158 .any(|b| dep == b || dep.starts_with(b.as_str()))
1159 })
1160 })
1161 .unwrap_or(false)
1162 })
1163 .cloned()
1164 .collect();
1165
1166 if !params_table_keys.is_empty() {
1167 parent_cache.invalidate_params_tables_for_item(idx, ¶ms_table_keys);
1168 any_table_invalidated = true;
1169 }
1170 }
1171 }
1172
1173 parent_cache.set_active_item(idx);
1174 std::mem::swap(&mut subform.eval_cache, &mut parent_cache);
1175
1176 let subform_result = time_block!(" [subform_pass] rider evaluate_dependents", {
1177 subform.evaluate_dependents(
1178 &item_changed_paths,
1179 None,
1180 None,
1181 sub_re_evaluate,
1182 token,
1183 None,
1184 false,
1185 )
1186 });
1187
1188 std::mem::swap(&mut subform.eval_cache, &mut parent_cache);
1190 parent_cache.clear_active_item();
1191
1192 if let Some(parent_item_cache) = self.eval_cache.subform_caches.get(&idx) {
1197 let snapshot = parent_item_cache.item_snapshot.clone();
1198 subform.eval_cache.ensure_active_item_cache(idx);
1199 if let Some(sub_cache) = subform.eval_cache.subform_caches.get_mut(&idx) {
1200 sub_cache.item_snapshot = snapshot;
1201 }
1202 }
1203
1204 self.eval_cache = parent_cache;
1205
1206 if let Ok(Value::Array(changes)) = subform_result {
1207 let mut had_any_change = false;
1208 for change in changes {
1209 if let Some(obj) = change.as_object() {
1210 if let Some(Value::String(ref_path)) = obj.get("$ref") {
1211 let new_ref = if ref_path.starts_with(&field_prefix) {
1213 format!(
1214 "{}.{}.{}",
1215 subform_dot_path,
1216 idx,
1217 &ref_path[field_prefix.len()..]
1218 )
1219 } else {
1220 format!("{}.{}.{}", subform_dot_path, idx, ref_path)
1221 };
1222
1223 if let Some(val) = obj.get("value") {
1229 let data_ptr = format!("/{}", new_ref.replace('.', "/"));
1230 self.eval_data.set(&data_ptr, val.clone());
1231 had_any_change = true;
1232 } else if obj.get("clear").and_then(Value::as_bool) == Some(true) {
1233 let data_ptr = format!("/{}", new_ref.replace('.', "/"));
1234 self.eval_data.set(&data_ptr, Value::Null);
1235 had_any_change = true;
1236 }
1237
1238 let mut new_obj = obj.clone();
1239 new_obj.insert("$ref".to_string(), Value::String(new_ref));
1240 result.push(Value::Object(new_obj));
1241 } else {
1242 result.push(change);
1244 }
1245 }
1246 }
1247
1248 if had_any_change {
1256 let item_path = format!("{}/{}", subform_ptr, idx);
1257 let updated_item = self
1258 .eval_data
1259 .get(&item_path)
1260 .cloned()
1261 .unwrap_or(Value::Null);
1262 if let Some(c) = self.eval_cache.subform_caches.get_mut(&idx) {
1264 c.item_snapshot = updated_item.clone();
1265 }
1266 subform.eval_cache.ensure_active_item_cache(idx);
1269 if let Some(sub_cache) = subform.eval_cache.subform_caches.get_mut(&idx) {
1270 sub_cache.item_snapshot = updated_item;
1271 }
1272 }
1273 }
1274 }
1275 }
1276 Ok(any_table_invalidated)
1277 }
1278
1279 pub(crate) fn evaluate_dependent_value_static(
1281 engine: &RLogic,
1282 evaluations: &IndexMap<String, LogicId>,
1283 eval_data: &EvalData,
1284 value: &Value,
1285 changed_field_value: &Value,
1286 changed_field_ref_value: &Value,
1287 ) -> Result<Value, String> {
1288 match value {
1289 Value::String(eval_key) => {
1291 if let Some(logic_id) = evaluations.get(eval_key) {
1292 let mut internal_context = serde_json::Map::new();
1295 internal_context.insert("$value".to_string(), changed_field_value.clone());
1296 internal_context.insert("$refValue".to_string(), changed_field_ref_value.clone());
1297 let context_value = Value::Object(internal_context);
1298
1299 let result = engine.run_with_context(logic_id, eval_data.data(), &context_value)
1300 .map_err(|e| format!("Failed to evaluate dependent logic '{}': {}", eval_key, e))?;
1301 Ok(result)
1302 } else {
1303 Ok(value.clone())
1305 }
1306 }
1307 Value::Object(map) if map.contains_key("$evaluation") => {
1310 Err("Dependent evaluation contains unparsed $evaluation - schema was not properly parsed".to_string())
1311 }
1312 _ => Ok(value.clone()),
1314 }
1315 }
1316
1317 pub(crate) fn check_readonly_for_dependents(
1319 &self,
1320 schema_element: &Value,
1321 path: &str,
1322 changes: &mut Vec<(String, Value)>,
1323 all_values: &mut Vec<(String, Value)>,
1324 ) {
1325 match schema_element {
1326 Value::Object(map) => {
1327 let mut is_disabled = false;
1329 if let Some(Value::Object(condition)) = map.get("condition") {
1330 if let Some(Value::Bool(d)) = condition.get("disabled") {
1331 is_disabled = *d;
1332 }
1333 }
1334
1335 let mut skip_readonly = false;
1337 if let Some(Value::Object(config)) = map.get("config") {
1338 if let Some(Value::Object(all)) = config.get("all") {
1339 if let Some(Value::Bool(skip)) = all.get("skipReadOnlyValue") {
1340 skip_readonly = *skip;
1341 }
1342 }
1343 }
1344
1345 if is_disabled && !skip_readonly {
1346 if let Some(schema_value) = map.get("value") {
1347 let data_path = path_utils::schema_path_to_data_pointer(path)
1348 .replace("/value/", "/");
1351
1352 let current_data = self
1353 .eval_data
1354 .data()
1355 .pointer(&data_path)
1356 .unwrap_or(&Value::Null);
1357
1358 all_values.push((path.to_string(), schema_value.clone()));
1361 if current_data != schema_value {
1362 changes.push((path.to_string(), schema_value.clone()));
1363 }
1364 }
1365 }
1366 }
1367 _ => {}
1368 }
1369 }
1370
1371 #[allow(dead_code)]
1373 pub(crate) fn collect_readonly_fixes(
1374 &self,
1375 schema_element: &Value,
1376 path: &str,
1377 changes: &mut Vec<(String, Value)>,
1378 ) {
1379 match schema_element {
1380 Value::Object(map) => {
1381 let mut is_disabled = false;
1383 if let Some(Value::Object(condition)) = map.get("condition") {
1384 if let Some(Value::Bool(d)) = condition.get("disabled") {
1385 is_disabled = *d;
1386 }
1387 }
1388
1389 let mut skip_readonly = false;
1391 if let Some(Value::Object(config)) = map.get("config") {
1392 if let Some(Value::Object(all)) = config.get("all") {
1393 if let Some(Value::Bool(skip)) = all.get("skipReadOnlyValue") {
1394 skip_readonly = *skip;
1395 }
1396 }
1397 }
1398
1399 if is_disabled && !skip_readonly {
1400 if let Some(schema_value) = map.get("value") {
1404 let data_path = path_utils::schema_path_to_data_pointer(path).into_owned();
1405
1406 let current_data = self
1407 .eval_data
1408 .data()
1409 .pointer(&data_path)
1410 .unwrap_or(&Value::Null);
1411
1412 if current_data != schema_value {
1413 changes.push((path.to_string(), schema_value.clone()));
1414 }
1415 }
1416 }
1417
1418 if let Some(Value::Object(props)) = map.get("properties") {
1420 for (key, val) in props {
1421 let next_path = if path == "#" {
1422 format!("#/properties/{}", key)
1423 } else {
1424 format!("{}/properties/{}", path, key)
1425 };
1426 self.collect_readonly_fixes(val, &next_path, changes);
1427 }
1428 }
1429 }
1430 _ => {}
1431 }
1432 }
1433
1434 pub(crate) fn check_hidden_field(
1436 &self,
1437 schema_element: &Value,
1438 path: &str,
1439 hidden_fields: &mut Vec<String>,
1440 ) {
1441 match schema_element {
1442 Value::Object(map) => {
1443 let mut is_hidden = false;
1445 if let Some(Value::Object(condition)) = map.get("condition") {
1446 if let Some(Value::Bool(h)) = condition.get("hidden") {
1447 is_hidden = *h;
1448 }
1449 }
1450
1451 let mut keep_hidden = false;
1453 if let Some(Value::Object(config)) = map.get("config") {
1454 if let Some(Value::Object(all)) = config.get("all") {
1455 if let Some(Value::Bool(keep)) = all.get("keepHiddenValue") {
1456 keep_hidden = *keep;
1457 }
1458 }
1459 }
1460
1461 if is_hidden && !keep_hidden {
1462 let data_path = path_utils::schema_path_to_data_pointer(path).into_owned();
1463
1464 let current_data = self
1465 .eval_data
1466 .data()
1467 .pointer(&data_path)
1468 .unwrap_or(&Value::Null);
1469
1470 if current_data != &Value::Null && current_data != "" {
1472 hidden_fields.push(path.to_string());
1473 }
1474 }
1475 }
1476 _ => {}
1477 }
1478 }
1479
1480 fn check_effectively_hidden_field(
1482 &self,
1483 schema_element: &Value,
1484 path: &str,
1485 hidden_fields: &mut Vec<String>,
1486 ) {
1487 let Value::Object(map) = schema_element else {
1488 return;
1489 };
1490
1491 let keep_hidden = map
1492 .get("config")
1493 .and_then(Value::as_object)
1494 .and_then(|config| config.get("all"))
1495 .and_then(Value::as_object)
1496 .and_then(|all| all.get("keepHiddenValue"))
1497 .and_then(Value::as_bool)
1498 .unwrap_or(false);
1499 if keep_hidden {
1500 return;
1501 }
1502
1503 let current_data = self
1504 .eval_data
1505 .data()
1506 .pointer(&path_utils::schema_path_to_data_pointer(path))
1507 .unwrap_or(&Value::Null);
1508 if current_data != &Value::Null && current_data != "" {
1509 hidden_fields.push(path.to_string());
1510 }
1511 }
1512
1513 #[allow(dead_code)]
1515 pub(crate) fn collect_hidden_fields(
1516 &self,
1517 schema_element: &Value,
1518 path: &str,
1519 hidden_fields: &mut Vec<String>,
1520 ) {
1521 match schema_element {
1522 Value::Object(map) => {
1523 let mut is_hidden = false;
1525 if let Some(Value::Object(condition)) = map.get("condition") {
1526 if let Some(Value::Bool(h)) = condition.get("hidden") {
1527 is_hidden = *h;
1528 }
1529 }
1530
1531 let mut keep_hidden = false;
1533 if let Some(Value::Object(config)) = map.get("config") {
1534 if let Some(Value::Object(all)) = config.get("all") {
1535 if let Some(Value::Bool(keep)) = all.get("keepHiddenValue") {
1536 keep_hidden = *keep;
1537 }
1538 }
1539 }
1540
1541 if is_hidden && !keep_hidden {
1542 let data_path = path_utils::schema_path_to_data_pointer(path).into_owned();
1543
1544 let current_data = self
1545 .eval_data
1546 .data()
1547 .pointer(&data_path)
1548 .unwrap_or(&Value::Null);
1549
1550 if current_data != &Value::Null && current_data != "" {
1552 hidden_fields.push(path.to_string());
1553 }
1554 }
1555
1556 for (key, val) in map {
1558 if key == "properties" {
1559 if let Value::Object(props) = val {
1560 for (p_key, p_val) in props {
1561 let next_path = if path == "#" {
1562 format!("#/properties/{}", p_key)
1563 } else {
1564 format!("{}/properties/{}", path, p_key)
1565 };
1566 self.collect_hidden_fields(p_val, &next_path, hidden_fields);
1567 }
1568 }
1569 } else if let Value::Object(_) = val {
1570 if key == "condition"
1572 || key == "config"
1573 || key == "rules"
1574 || key == "dependents"
1575 || key == "hideLayout"
1576 || key == "$layout"
1577 || key == "$params"
1578 || key == "definitions"
1579 || key == "$defs"
1580 || key.starts_with('$')
1581 {
1582 continue;
1583 }
1584
1585 let next_path = if path == "#" {
1586 format!("#/{}", key)
1587 } else {
1588 format!("{}/{}", path, key)
1589 };
1590 self.collect_hidden_fields(val, &next_path, hidden_fields);
1591 }
1592 }
1593 }
1594 _ => {}
1595 }
1596 }
1597
1598 pub(crate) fn recursive_hide_effect(
1601 engine: &RLogic,
1602 evaluations: &IndexMap<String, LogicId>,
1603 reffed_by: &IndexMap<String, Vec<String>>,
1604 eval_data: &mut EvalData,
1605 eval_cache: &mut crate::jsoneval::eval_cache::EvalCache,
1606 mut hidden_fields: Vec<String>,
1607 queue: &mut Vec<(String, bool, Option<Vec<usize>>)>,
1608 result: &mut Vec<Value>,
1609 ) {
1610 while let Some(hf) = hidden_fields.pop() {
1611 let data_path = path_utils::schema_path_to_data_pointer(&hf).into_owned();
1612
1613 eval_data.set(&data_path, Value::Null);
1615 eval_cache.bump_data_version(&data_path);
1616
1617 let mut change_obj = serde_json::Map::new();
1619 change_obj.insert(
1620 "$ref".to_string(),
1621 Value::String(path_utils::pointer_to_dot_notation(&data_path)),
1622 );
1623 change_obj.insert("$hidden".to_string(), Value::Bool(true));
1624 change_obj.insert("clear".to_string(), Value::Bool(true));
1625 result.push(Value::Object(change_obj));
1626
1627 queue.push((hf.clone(), true, None));
1629
1630 if let Some(referencing_fields) = reffed_by.get(&data_path) {
1632 for rb in referencing_fields {
1633 let hidden_eval_key = format!("{}/condition/hidden", rb);
1637
1638 if let Some(logic_id) = evaluations.get(&hidden_eval_key) {
1639 let rb_data_path = path_utils::schema_path_to_data_pointer(rb).into_owned();
1646 let rb_value = eval_data
1647 .data()
1648 .pointer(&rb_data_path)
1649 .cloned()
1650 .unwrap_or(Value::Null);
1651
1652 if let Ok(Value::Bool(is_hidden)) = engine.run(logic_id, eval_data.data()) {
1654 if is_hidden {
1655 if !hidden_fields.contains(rb) {
1658 let has_value = rb_value != Value::Null && rb_value != "";
1659 if has_value {
1660 hidden_fields.push(rb.clone());
1661 }
1662 }
1663 }
1664 }
1665 }
1666 }
1667 }
1668 }
1669 }
1670
1671 pub(crate) fn process_dependents_queue(
1674 engine: &RLogic,
1675 evaluations: &IndexMap<String, LogicId>,
1676 eval_data: &mut EvalData,
1677 eval_cache: &mut crate::jsoneval::eval_cache::EvalCache,
1678 dependents_evaluations: &IndexMap<String, Vec<DependentItem>>,
1679 dep_formula_triggers: &IndexMap<String, Vec<(String, usize)>>,
1680 evaluated_schema: &Value,
1681 queue: &mut Vec<(String, bool, Option<Vec<usize>>)>,
1682 processed: &mut std::collections::HashMap<String, Option<std::collections::HashSet<usize>>>,
1683 result: &mut Vec<Value>,
1684 token: Option<&CancellationToken>,
1685 canceled_paths: Option<&mut Vec<String>>,
1686 ) -> Result<(), String> {
1687 while let Some((current_path, is_transitive, target_indices)) = queue.pop() {
1688 if let Some(t) = token {
1689 if t.is_cancelled() {
1690 if let Some(cp) = canceled_paths {
1691 cp.push(current_path.clone());
1692 for (path, _, _) in queue.iter() {
1693 cp.push(path.clone());
1694 }
1695 }
1696 return Err("Cancelled".to_string());
1697 }
1698 }
1699
1700 let (should_run, indices_to_run) = match processed.get(¤t_path) {
1701 Some(None) => {
1702 continue;
1704 }
1705 Some(Some(already_processed_indices)) => {
1706 if let Some(targets) = &target_indices {
1707 let new_targets: std::collections::HashSet<usize> = targets
1708 .iter()
1709 .copied()
1710 .filter(|i| !already_processed_indices.contains(i))
1711 .collect();
1712 if new_targets.is_empty() {
1713 continue;
1714 }
1715 (true, Some(new_targets))
1716 } else {
1717 (true, None)
1718 }
1719 }
1720 None => (
1721 true,
1722 target_indices.clone().map(|t| t.into_iter().collect()),
1723 ),
1724 };
1725
1726 if !should_run {
1727 continue;
1728 }
1729
1730 let new_processed_state = if let Some(targets_to_run) = &indices_to_run {
1731 match processed.get(¤t_path) {
1732 Some(Some(existing_targets)) => {
1733 let mut copy = existing_targets.clone();
1734 for t in targets_to_run {
1735 copy.insert(*t);
1736 }
1737 Some(copy)
1738 }
1739 _ => Some(targets_to_run.clone()),
1740 }
1741 } else {
1742 None
1743 };
1744 processed.insert(current_path.clone(), new_processed_state);
1745
1746 let current_data_path =
1748 path_utils::schema_path_to_data_pointer(¤t_path).into_owned();
1749 let mut current_value = eval_data
1750 .data()
1751 .pointer(¤t_data_path)
1752 .cloned()
1753 .unwrap_or(Value::Null);
1754
1755 if let Some(formula_sources) = dep_formula_triggers.get(¤t_data_path) {
1760 let mut targets_by_source: std::collections::HashMap<String, Vec<usize>> =
1761 std::collections::HashMap::new();
1762 for (source_schema_path, dep_idx) in formula_sources {
1763 let source_ptr = path_utils::dot_notation_to_schema_pointer(source_schema_path);
1764 targets_by_source
1765 .entry(source_ptr)
1766 .or_default()
1767 .push(*dep_idx);
1768 }
1769 for (source_ptr, targets) in targets_by_source {
1770 if let Some(None) = processed.get(&source_ptr) {
1772 continue;
1773 }
1774 queue.push((source_ptr, true, Some(targets)));
1775 }
1776 }
1777
1778 if let Some(dependent_items) = dependents_evaluations.get(¤t_path) {
1780 for (dep_idx, dep_item) in dependent_items.iter().enumerate() {
1781 if let Some(targets) = &indices_to_run {
1782 if !targets.contains(&dep_idx) {
1783 continue;
1784 }
1785 }
1786 let ref_path = &dep_item.ref_path;
1787 let pointer_path = path_utils::normalize_to_json_pointer(ref_path);
1788 let data_path =
1790 crate::jsoneval::path_utils::schema_path_to_data_pointer(&pointer_path)
1791 .into_owned();
1792
1793 let current_ref_value = eval_data
1794 .data()
1795 .pointer(&data_path)
1796 .cloned()
1797 .unwrap_or(Value::Null);
1798
1799 let field = evaluated_schema.pointer(&pointer_path).cloned();
1801
1802 let parent_path = if let Some(last_slash) = pointer_path.rfind("/properties") {
1804 &pointer_path[..last_slash]
1805 } else {
1806 "/"
1807 };
1808 let mut parent_field = if parent_path.is_empty() || parent_path == "/" {
1809 evaluated_schema.clone()
1810 } else {
1811 evaluated_schema
1812 .pointer(parent_path)
1813 .cloned()
1814 .unwrap_or_else(|| Value::Object(serde_json::Map::new()))
1815 };
1816
1817 if let Value::Object(ref mut map) = parent_field {
1819 map.remove("properties");
1820 map.remove("$layout");
1821 }
1822
1823 let mut change_obj = serde_json::Map::new();
1824 change_obj.insert(
1825 "$ref".to_string(),
1826 Value::String(path_utils::pointer_to_dot_notation(&data_path)),
1827 );
1828 if let Some(f) = field {
1829 change_obj.insert("$field".to_string(), f);
1830 }
1831 change_obj.insert("$parentField".to_string(), parent_field);
1832 change_obj.insert("transitive".to_string(), Value::Bool(is_transitive));
1833
1834 if processed.contains_key(ref_path) {
1839 continue;
1840 }
1841
1842 let mut add_transitive = false;
1843 let mut add_deps = false;
1844 if let Some(clear_val) = &dep_item.clear {
1846 let should_clear = Self::evaluate_dependent_value_static(
1847 engine,
1848 evaluations,
1849 eval_data,
1850 clear_val,
1851 ¤t_value,
1852 ¤t_ref_value,
1853 )?;
1854 let clear_bool = match should_clear {
1855 Value::Bool(b) => b,
1856 _ => false,
1857 };
1858
1859 if clear_bool {
1860 if data_path == current_data_path {
1861 current_value = Value::Null;
1862 }
1863 eval_data.set(&data_path, Value::Null);
1864 eval_cache.bump_data_version(&data_path);
1865 change_obj.insert("clear".to_string(), Value::Bool(true));
1866 add_transitive = true;
1867 add_deps = true;
1868 }
1869 }
1870
1871 if let Some(value_val) = &dep_item.value {
1873 let computed_value = Self::evaluate_dependent_value_static(
1874 engine,
1875 evaluations,
1876 eval_data,
1877 value_val,
1878 ¤t_value,
1879 ¤t_ref_value,
1880 )?;
1881 let cleaned_val = clean_float_noise_scalar(computed_value);
1882
1883 let is_clear =
1884 cleaned_val == Value::Null || cleaned_val.as_str() == Some("");
1885
1886 if cleaned_val != current_ref_value && !is_clear {
1887 if data_path == current_data_path {
1888 current_value = cleaned_val.clone();
1889 }
1890 eval_data.set(&data_path, cleaned_val.clone());
1891 eval_cache.bump_data_version(&data_path);
1892 change_obj.insert("value".to_string(), cleaned_val);
1893 add_transitive = true;
1894 add_deps = true;
1895 }
1896 }
1897
1898 if add_deps {
1900 result.push(Value::Object(change_obj));
1901 }
1902
1903 if add_transitive {
1905 queue.push((ref_path.clone(), true, None));
1906 }
1907 }
1908 }
1909 }
1910 Ok(())
1911 }
1912}
1913
1914fn subform_field_key(subform_path: &str) -> String {
1921 let stripped = subform_path.trim_start_matches('#').trim_start_matches('/');
1923
1924 stripped
1926 .split('/')
1927 .filter(|seg| !seg.is_empty() && *seg != "properties")
1928 .last()
1929 .unwrap_or(stripped)
1930 .to_string()
1931}