1#[cfg(windows)]
11#[global_allocator]
12static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
13
14pub mod rlogic;
15pub mod table_evaluate;
16pub mod table_metadata;
17pub mod topo_sort;
18pub mod parse_schema;
19
20pub mod parsed_schema;
21pub mod parsed_schema_cache;
22pub mod json_parser;
23pub mod path_utils;
24pub mod eval_data;
25pub mod eval_cache;
26pub mod subform_methods;
27
28#[cfg(feature = "ffi")]
30pub mod ffi;
31
32#[cfg(feature = "wasm")]
34pub mod wasm;
35
36use indexmap::{IndexMap, IndexSet};
38pub use rlogic::{
39 CompiledLogic, CompiledLogicStore, Evaluator,
40 LogicId, RLogic, RLogicConfig,
41 CompiledLogicId, CompiledLogicStoreStats,
42};
43use serde::{Deserialize, Serialize};
44pub use table_metadata::TableMetadata;
45pub use path_utils::ArrayMetadata;
46pub use eval_data::EvalData;
47pub use eval_cache::{EvalCache, CacheKey, CacheStats};
48pub use parsed_schema::ParsedSchema;
49pub use parsed_schema_cache::{ParsedSchemaCache, ParsedSchemaCacheStats, PARSED_SCHEMA_CACHE};
50use serde::de::Error as _;
51use serde_json::{Value};
52
53#[cfg(feature = "parallel")]
54use rayon::prelude::*;
55
56use std::mem;
57use std::sync::{Arc, Mutex};
58use std::time::Instant;
59use std::cell::RefCell;
60
61thread_local! {
63 static TIMING_ENABLED: RefCell<bool> = RefCell::new(std::env::var("JSONEVAL_TIMING").is_ok());
64 static TIMING_DATA: RefCell<Vec<(String, std::time::Duration)>> = RefCell::new(Vec::new());
65}
66
67#[inline]
69fn is_timing_enabled() -> bool {
70 TIMING_ENABLED.with(|enabled| *enabled.borrow())
71}
72
73pub fn enable_timing() {
75 TIMING_ENABLED.with(|enabled| {
76 *enabled.borrow_mut() = true;
77 });
78}
79
80pub fn disable_timing() {
82 TIMING_ENABLED.with(|enabled| {
83 *enabled.borrow_mut() = false;
84 });
85}
86
87#[inline]
89fn record_timing(label: &str, duration: std::time::Duration) {
90 if is_timing_enabled() {
91 TIMING_DATA.with(|data| {
92 data.borrow_mut().push((label.to_string(), duration));
93 });
94 }
95}
96
97pub fn print_timing_summary() {
99 if !is_timing_enabled() {
100 return;
101 }
102
103 TIMING_DATA.with(|data| {
104 let timings = data.borrow();
105 if timings.is_empty() {
106 return;
107 }
108
109 eprintln!("\nš Timing Summary (JSONEVAL_TIMING enabled)");
110 eprintln!("{}", "=".repeat(60));
111
112 let mut total = std::time::Duration::ZERO;
113 for (label, duration) in timings.iter() {
114 eprintln!("{:40} {:>12?}", label, duration);
115 total += *duration;
116 }
117
118 eprintln!("{}", "=".repeat(60));
119 eprintln!("{:40} {:>12?}", "TOTAL", total);
120 eprintln!();
121 });
122}
123
124pub fn clear_timing_data() {
126 TIMING_DATA.with(|data| {
127 data.borrow_mut().clear();
128 });
129}
130
131macro_rules! time_block {
133 ($label:expr, $block:block) => {{
134 let _start = if is_timing_enabled() {
135 Some(Instant::now())
136 } else {
137 None
138 };
139 let result = $block;
140 if let Some(start) = _start {
141 record_timing($label, start.elapsed());
142 }
143 result
144 }};
145}
146
147pub fn version() -> &'static str {
149 env!("CARGO_PKG_VERSION")
150}
151
152fn clean_float_noise(value: Value) -> Value {
155 const EPSILON: f64 = 1e-10;
156
157 match value {
158 Value::Number(n) => {
159 if let Some(f) = n.as_f64() {
160 if f.abs() < EPSILON {
161 Value::Number(serde_json::Number::from(0))
163 } else if f.fract().abs() < EPSILON {
164 Value::Number(serde_json::Number::from(f.round() as i64))
166 } else {
167 Value::Number(n)
168 }
169 } else {
170 Value::Number(n)
171 }
172 }
173 Value::Array(arr) => {
174 Value::Array(arr.into_iter().map(clean_float_noise).collect())
175 }
176 Value::Object(obj) => {
177 Value::Object(obj.into_iter().map(|(k, v)| (k, clean_float_noise(v))).collect())
178 }
179 _ => value,
180 }
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct DependentItem {
186 pub ref_path: String,
187 pub clear: Option<Value>, pub value: Option<Value>, }
190
191pub struct JSONEval {
192 pub schema: Arc<Value>,
193 pub engine: Arc<RLogic>,
194 pub evaluations: Arc<IndexMap<String, LogicId>>,
196 pub tables: Arc<IndexMap<String, Value>>,
197 pub table_metadata: Arc<IndexMap<String, TableMetadata>>,
199 pub dependencies: Arc<IndexMap<String, IndexSet<String>>>,
200 pub sorted_evaluations: Arc<Vec<Vec<String>>>,
203 pub dependents_evaluations: Arc<IndexMap<String, Vec<DependentItem>>>,
206 pub rules_evaluations: Arc<Vec<String>>,
208 pub fields_with_rules: Arc<Vec<String>>,
210 pub others_evaluations: Arc<Vec<String>>,
212 pub value_evaluations: Arc<Vec<String>>,
214 pub layout_paths: Arc<Vec<String>>,
216 pub options_templates: Arc<Vec<(String, String, String)>>,
218 pub subforms: IndexMap<String, Box<JSONEval>>,
221 pub context: Value,
222 pub data: Value,
223 pub evaluated_schema: Value,
224 pub eval_data: EvalData,
225 pub eval_cache: EvalCache,
227 pub cache_enabled: bool,
230 eval_lock: Mutex<()>,
232 cached_msgpack_schema: Option<Vec<u8>>,
235}
236
237impl Clone for JSONEval {
238 fn clone(&self) -> Self {
239 Self {
240 cache_enabled: self.cache_enabled,
241 schema: Arc::clone(&self.schema),
242 engine: Arc::clone(&self.engine),
243 evaluations: self.evaluations.clone(),
244 tables: self.tables.clone(),
245 table_metadata: self.table_metadata.clone(),
246 dependencies: self.dependencies.clone(),
247 sorted_evaluations: self.sorted_evaluations.clone(),
248 dependents_evaluations: self.dependents_evaluations.clone(),
249 rules_evaluations: self.rules_evaluations.clone(),
250 fields_with_rules: self.fields_with_rules.clone(),
251 others_evaluations: self.others_evaluations.clone(),
252 value_evaluations: self.value_evaluations.clone(),
253 layout_paths: self.layout_paths.clone(),
254 options_templates: self.options_templates.clone(),
255 subforms: self.subforms.clone(),
256 context: self.context.clone(),
257 data: self.data.clone(),
258 evaluated_schema: self.evaluated_schema.clone(),
259 eval_data: self.eval_data.clone(),
260 eval_cache: EvalCache::new(), eval_lock: Mutex::new(()), cached_msgpack_schema: self.cached_msgpack_schema.clone(),
263 }
264 }
265}
266
267impl JSONEval {
268 pub fn new(
269 schema: &str,
270 context: Option<&str>,
271 data: Option<&str>,
272 ) -> Result<Self, serde_json::Error> {
273 time_block!("JSONEval::new() [total]", {
274 let schema_val: Value = time_block!(" parse schema JSON", {
276 serde_json::from_str(schema)?
277 });
278 let context: Value = time_block!(" parse context JSON", {
279 json_parser::parse_json_str(context.unwrap_or("{}")).map_err(serde_json::Error::custom)?
280 });
281 let data: Value = time_block!(" parse data JSON", {
282 json_parser::parse_json_str(data.unwrap_or("{}")).map_err(serde_json::Error::custom)?
283 });
284 let evaluated_schema = schema_val.clone();
285 let engine_config = RLogicConfig::default();
287
288 let mut instance = time_block!(" create instance struct", {
289 Self {
290 schema: Arc::new(schema_val),
291 evaluations: Arc::new(IndexMap::new()),
292 tables: Arc::new(IndexMap::new()),
293 table_metadata: Arc::new(IndexMap::new()),
294 dependencies: Arc::new(IndexMap::new()),
295 sorted_evaluations: Arc::new(Vec::new()),
296 dependents_evaluations: Arc::new(IndexMap::new()),
297 rules_evaluations: Arc::new(Vec::new()),
298 fields_with_rules: Arc::new(Vec::new()),
299 others_evaluations: Arc::new(Vec::new()),
300 value_evaluations: Arc::new(Vec::new()),
301 layout_paths: Arc::new(Vec::new()),
302 options_templates: Arc::new(Vec::new()),
303 subforms: IndexMap::new(),
304 engine: Arc::new(RLogic::with_config(engine_config)),
305 context: context.clone(),
306 data: data.clone(),
307 evaluated_schema: evaluated_schema.clone(),
308 eval_data: EvalData::with_schema_data_context(&evaluated_schema, &data, &context),
309 eval_cache: EvalCache::new(),
310 cache_enabled: true, eval_lock: Mutex::new(()),
312 cached_msgpack_schema: None, }
314 });
315 time_block!(" parse_schema", {
316 parse_schema::legacy::parse_schema(&mut instance).map_err(serde_json::Error::custom)?
317 });
318 Ok(instance)
319 })
320 }
321
322 pub fn new_from_msgpack(
334 schema_msgpack: &[u8],
335 context: Option<&str>,
336 data: Option<&str>,
337 ) -> Result<Self, String> {
338 let cached_msgpack = schema_msgpack.to_vec();
340
341 let schema_val: Value = rmp_serde::from_slice(schema_msgpack)
343 .map_err(|e| format!("Failed to deserialize MessagePack schema: {}", e))?;
344
345 let context: Value = json_parser::parse_json_str(context.unwrap_or("{}"))
346 .map_err(|e| format!("Failed to parse context: {}", e))?;
347 let data: Value = json_parser::parse_json_str(data.unwrap_or("{}"))
348 .map_err(|e| format!("Failed to parse data: {}", e))?;
349 let evaluated_schema = schema_val.clone();
350 let engine_config = RLogicConfig::default();
351
352 let mut instance = Self {
353 schema: Arc::new(schema_val),
354 evaluations: Arc::new(IndexMap::new()),
355 tables: Arc::new(IndexMap::new()),
356 table_metadata: Arc::new(IndexMap::new()),
357 dependencies: Arc::new(IndexMap::new()),
358 sorted_evaluations: Arc::new(Vec::new()),
359 dependents_evaluations: Arc::new(IndexMap::new()),
360 rules_evaluations: Arc::new(Vec::new()),
361 fields_with_rules: Arc::new(Vec::new()),
362 others_evaluations: Arc::new(Vec::new()),
363 value_evaluations: Arc::new(Vec::new()),
364 layout_paths: Arc::new(Vec::new()),
365 options_templates: Arc::new(Vec::new()),
366 subforms: IndexMap::new(),
367 engine: Arc::new(RLogic::with_config(engine_config)),
368 context: context.clone(),
369 data: data.clone(),
370 evaluated_schema: evaluated_schema.clone(),
371 eval_data: EvalData::with_schema_data_context(&evaluated_schema, &data, &context),
372 eval_cache: EvalCache::new(),
373 cache_enabled: true, eval_lock: Mutex::new(()),
375 cached_msgpack_schema: Some(cached_msgpack), };
377 parse_schema::legacy::parse_schema(&mut instance)?;
378 Ok(instance)
379 }
380
381 pub fn with_parsed_schema(
409 parsed: Arc<ParsedSchema>,
410 context: Option<&str>,
411 data: Option<&str>,
412 ) -> Result<Self, String> {
413 let context: Value = json_parser::parse_json_str(context.unwrap_or("{}"))
414 .map_err(|e| format!("Failed to parse context: {}", e))?;
415 let data: Value = json_parser::parse_json_str(data.unwrap_or("{}"))
416 .map_err(|e| format!("Failed to parse data: {}", e))?;
417
418 let evaluated_schema = parsed.schema.clone();
419
420 let engine = parsed.engine.clone();
423
424 let mut subforms = IndexMap::new();
427 for (path, subform_parsed) in &parsed.subforms {
428 let subform_eval = JSONEval::with_parsed_schema(
430 subform_parsed.clone(),
431 Some("{}"),
432 None
433 )?;
434 subforms.insert(path.clone(), Box::new(subform_eval));
435 }
436
437 let instance = Self {
438 schema: Arc::clone(&parsed.schema),
439 evaluations: Arc::clone(&parsed.evaluations),
441 tables: Arc::clone(&parsed.tables),
442 table_metadata: Arc::clone(&parsed.table_metadata),
443 dependencies: Arc::clone(&parsed.dependencies),
444 sorted_evaluations: Arc::clone(&parsed.sorted_evaluations),
445 dependents_evaluations: Arc::clone(&parsed.dependents_evaluations),
446 rules_evaluations: Arc::clone(&parsed.rules_evaluations),
447 fields_with_rules: Arc::clone(&parsed.fields_with_rules),
448 others_evaluations: Arc::clone(&parsed.others_evaluations),
449 value_evaluations: Arc::clone(&parsed.value_evaluations),
450 layout_paths: Arc::clone(&parsed.layout_paths),
451 options_templates: Arc::clone(&parsed.options_templates),
452 subforms,
453 engine,
454 context: context.clone(),
455 data: data.clone(),
456 evaluated_schema: (*evaluated_schema).clone(),
457 eval_data: EvalData::with_schema_data_context(&evaluated_schema, &data, &context),
458 eval_cache: EvalCache::new(),
459 cache_enabled: true, eval_lock: Mutex::new(()),
461 cached_msgpack_schema: None, };
463
464 Ok(instance)
465 }
466
467 pub fn reload_schema(
468 &mut self,
469 schema: &str,
470 context: Option<&str>,
471 data: Option<&str>,
472 ) -> Result<(), String> {
473 let schema_val: Value = serde_json::from_str(schema).map_err(|e| format!("failed to parse schema: {e}"))?;
475 let context: Value = json_parser::parse_json_str(context.unwrap_or("{}"))?;
476 let data: Value = json_parser::parse_json_str(data.unwrap_or("{}"))?;
477 self.schema = Arc::new(schema_val);
478 self.context = context.clone();
479 self.data = data.clone();
480 self.evaluated_schema = (*self.schema).clone();
481 self.engine = Arc::new(RLogic::new());
482 self.dependents_evaluations = Arc::new(IndexMap::new());
483 self.rules_evaluations = Arc::new(Vec::new());
484 self.fields_with_rules = Arc::new(Vec::new());
485 self.others_evaluations = Arc::new(Vec::new());
486 self.value_evaluations = Arc::new(Vec::new());
487 self.layout_paths = Arc::new(Vec::new());
488 self.options_templates = Arc::new(Vec::new());
489 self.subforms.clear();
490 parse_schema::legacy::parse_schema(self)?;
491
492 self.eval_data = EvalData::with_schema_data_context(&self.evaluated_schema, &data, &context);
494
495 self.eval_cache.clear();
497
498 self.cached_msgpack_schema = None;
500
501 Ok(())
502 }
503
504 pub fn reload_schema_msgpack(
516 &mut self,
517 schema_msgpack: &[u8],
518 context: Option<&str>,
519 data: Option<&str>,
520 ) -> Result<(), String> {
521 let schema_val: Value = rmp_serde::from_slice(schema_msgpack)
523 .map_err(|e| format!("failed to deserialize MessagePack schema: {e}"))?;
524
525 let context: Value = json_parser::parse_json_str(context.unwrap_or("{}"))?;
526 let data: Value = json_parser::parse_json_str(data.unwrap_or("{}"))?;
527
528 self.schema = Arc::new(schema_val);
529 self.context = context.clone();
530 self.data = data.clone();
531 self.evaluated_schema = (*self.schema).clone();
532 self.engine = Arc::new(RLogic::new());
533 self.dependents_evaluations = Arc::new(IndexMap::new());
534 self.rules_evaluations = Arc::new(Vec::new());
535 self.fields_with_rules = Arc::new(Vec::new());
536 self.others_evaluations = Arc::new(Vec::new());
537 self.value_evaluations = Arc::new(Vec::new());
538 self.layout_paths = Arc::new(Vec::new());
539 self.options_templates = Arc::new(Vec::new());
540 self.subforms.clear();
541 parse_schema::legacy::parse_schema(self)?;
542
543 self.eval_data = EvalData::with_schema_data_context(&self.evaluated_schema, &data, &context);
545
546 self.eval_cache.clear();
548
549 self.cached_msgpack_schema = Some(schema_msgpack.to_vec());
551
552 Ok(())
553 }
554
555 pub fn reload_schema_parsed(
569 &mut self,
570 parsed: Arc<ParsedSchema>,
571 context: Option<&str>,
572 data: Option<&str>,
573 ) -> Result<(), String> {
574 let context: Value = json_parser::parse_json_str(context.unwrap_or("{}"))?;
575 let data: Value = json_parser::parse_json_str(data.unwrap_or("{}"))?;
576
577 self.schema = Arc::clone(&parsed.schema);
579 self.evaluations = parsed.evaluations.clone();
580 self.tables = parsed.tables.clone();
581 self.table_metadata = parsed.table_metadata.clone();
582 self.dependencies = parsed.dependencies.clone();
583 self.sorted_evaluations = parsed.sorted_evaluations.clone();
584 self.dependents_evaluations = parsed.dependents_evaluations.clone();
585 self.rules_evaluations = parsed.rules_evaluations.clone();
586 self.fields_with_rules = parsed.fields_with_rules.clone();
587 self.others_evaluations = parsed.others_evaluations.clone();
588 self.value_evaluations = parsed.value_evaluations.clone();
589 self.layout_paths = parsed.layout_paths.clone();
590 self.options_templates = parsed.options_templates.clone();
591
592 self.engine = parsed.engine.clone();
594
595 let mut subforms = IndexMap::new();
597 for (path, subform_parsed) in &parsed.subforms {
598 let subform_eval = JSONEval::with_parsed_schema(
599 subform_parsed.clone(),
600 Some("{}"),
601 None
602 )?;
603 subforms.insert(path.clone(), Box::new(subform_eval));
604 }
605 self.subforms = subforms;
606
607 self.context = context.clone();
608 self.data = data.clone();
609 self.evaluated_schema = (*self.schema).clone();
610
611 self.eval_data = EvalData::with_schema_data_context(&self.evaluated_schema, &data, &context);
613
614 self.eval_cache.clear();
616
617 self.cached_msgpack_schema = None;
619
620 Ok(())
621 }
622
623 pub fn reload_schema_from_cache(
637 &mut self,
638 cache_key: &str,
639 context: Option<&str>,
640 data: Option<&str>,
641 ) -> Result<(), String> {
642 let parsed = PARSED_SCHEMA_CACHE.get(cache_key)
644 .ok_or_else(|| format!("Schema '{}' not found in cache", cache_key))?;
645
646 self.reload_schema_parsed(parsed, context, data)
648 }
649
650 pub fn evaluate(&mut self, data: &str, context: Option<&str>) -> Result<(), String> {
661 time_block!("evaluate() [total]", {
662 let data: Value = time_block!(" parse data", {
664 json_parser::parse_json_str(data)?
665 });
666 let context: Value = time_block!(" parse context", {
667 json_parser::parse_json_str(context.unwrap_or("{}"))?
668 });
669
670 self.data = data.clone();
671
672 let changed_data_paths: Vec<String> = if let Some(obj) = data.as_object() {
674 obj.keys().map(|k| k.clone()).collect()
675 } else {
676 Vec::new()
677 };
678
679 time_block!(" replace_data_and_context", {
681 self.eval_data.replace_data_and_context(data, context);
682 });
683
684 time_block!(" purge_cache", {
687 self.purge_cache_for_changed_data(&changed_data_paths);
688 });
689
690 self.evaluate_internal()
692 })
693 }
694
695 fn evaluate_internal(&mut self) -> Result<(), String> {
698 time_block!(" evaluate_internal() [total]", {
699 let _lock = self.eval_lock.lock().unwrap();
701
702 let eval_batches: Vec<Vec<String>> = (*self.sorted_evaluations).clone();
704
705 time_block!(" process batches", {
708 for batch in eval_batches {
709 if batch.is_empty() {
711 continue;
712 }
713
714 let eval_data_snapshot = self.eval_data.clone();
721
722 #[cfg(feature = "parallel")]
724 if batch.len() > 1000 {
725 let results: Mutex<Vec<(String, String, Value)>> = Mutex::new(Vec::with_capacity(batch.len()));
726 batch.par_iter().for_each(|eval_key| {
727 let pointer_path = path_utils::normalize_to_json_pointer(eval_key);
728
729 if let Some(_) = self.try_get_cached(eval_key, &eval_data_snapshot) {
731 return;
732 }
733
734 let is_table = self.table_metadata.contains_key(eval_key);
736
737 if is_table {
738 if let Ok(rows) = table_evaluate::evaluate_table(self, eval_key, &eval_data_snapshot) {
740 let value = Value::Array(rows);
741 self.cache_result(eval_key, Value::Null, &eval_data_snapshot);
743 results.lock().unwrap().push((eval_key.clone(), pointer_path, value));
744 }
745 } else {
746 if let Some(logic_id) = self.evaluations.get(eval_key) {
747 if let Ok(val) = self.engine.run(logic_id, eval_data_snapshot.data()) {
749 let cleaned_val = clean_float_noise(val);
750 self.cache_result(eval_key, Value::Null, &eval_data_snapshot);
752 results.lock().unwrap().push((eval_key.clone(), pointer_path, cleaned_val));
753 }
754 }
755 }
756 });
757
758 for (_eval_key, path, value) in results.into_inner().unwrap() {
760 let cleaned_value = clean_float_noise(value);
761
762 self.eval_data.set(&path, cleaned_value.clone());
763 if let Some(schema_value) = self.evaluated_schema.pointer_mut(&path) {
765 *schema_value = cleaned_value;
766 }
767 }
768 continue;
769 }
770
771 #[cfg(not(feature = "parallel"))]
773 let batch_items = &batch;
774
775 #[cfg(feature = "parallel")]
776 let batch_items = if batch.len() > 1000 { &batch[0..0] } else { &batch }; for eval_key in batch_items {
779 let pointer_path = path_utils::normalize_to_json_pointer(eval_key);
780
781 if let Some(_) = self.try_get_cached(eval_key, &eval_data_snapshot) {
783 continue;
784 }
785
786 let is_table = self.table_metadata.contains_key(eval_key);
788
789 if is_table {
790 if let Ok(rows) = table_evaluate::evaluate_table(self, eval_key, &eval_data_snapshot) {
791 let value = Value::Array(rows);
792 self.cache_result(eval_key, Value::Null, &eval_data_snapshot);
794
795 let cleaned_value = clean_float_noise(value);
796 self.eval_data.set(&pointer_path, cleaned_value.clone());
797 if let Some(schema_value) = self.evaluated_schema.pointer_mut(&pointer_path) {
798 *schema_value = cleaned_value;
799 }
800 }
801 } else {
802 if let Some(logic_id) = self.evaluations.get(eval_key) {
803 if let Ok(val) = self.engine.run(logic_id, eval_data_snapshot.data()) {
804 let cleaned_val = clean_float_noise(val);
805 self.cache_result(eval_key, Value::Null, &eval_data_snapshot);
807
808 self.eval_data.set(&pointer_path, cleaned_val.clone());
809 if let Some(schema_value) = self.evaluated_schema.pointer_mut(&pointer_path) {
810 *schema_value = cleaned_val;
811 }
812 }
813 }
814 }
815 }
816 }
817 });
818
819 drop(_lock);
821
822 self.evaluate_others();
823
824 Ok(())
825 })
826 }
827
828 pub fn get_evaluated_schema(&mut self, skip_layout: bool) -> Value {
838 time_block!("get_evaluated_schema()", {
839 if !skip_layout {
840 self.resolve_layout_internal();
841 }
842
843 self.evaluated_schema.clone()
844 })
845 }
846
847 pub fn get_evaluated_schema_msgpack(&mut self, skip_layout: bool) -> Result<Vec<u8>, String> {
864 if !skip_layout {
865 self.resolve_layout_internal();
866 }
867
868 rmp_serde::to_vec(&self.evaluated_schema)
872 .map_err(|e| format!("Failed to serialize schema to MessagePack: {}", e))
873 }
874
875 pub fn get_schema_value(&mut self) -> Value {
879 if !self.data.is_object() {
881 self.data = Value::Object(serde_json::Map::new());
882 }
883
884 for eval_key in self.value_evaluations.iter() {
886 let clean_key = eval_key.replace("#", "");
887 let path = clean_key.replace("/properties", "").replace("/value", "");
888
889 let value = match self.evaluated_schema.pointer(&clean_key) {
891 Some(v) => v.clone(),
892 None => continue,
893 };
894
895 let path_parts: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
897
898 if path_parts.is_empty() {
899 continue;
900 }
901
902 let mut current = &mut self.data;
904 for (i, part) in path_parts.iter().enumerate() {
905 let is_last = i == path_parts.len() - 1;
906
907 if is_last {
908 if let Some(obj) = current.as_object_mut() {
910 obj.insert(part.to_string(), clean_float_noise(value.clone()));
911 }
912 } else {
913 if let Some(obj) = current.as_object_mut() {
915 current = obj.entry(part.to_string())
916 .or_insert_with(|| Value::Object(serde_json::Map::new()));
917 } else {
918 break;
920 }
921 }
922 }
923 }
924
925 clean_float_noise(self.data.clone())
926 }
927
928 pub fn get_evaluated_schema_without_params(&mut self, skip_layout: bool) -> Value {
939 if !skip_layout {
940 self.resolve_layout_internal();
941 }
942
943 if let Value::Object(mut map) = self.evaluated_schema.clone() {
945 map.remove("$params");
946 Value::Object(map)
947 } else {
948 self.evaluated_schema.clone()
949 }
950 }
951
952 pub fn get_evaluated_schema_by_path(&mut self, path: &str, skip_layout: bool) -> Option<Value> {
964 if !skip_layout {
965 self.resolve_layout_internal();
966 }
967
968 let pointer = if path.is_empty() {
970 "".to_string()
971 } else {
972 format!("/{}", path.replace(".", "/"))
973 };
974
975 self.evaluated_schema.pointer(&pointer).cloned()
976 }
977
978 pub fn get_evaluated_schema_by_paths(&mut self, paths: &[String], skip_layout: bool) -> Value {
990 if !skip_layout {
991 self.resolve_layout_internal();
992 }
993
994 let mut result = serde_json::Map::new();
995
996 for path in paths {
997 let pointer = if path.is_empty() {
999 "".to_string()
1000 } else {
1001 format!("/{}", path.replace(".", "/"))
1002 };
1003
1004 if let Some(value) = self.evaluated_schema.pointer(&pointer) {
1006 self.insert_at_path(&mut result, path, value.clone());
1009 }
1010 }
1011
1012 Value::Object(result)
1013 }
1014
1015 fn insert_at_path(&self, obj: &mut serde_json::Map<String, Value>, path: &str, value: Value) {
1017 if path.is_empty() {
1018 if let Value::Object(map) = value {
1020 for (k, v) in map {
1021 obj.insert(k, v);
1022 }
1023 }
1024 return;
1025 }
1026
1027 let parts: Vec<&str> = path.split('.').collect();
1028 if parts.is_empty() {
1029 return;
1030 }
1031
1032 let mut current = obj;
1033 let last_index = parts.len() - 1;
1034
1035 for (i, part) in parts.iter().enumerate() {
1036 if i == last_index {
1037 current.insert(part.to_string(), value);
1039 break;
1040 } else {
1041 current = current
1043 .entry(part.to_string())
1044 .or_insert_with(|| Value::Object(serde_json::Map::new()))
1045 .as_object_mut()
1046 .unwrap();
1047 }
1048 }
1049 }
1050
1051 pub fn get_schema_by_path(&self, path: &str) -> Option<Value> {
1062 let pointer = if path.is_empty() {
1064 "".to_string()
1065 } else {
1066 format!("/{}", path.replace(".", "/"))
1067 };
1068
1069 self.schema.pointer(&pointer).cloned()
1070 }
1071
1072 pub fn get_schema_by_paths(&self, paths: &[String]) -> Value {
1083 let mut result = serde_json::Map::new();
1084
1085 for path in paths {
1086 let pointer = if path.is_empty() {
1088 "".to_string()
1089 } else {
1090 format!("/{}", path.replace(".", "/"))
1091 };
1092
1093 if let Some(value) = self.schema.pointer(&pointer) {
1095 self.insert_at_path(&mut result, path, value.clone());
1098 }
1099 }
1100
1101 Value::Object(result)
1102 }
1103
1104 #[inline]
1107 fn should_cache_dependency(key: &str) -> bool {
1108 if key.starts_with("/$") || key.starts_with('$') {
1109 key == "$context" || key.starts_with("$context.") || key.starts_with("/$context")
1111 } else {
1112 true
1113 }
1114 }
1115
1116 fn try_get_cached(&self, eval_key: &str, eval_data: &EvalData) -> Option<Value> {
1119 if !self.cache_enabled {
1121 return None;
1122 }
1123
1124 let deps = self.dependencies.get(eval_key)?;
1126
1127 let cache_key = if deps.is_empty() {
1129 CacheKey::simple(eval_key.to_string())
1130 } else {
1131 let filtered_deps: IndexSet<String> = deps
1133 .iter()
1134 .filter(|dep_key| JSONEval::should_cache_dependency(dep_key))
1135 .cloned()
1136 .collect();
1137
1138 let dep_values: Vec<(String, &Value)> = filtered_deps
1140 .iter()
1141 .filter_map(|dep_key| {
1142 eval_data.get(dep_key).map(|v| (dep_key.clone(), v))
1143 })
1144 .collect();
1145
1146 CacheKey::new(eval_key.to_string(), &filtered_deps, &dep_values)
1147 };
1148
1149 self.eval_cache.get(&cache_key).map(|arc_val| (*arc_val).clone())
1151 }
1152
1153 fn cache_result(&self, eval_key: &str, value: Value, eval_data: &EvalData) {
1155 if !self.cache_enabled {
1157 return;
1158 }
1159
1160 let deps = match self.dependencies.get(eval_key) {
1162 Some(d) => d,
1163 None => {
1164 let cache_key = CacheKey::simple(eval_key.to_string());
1166 self.eval_cache.insert(cache_key, value);
1167 return;
1168 }
1169 };
1170
1171 let filtered_deps: IndexSet<String> = deps
1173 .iter()
1174 .filter(|dep_key| JSONEval::should_cache_dependency(dep_key))
1175 .cloned()
1176 .collect();
1177
1178 let dep_values: Vec<(String, &Value)> = filtered_deps
1179 .iter()
1180 .filter_map(|dep_key| {
1181 eval_data.get(dep_key).map(|v| (dep_key.clone(), v))
1182 })
1183 .collect();
1184
1185 let cache_key = CacheKey::new(eval_key.to_string(), &filtered_deps, &dep_values);
1186 self.eval_cache.insert(cache_key, value);
1187 }
1188
1189 fn purge_cache_for_changed_data_with_comparison(
1193 &self,
1194 changed_data_paths: &[String],
1195 old_data: &Value,
1196 new_data: &Value
1197 ) {
1198 if changed_data_paths.is_empty() {
1199 return;
1200 }
1201
1202 let mut actually_changed_paths = Vec::new();
1204 for path in changed_data_paths {
1205 let old_val = old_data.pointer(path);
1206 let new_val = new_data.pointer(path);
1207
1208 if old_val != new_val {
1210 actually_changed_paths.push(path.clone());
1211 }
1212 }
1213
1214 if actually_changed_paths.is_empty() {
1216 return;
1217 }
1218
1219 let mut affected_eval_keys = IndexSet::new();
1221
1222 for (eval_key, deps) in self.dependencies.iter() {
1223 let is_affected = deps.iter().any(|dep| {
1225 actually_changed_paths.iter().any(|changed_path| {
1227 dep == changed_path ||
1229 dep.starts_with(&format!("{}/", changed_path)) ||
1230 changed_path.starts_with(&format!("{}/", dep))
1231 })
1232 });
1233
1234 if is_affected {
1235 affected_eval_keys.insert(eval_key.clone());
1236 }
1237 }
1238
1239 self.eval_cache.retain(|cache_key, _| {
1242 !affected_eval_keys.contains(&cache_key.eval_key)
1243 });
1244 }
1245
1246 fn purge_cache_for_changed_data(&self, changed_data_paths: &[String]) {
1249 if changed_data_paths.is_empty() {
1250 return;
1251 }
1252
1253 let mut affected_eval_keys = IndexSet::new();
1255
1256 for (eval_key, deps) in self.dependencies.iter() {
1257 let is_affected = deps.iter().any(|dep| {
1259 changed_data_paths.iter().any(|changed_path| {
1261 dep == changed_path ||
1263 dep.starts_with(&format!("{}/", changed_path)) ||
1264 changed_path.starts_with(&format!("{}/", dep))
1265 })
1266 });
1267
1268 if is_affected {
1269 affected_eval_keys.insert(eval_key.clone());
1270 }
1271 }
1272
1273 self.eval_cache.retain(|cache_key, _| {
1276 !affected_eval_keys.contains(&cache_key.eval_key)
1277 });
1278 }
1279
1280 pub fn cache_stats(&self) -> CacheStats {
1282 self.eval_cache.stats()
1283 }
1284
1285 pub fn clear_cache(&mut self) {
1287 self.eval_cache.clear();
1288 }
1289
1290 pub fn cache_len(&self) -> usize {
1292 self.eval_cache.len()
1293 }
1294
1295 pub fn enable_cache(&mut self) {
1298 self.cache_enabled = true;
1299 }
1300
1301 pub fn disable_cache(&mut self) {
1305 self.cache_enabled = false;
1306 self.eval_cache.clear(); }
1308
1309 pub fn is_cache_enabled(&self) -> bool {
1311 self.cache_enabled
1312 }
1313
1314 fn evaluate_others(&mut self) {
1315 time_block!(" evaluate_others()", {
1316 time_block!(" evaluate_options_templates", {
1318 self.evaluate_options_templates();
1319 });
1320
1321 let combined_count = self.rules_evaluations.len() + self.others_evaluations.len();
1324 if combined_count == 0 {
1325 return;
1326 }
1327
1328 time_block!(" evaluate rules+others", {
1329 let eval_data_snapshot = self.eval_data.clone();
1330
1331 #[cfg(feature = "parallel")]
1332 {
1333 let combined_results: Mutex<Vec<(String, Value)>> = Mutex::new(Vec::with_capacity(combined_count));
1334
1335 self.rules_evaluations
1336 .par_iter()
1337 .chain(self.others_evaluations.par_iter())
1338 .for_each(|eval_key| {
1339 let pointer_path = path_utils::normalize_to_json_pointer(eval_key);
1340
1341 if let Some(_) = self.try_get_cached(eval_key, &eval_data_snapshot) {
1343 return;
1344 }
1345
1346 if let Some(logic_id) = self.evaluations.get(eval_key) {
1348 if let Ok(val) = self.engine.run(logic_id, eval_data_snapshot.data()) {
1349 let cleaned_val = clean_float_noise(val);
1350 self.cache_result(eval_key, Value::Null, &eval_data_snapshot);
1352 combined_results.lock().unwrap().push((pointer_path, cleaned_val));
1353 }
1354 }
1355 });
1356
1357 for (result_path, value) in combined_results.into_inner().unwrap() {
1359 if let Some(pointer_value) = self.evaluated_schema.pointer_mut(&result_path) {
1360 if !result_path.starts_with("$") && result_path.contains("/rules/") && !result_path.ends_with("/value") {
1363 match pointer_value.as_object_mut() {
1364 Some(pointer_obj) => {
1365 pointer_obj.remove("$evaluation");
1366 pointer_obj.insert("value".to_string(), value);
1367 },
1368 None => continue,
1369 }
1370 } else {
1371 *pointer_value = value;
1372 }
1373 }
1374 }
1375 }
1376
1377 #[cfg(not(feature = "parallel"))]
1378 {
1379 for eval_key in self.rules_evaluations.iter().chain(self.others_evaluations.iter()) {
1381 let pointer_path = path_utils::normalize_to_json_pointer(eval_key);
1382
1383 if let Some(_) = self.try_get_cached(eval_key, &eval_data_snapshot) {
1385 continue;
1386 }
1387
1388 if let Some(logic_id) = self.evaluations.get(eval_key) {
1390 if let Ok(val) = self.engine.run(logic_id, eval_data_snapshot.data()) {
1391 let cleaned_val = clean_float_noise(val);
1392 self.cache_result(eval_key, Value::Null, &eval_data_snapshot);
1394
1395 if let Some(pointer_value) = self.evaluated_schema.pointer_mut(&pointer_path) {
1396 if !pointer_path.starts_with("$") && pointer_path.contains("/rules/") && !pointer_path.ends_with("/value") {
1397 match pointer_value.as_object_mut() {
1398 Some(pointer_obj) => {
1399 pointer_obj.remove("$evaluation");
1400 pointer_obj.insert("value".to_string(), cleaned_val);
1401 },
1402 None => continue,
1403 }
1404 } else {
1405 *pointer_value = cleaned_val;
1406 }
1407 }
1408 }
1409 }
1410 }
1411 }
1412 });
1413 });
1414 }
1415
1416 fn evaluate_options_templates(&mut self) {
1418 let templates_to_eval = self.options_templates.clone();
1420
1421 for (path, template_str, params_path) in templates_to_eval.iter() {
1423 if let Some(params) = self.evaluated_schema.pointer(¶ms_path) {
1424 if let Ok(evaluated) = self.evaluate_template(&template_str, params) {
1425 if let Some(target) = self.evaluated_schema.pointer_mut(&path) {
1426 *target = Value::String(evaluated);
1427 }
1428 }
1429 }
1430 }
1431 }
1432
1433 fn evaluate_template(&self, template: &str, params: &Value) -> Result<String, String> {
1435 let mut result = template.to_string();
1436
1437 if let Value::Object(params_map) = params {
1439 for (key, value) in params_map {
1440 let placeholder = format!("{{{}}}", key);
1441 if let Some(str_val) = value.as_str() {
1442 result = result.replace(&placeholder, str_val);
1443 } else {
1444 result = result.replace(&placeholder, &value.to_string());
1446 }
1447 }
1448 }
1449
1450 Ok(result)
1451 }
1452
1453 pub fn compile_logic(&self, logic_str: &str) -> Result<CompiledLogicId, String> {
1469 rlogic::compiled_logic_store::compile_logic(logic_str)
1470 }
1471
1472 pub fn compile_logic_value(&self, logic: &Value) -> Result<CompiledLogicId, String> {
1489 rlogic::compiled_logic_store::compile_logic_value(logic)
1490 }
1491
1492 pub fn run_logic(&mut self, logic_id: CompiledLogicId, data: Option<&Value>, context: Option<&Value>) -> Result<Value, String> {
1509 let compiled_logic = rlogic::compiled_logic_store::get_compiled_logic(logic_id)
1511 .ok_or_else(|| format!("Compiled logic ID {:?} not found in store", logic_id))?;
1512
1513 let eval_data_value = if let Some(input_data) = data {
1517 let context_value = context.unwrap_or(&self.context);
1518
1519 self.eval_data.replace_data_and_context(input_data.clone(), context_value.clone());
1520 self.eval_data.data()
1521 } else {
1522 self.eval_data.data()
1523 };
1524
1525 let evaluator = Evaluator::new();
1527 let result = evaluator.evaluate(&compiled_logic, &eval_data_value)?;
1528
1529 Ok(clean_float_noise(result))
1530 }
1531
1532 pub fn compile_and_run_logic(&mut self, logic_str: &str, data: Option<&str>, context: Option<&str>) -> Result<Value, String> {
1548 let compiled_logic = self.compile_logic(logic_str)?;
1550
1551 let data_value = if let Some(data_str) = data {
1553 Some(json_parser::parse_json_str(data_str)?)
1554 } else {
1555 None
1556 };
1557
1558 let context_value = if let Some(ctx_str) = context {
1559 Some(json_parser::parse_json_str(ctx_str)?)
1560 } else {
1561 None
1562 };
1563
1564 self.run_logic(compiled_logic, data_value.as_ref(), context_value.as_ref())
1566 }
1567
1568 pub fn resolve_layout(&mut self, evaluate: bool) -> Result<(), String> {
1578 if evaluate {
1579 let data_str = serde_json::to_string(&self.data)
1581 .map_err(|e| format!("Failed to serialize data: {}", e))?;
1582 self.evaluate(&data_str, None)?;
1583 }
1584
1585 self.resolve_layout_internal();
1586 Ok(())
1587 }
1588
1589 fn resolve_layout_internal(&mut self) {
1590 time_block!(" resolve_layout_internal()", {
1591 let layout_paths = self.layout_paths.clone();
1594
1595 time_block!(" resolve_layout_elements", {
1596 for layout_path in layout_paths.iter() {
1597 self.resolve_layout_elements(layout_path);
1598 }
1599 });
1600
1601 time_block!(" propagate_parent_conditions", {
1603 for layout_path in layout_paths.iter() {
1604 self.propagate_parent_conditions(layout_path);
1605 }
1606 });
1607 });
1608 }
1609
1610 fn propagate_parent_conditions(&mut self, layout_elements_path: &str) {
1612 let normalized_path = path_utils::normalize_to_json_pointer(layout_elements_path);
1614
1615 let elements = if let Some(Value::Array(arr)) = self.evaluated_schema.pointer_mut(&normalized_path) {
1617 mem::take(arr)
1618 } else {
1619 return;
1620 };
1621
1622 let mut updated_elements = Vec::with_capacity(elements.len());
1624 for element in elements {
1625 updated_elements.push(self.apply_parent_conditions(element, false, false));
1626 }
1627
1628 if let Some(target) = self.evaluated_schema.pointer_mut(&normalized_path) {
1630 *target = Value::Array(updated_elements);
1631 }
1632 }
1633
1634 fn apply_parent_conditions(&self, element: Value, parent_hidden: bool, parent_disabled: bool) -> Value {
1636 if let Value::Object(mut map) = element {
1637 let mut element_hidden = parent_hidden;
1639 let mut element_disabled = parent_disabled;
1640
1641 if let Some(Value::Object(condition)) = map.get("condition") {
1643 if let Some(Value::Bool(hidden)) = condition.get("hidden") {
1644 element_hidden = element_hidden || *hidden;
1645 }
1646 if let Some(Value::Bool(disabled)) = condition.get("disabled") {
1647 element_disabled = element_disabled || *disabled;
1648 }
1649 }
1650
1651 if let Some(Value::Object(hide_layout)) = map.get("hideLayout") {
1653 if let Some(Value::Bool(all_hidden)) = hide_layout.get("all") {
1655 if *all_hidden {
1656 element_hidden = true;
1657 }
1658 }
1659 }
1660
1661 if parent_hidden || parent_disabled {
1663 if map.contains_key("condition") || map.contains_key("$ref") || map.contains_key("$fullpath") {
1665 let mut condition = if let Some(Value::Object(c)) = map.get("condition") {
1666 c.clone()
1667 } else {
1668 serde_json::Map::new()
1669 };
1670
1671 if parent_hidden {
1672 condition.insert("hidden".to_string(), Value::Bool(true));
1673 }
1674 if parent_disabled {
1675 condition.insert("disabled".to_string(), Value::Bool(true));
1676 }
1677
1678 map.insert("condition".to_string(), Value::Object(condition));
1679 }
1680
1681 if parent_hidden && (map.contains_key("hideLayout") || map.contains_key("type")) {
1683 let mut hide_layout = if let Some(Value::Object(h)) = map.get("hideLayout") {
1684 h.clone()
1685 } else {
1686 serde_json::Map::new()
1687 };
1688
1689 hide_layout.insert("all".to_string(), Value::Bool(true));
1691 map.insert("hideLayout".to_string(), Value::Object(hide_layout));
1692 }
1693 }
1694
1695 if map.contains_key("$parentHide") {
1698 map.insert("$parentHide".to_string(), Value::Bool(parent_hidden));
1699 }
1700
1701 if let Some(Value::Array(elements)) = map.get("elements") {
1703 let mut updated_children = Vec::with_capacity(elements.len());
1704 for child in elements {
1705 updated_children.push(self.apply_parent_conditions(
1706 child.clone(),
1707 element_hidden,
1708 element_disabled,
1709 ));
1710 }
1711 map.insert("elements".to_string(), Value::Array(updated_children));
1712 }
1713
1714 return Value::Object(map);
1715 }
1716
1717 element
1718 }
1719
1720 fn resolve_layout_elements(&mut self, layout_elements_path: &str) {
1722 let normalized_path = path_utils::normalize_to_json_pointer(layout_elements_path);
1724
1725 let elements = if let Some(Value::Array(arr)) = self.schema.pointer(&normalized_path) {
1729 arr.clone()
1730 } else {
1731 return;
1732 };
1733
1734 let parent_path = normalized_path
1736 .trim_start_matches('/')
1737 .replace("/elements", "")
1738 .replace('/', ".");
1739
1740 let mut resolved_elements = Vec::with_capacity(elements.len());
1742 for (index, element) in elements.iter().enumerate() {
1743 let element_path = if parent_path.is_empty() {
1744 format!("elements.{}", index)
1745 } else {
1746 format!("{}.elements.{}", parent_path, index)
1747 };
1748 let resolved = self.resolve_element_ref_recursive(element.clone(), &element_path);
1749 resolved_elements.push(resolved);
1750 }
1751
1752 if let Some(target) = self.evaluated_schema.pointer_mut(&normalized_path) {
1754 *target = Value::Array(resolved_elements);
1755 }
1756 }
1757
1758 fn resolve_element_ref_recursive(&self, element: Value, path_context: &str) -> Value {
1761 let resolved = self.resolve_element_ref(element);
1763
1764 if let Value::Object(mut map) = resolved {
1766 if !map.contains_key("$parentHide") {
1770 map.insert("$parentHide".to_string(), Value::Bool(false));
1771 }
1772
1773 if !map.contains_key("$fullpath") {
1775 map.insert("$fullpath".to_string(), Value::String(path_context.to_string()));
1776 }
1777
1778 if !map.contains_key("$path") {
1779 let last_segment = path_context.split('.').last().unwrap_or(path_context);
1781 map.insert("$path".to_string(), Value::String(last_segment.to_string()));
1782 }
1783
1784 if let Some(Value::Array(elements)) = map.get("elements") {
1786 let mut resolved_nested = Vec::with_capacity(elements.len());
1787 for (index, nested_element) in elements.iter().enumerate() {
1788 let nested_path = format!("{}.elements.{}", path_context, index);
1789 resolved_nested.push(self.resolve_element_ref_recursive(nested_element.clone(), &nested_path));
1790 }
1791 map.insert("elements".to_string(), Value::Array(resolved_nested));
1792 }
1793
1794 return Value::Object(map);
1795 }
1796
1797 resolved
1798 }
1799
1800 fn resolve_element_ref(&self, element: Value) -> Value {
1802 match element {
1803 Value::Object(mut map) => {
1804 if let Some(Value::String(ref_path)) = map.get("$ref").cloned() {
1806 let dotted_path = path_utils::pointer_to_dot_notation(&ref_path);
1808
1809 let last_segment = dotted_path.split('.').last().unwrap_or(&dotted_path);
1811
1812 map.insert("$fullpath".to_string(), Value::String(dotted_path.clone()));
1814 map.insert("$path".to_string(), Value::String(last_segment.to_string()));
1815 map.insert("$parentHide".to_string(), Value::Bool(false));
1816
1817 let normalized_path = if ref_path.starts_with('#') || ref_path.starts_with('/') {
1820 path_utils::normalize_to_json_pointer(&ref_path)
1822 } else {
1823 let schema_pointer = path_utils::dot_notation_to_schema_pointer(&ref_path);
1825 let schema_path = path_utils::normalize_to_json_pointer(&schema_pointer);
1826
1827 if self.evaluated_schema.pointer(&schema_path).is_some() {
1829 schema_path
1830 } else {
1831 let with_properties = format!("/properties/{}", ref_path.replace('.', "/properties/"));
1833 with_properties
1834 }
1835 };
1836
1837 if let Some(referenced_value) = self.evaluated_schema.pointer(&normalized_path) {
1839 let resolved = referenced_value.clone();
1841
1842 if let Value::Object(mut resolved_map) = resolved {
1844 map.remove("$ref");
1846
1847 if let Some(Value::Object(layout_obj)) = resolved_map.remove("$layout") {
1850 let mut result = layout_obj.clone();
1852
1853 resolved_map.remove("properties");
1855
1856 for (key, value) in resolved_map {
1858 if key != "type" || !result.contains_key("type") {
1859 result.insert(key, value);
1860 }
1861 }
1862
1863 for (key, value) in map {
1865 result.insert(key, value);
1866 }
1867
1868 return Value::Object(result);
1869 } else {
1870 for (key, value) in map {
1872 resolved_map.insert(key, value);
1873 }
1874
1875 return Value::Object(resolved_map);
1876 }
1877 } else {
1878 return resolved;
1880 }
1881 }
1882 }
1883
1884 Value::Object(map)
1886 }
1887 _ => element,
1888 }
1889 }
1890
1891 pub fn evaluate_dependents(
1900 &mut self,
1901 changed_paths: &[String],
1902 data: Option<&str>,
1903 context: Option<&str>,
1904 re_evaluate: bool,
1905 ) -> Result<Value, String> {
1906 let _lock = self.eval_lock.lock().unwrap();
1908
1909 if let Some(data_str) = data {
1911 let old_data = self.eval_data.clone_data_without(&["$params"]);
1913
1914 let data_value = json_parser::parse_json_str(data_str)?;
1915 let context_value = if let Some(ctx) = context {
1916 json_parser::parse_json_str(ctx)?
1917 } else {
1918 Value::Object(serde_json::Map::new())
1919 };
1920 self.eval_data.replace_data_and_context(data_value.clone(), context_value);
1921
1922 let data_paths: Vec<String> = changed_paths
1926 .iter()
1927 .map(|path| {
1928 format!("/{}", path.replace('.', "/"))
1930 })
1931 .collect();
1932 self.purge_cache_for_changed_data_with_comparison(&data_paths, &old_data, &data_value);
1933 }
1934
1935 let mut result = Vec::new();
1936 let mut processed = IndexSet::new();
1937
1938 let mut to_process: Vec<(String, bool)> = changed_paths
1941 .iter()
1942 .map(|path| (path_utils::dot_notation_to_schema_pointer(path), false))
1943 .collect(); while let Some((current_path, is_transitive)) = to_process.pop() {
1947 if processed.contains(¤t_path) {
1948 continue;
1949 }
1950 processed.insert(current_path.clone());
1951
1952 let current_data_path = path_utils::normalize_to_json_pointer(¤t_path)
1954 .replace("/properties/", "/")
1955 .trim_start_matches('#')
1956 .to_string();
1957 let mut current_value = self.eval_data.data().pointer(¤t_data_path)
1958 .cloned()
1959 .unwrap_or(Value::Null);
1960
1961 if let Some(dependent_items) = self.dependents_evaluations.get(¤t_path) {
1963 for dep_item in dependent_items {
1964 let ref_path = &dep_item.ref_path;
1965 let pointer_path = path_utils::normalize_to_json_pointer(ref_path);
1966 let data_path = pointer_path.replace("/properties/", "/");
1968
1969 let current_ref_value = self.eval_data.data().pointer(&data_path)
1970 .cloned()
1971 .unwrap_or(Value::Null);
1972
1973 let field = self.evaluated_schema.pointer(&pointer_path).cloned();
1975
1976 let parent_path = if let Some(last_slash) = pointer_path.rfind("/properties") {
1978 &pointer_path[..last_slash]
1979 } else {
1980 "/"
1981 };
1982 let mut parent_field = if parent_path.is_empty() || parent_path == "/" {
1983 self.evaluated_schema.clone()
1984 } else {
1985 self.evaluated_schema.pointer(parent_path).cloned()
1986 .unwrap_or_else(|| Value::Object(serde_json::Map::new()))
1987 };
1988
1989 if let Value::Object(ref mut map) = parent_field {
1991 map.remove("properties");
1992 map.remove("$layout");
1993 }
1994
1995 let mut change_obj = serde_json::Map::new();
1996 change_obj.insert("$ref".to_string(), Value::String(path_utils::pointer_to_dot_notation(&data_path)));
1997 if let Some(f) = field {
1998 change_obj.insert("$field".to_string(), f);
1999 }
2000 change_obj.insert("$parentField".to_string(), parent_field);
2001 change_obj.insert("transitive".to_string(), Value::Bool(is_transitive));
2002
2003 let mut add_transitive = false;
2004 let mut add_deps = false;
2005 if let Some(clear_val) = &dep_item.clear {
2007 let clear_val_clone = clear_val.clone();
2008 let should_clear = Self::evaluate_dependent_value_static(&self.engine, &self.evaluations, &self.eval_data, &clear_val_clone, ¤t_value, ¤t_ref_value)?;
2009 let clear_bool = match should_clear {
2010 Value::Bool(b) => b,
2011 _ => false,
2012 };
2013
2014 if clear_bool {
2015 if data_path == current_data_path {
2017 current_value = Value::Null;
2018 }
2019 self.eval_data.set(&data_path, Value::Null);
2020 change_obj.insert("clear".to_string(), Value::Bool(true));
2021 add_transitive = true;
2022 add_deps = true;
2023 }
2024 }
2025
2026 if let Some(value_val) = &dep_item.value {
2028 let value_val_clone = value_val.clone();
2029 let computed_value = Self::evaluate_dependent_value_static(&self.engine, &self.evaluations, &self.eval_data, &value_val_clone, ¤t_value, ¤t_ref_value)?;
2030 let cleaned_val = clean_float_noise(computed_value.clone());
2031
2032 if cleaned_val != current_ref_value && cleaned_val != Value::Null {
2033 if data_path == current_data_path {
2035 current_value = cleaned_val.clone();
2036 }
2037 self.eval_data.set(&data_path, cleaned_val.clone());
2038 change_obj.insert("value".to_string(), cleaned_val);
2039 add_transitive = true;
2040 add_deps = true;
2041 }
2042 }
2043
2044 if add_deps {
2046 result.push(Value::Object(change_obj));
2047 }
2048
2049 if add_transitive {
2051 to_process.push((ref_path.clone(), true));
2052 }
2053 }
2054 }
2055 }
2056
2057 if re_evaluate {
2061 drop(_lock); self.evaluate_internal()?;
2063 }
2064
2065 Ok(Value::Array(result))
2066 }
2067
2068 fn evaluate_dependent_value_static(
2070 engine: &RLogic,
2071 evaluations: &IndexMap<String, LogicId>,
2072 eval_data: &EvalData,
2073 value: &Value,
2074 changed_field_value: &Value,
2075 changed_field_ref_value: &Value
2076 ) -> Result<Value, String> {
2077 match value {
2078 Value::String(eval_key) => {
2080 if let Some(logic_id) = evaluations.get(eval_key) {
2081 let mut internal_context = serde_json::Map::new();
2084 internal_context.insert("$value".to_string(), changed_field_value.clone());
2085 internal_context.insert("$refValue".to_string(), changed_field_ref_value.clone());
2086 let context_value = Value::Object(internal_context);
2087
2088 let result = engine.run_with_context(logic_id, eval_data.data(), &context_value)
2089 .map_err(|e| format!("Failed to evaluate dependent logic '{}': {}", eval_key, e))?;
2090 Ok(result)
2091 } else {
2092 Ok(value.clone())
2094 }
2095 }
2096 Value::Object(map) if map.contains_key("$evaluation") => {
2099 Err("Dependent evaluation contains unparsed $evaluation - schema was not properly parsed".to_string())
2100 }
2101 _ => Ok(value.clone()),
2103 }
2104 }
2105
2106 pub fn validate(
2109 &mut self,
2110 data: &str,
2111 context: Option<&str>,
2112 paths: Option<&[String]>
2113 ) -> Result<ValidationResult, String> {
2114 let _lock = self.eval_lock.lock().unwrap();
2116
2117 let old_data = self.eval_data.clone_data_without(&["$params"]);
2119
2120 let data_value = json_parser::parse_json_str(data)?;
2122 let context_value = if let Some(ctx) = context {
2123 json_parser::parse_json_str(ctx)?
2124 } else {
2125 Value::Object(serde_json::Map::new())
2126 };
2127
2128 self.eval_data.replace_data_and_context(data_value.clone(), context_value);
2130
2131 let changed_data_paths: Vec<String> = if let Some(obj) = data_value.as_object() {
2134 obj.keys().map(|k| format!("/{}", k)).collect()
2135 } else {
2136 Vec::new()
2137 };
2138 self.purge_cache_for_changed_data_with_comparison(&changed_data_paths, &old_data, &data_value);
2139
2140 drop(_lock);
2142
2143 self.evaluate_others();
2146
2147 self.evaluated_schema = self.get_evaluated_schema(false);
2149
2150 let mut errors: IndexMap<String, ValidationError> = IndexMap::new();
2151
2152 for field_path in self.fields_with_rules.iter() {
2155 if let Some(filter_paths) = paths {
2157 if !filter_paths.is_empty() && !filter_paths.iter().any(|p| field_path.starts_with(p.as_str()) || p.starts_with(field_path.as_str())) {
2158 continue;
2159 }
2160 }
2161
2162 self.validate_field(field_path, &data_value, &mut errors);
2163 }
2164
2165 let has_error = !errors.is_empty();
2166
2167 Ok(ValidationResult {
2168 has_error,
2169 errors,
2170 })
2171 }
2172
2173 fn validate_field(
2175 &self,
2176 field_path: &str,
2177 data: &Value,
2178 errors: &mut IndexMap<String, ValidationError>
2179 ) {
2180 if errors.contains_key(field_path) {
2182 return;
2183 }
2184
2185 let schema_path = path_utils::dot_notation_to_schema_pointer(field_path);
2187
2188 let pointer_path = schema_path.trim_start_matches('#');
2190
2191 let field_schema = match self.evaluated_schema.pointer(pointer_path) {
2193 Some(s) => s,
2194 None => {
2195 let alt_path = format!("/properties{}", pointer_path);
2197 match self.evaluated_schema.pointer(&alt_path) {
2198 Some(s) => s,
2199 None => return,
2200 }
2201 }
2202 };
2203
2204 if let Value::Object(schema_map) = field_schema {
2206 if let Some(Value::Object(condition)) = schema_map.get("condition") {
2207 if let Some(Value::Bool(true)) = condition.get("hidden") {
2208 return;
2209 }
2210 }
2211
2212 let rules = match schema_map.get("rules") {
2214 Some(Value::Object(r)) => r,
2215 _ => return,
2216 };
2217
2218 let field_data = self.get_field_data(field_path, data);
2220
2221 for (rule_name, rule_value) in rules {
2223 self.validate_rule(
2224 field_path,
2225 rule_name,
2226 rule_value,
2227 &field_data,
2228 schema_map,
2229 field_schema,
2230 errors
2231 );
2232 }
2233 }
2234 }
2235
2236 fn get_field_data(&self, field_path: &str, data: &Value) -> Value {
2238 let parts: Vec<&str> = field_path.split('.').collect();
2239 let mut current = data;
2240
2241 for part in parts {
2242 match current {
2243 Value::Object(map) => {
2244 current = map.get(part).unwrap_or(&Value::Null);
2245 }
2246 _ => return Value::Null,
2247 }
2248 }
2249
2250 current.clone()
2251 }
2252
2253 fn validate_rule(
2255 &self,
2256 field_path: &str,
2257 rule_name: &str,
2258 rule_value: &Value,
2259 field_data: &Value,
2260 schema_map: &serde_json::Map<String, Value>,
2261 _schema: &Value,
2262 errors: &mut IndexMap<String, ValidationError>
2263 ) {
2264 if errors.contains_key(field_path) {
2266 return;
2267 }
2268
2269 if let Some(Value::Object(condition)) = schema_map.get("condition") {
2271 if let Some(Value::Bool(true)) = condition.get("disabled") {
2272 return;
2273 }
2274 }
2275
2276 let schema_path = path_utils::dot_notation_to_schema_pointer(field_path);
2279 let rule_path = format!("{}/rules/{}", schema_path.trim_start_matches('#'), rule_name);
2280
2281 let evaluated_rule = if let Some(eval_rule) = self.evaluated_schema.pointer(&rule_path) {
2283 eval_rule.clone()
2284 } else {
2285 rule_value.clone()
2286 };
2287
2288 let (rule_active, rule_message, rule_code, rule_data) = match &evaluated_rule {
2290 Value::Object(rule_obj) => {
2291 let active = rule_obj.get("value").unwrap_or(&Value::Bool(false));
2292
2293 let message = match rule_obj.get("message") {
2295 Some(Value::String(s)) => s.clone(),
2296 Some(Value::Object(msg_obj)) if msg_obj.contains_key("value") => {
2297 msg_obj.get("value")
2298 .and_then(|v| v.as_str())
2299 .unwrap_or("Validation failed")
2300 .to_string()
2301 }
2302 Some(msg_val) => msg_val.as_str().unwrap_or("Validation failed").to_string(),
2303 None => "Validation failed".to_string()
2304 };
2305
2306 let code = rule_obj.get("code")
2307 .and_then(|c| c.as_str())
2308 .map(|s| s.to_string());
2309
2310 let data = rule_obj.get("data").map(|d| {
2312 if let Value::Object(data_obj) = d {
2313 let mut cleaned_data = serde_json::Map::new();
2314 for (key, value) in data_obj {
2315 if let Value::Object(val_obj) = value {
2317 if val_obj.len() == 1 && val_obj.contains_key("value") {
2318 cleaned_data.insert(key.clone(), val_obj["value"].clone());
2319 } else {
2320 cleaned_data.insert(key.clone(), value.clone());
2321 }
2322 } else {
2323 cleaned_data.insert(key.clone(), value.clone());
2324 }
2325 }
2326 Value::Object(cleaned_data)
2327 } else {
2328 d.clone()
2329 }
2330 });
2331
2332 (active.clone(), message, code, data)
2333 }
2334 _ => (evaluated_rule.clone(), "Validation failed".to_string(), None, None)
2335 };
2336
2337 let error_code = rule_code.or_else(|| Some(format!("{}.{}", field_path, rule_name)));
2339
2340 let is_empty = matches!(field_data, Value::Null) ||
2341 (field_data.is_string() && field_data.as_str().unwrap_or("").is_empty()) ||
2342 (field_data.is_array() && field_data.as_array().unwrap().is_empty());
2343
2344 match rule_name {
2345 "required" => {
2346 if let Value::Bool(true) = rule_active {
2347 if is_empty {
2348 errors.insert(field_path.to_string(), ValidationError {
2349 rule_type: "required".to_string(),
2350 message: rule_message,
2351 code: error_code.clone(),
2352 pattern: None,
2353 field_value: None,
2354 data: None,
2355 });
2356 }
2357 }
2358 }
2359 "minLength" => {
2360 if !is_empty {
2361 if let Some(min) = rule_active.as_u64() {
2362 let len = match field_data {
2363 Value::String(s) => s.len(),
2364 Value::Array(a) => a.len(),
2365 _ => 0
2366 };
2367 if len < min as usize {
2368 errors.insert(field_path.to_string(), ValidationError {
2369 rule_type: "minLength".to_string(),
2370 message: rule_message,
2371 code: error_code.clone(),
2372 pattern: None,
2373 field_value: None,
2374 data: None,
2375 });
2376 }
2377 }
2378 }
2379 }
2380 "maxLength" => {
2381 if !is_empty {
2382 if let Some(max) = rule_active.as_u64() {
2383 let len = match field_data {
2384 Value::String(s) => s.len(),
2385 Value::Array(a) => a.len(),
2386 _ => 0
2387 };
2388 if len > max as usize {
2389 errors.insert(field_path.to_string(), ValidationError {
2390 rule_type: "maxLength".to_string(),
2391 message: rule_message,
2392 code: error_code.clone(),
2393 pattern: None,
2394 field_value: None,
2395 data: None,
2396 });
2397 }
2398 }
2399 }
2400 }
2401 "minValue" => {
2402 if !is_empty {
2403 if let Some(min) = rule_active.as_f64() {
2404 if let Some(val) = field_data.as_f64() {
2405 if val < min {
2406 errors.insert(field_path.to_string(), ValidationError {
2407 rule_type: "minValue".to_string(),
2408 message: rule_message,
2409 code: error_code.clone(),
2410 pattern: None,
2411 field_value: None,
2412 data: None,
2413 });
2414 }
2415 }
2416 }
2417 }
2418 }
2419 "maxValue" => {
2420 if !is_empty {
2421 if let Some(max) = rule_active.as_f64() {
2422 if let Some(val) = field_data.as_f64() {
2423 if val > max {
2424 errors.insert(field_path.to_string(), ValidationError {
2425 rule_type: "maxValue".to_string(),
2426 message: rule_message,
2427 code: error_code.clone(),
2428 pattern: None,
2429 field_value: None,
2430 data: None,
2431 });
2432 }
2433 }
2434 }
2435 }
2436 }
2437 "pattern" => {
2438 if !is_empty {
2439 if let Some(pattern) = rule_active.as_str() {
2440 if let Some(text) = field_data.as_str() {
2441 if let Ok(regex) = regex::Regex::new(pattern) {
2442 if !regex.is_match(text) {
2443 errors.insert(field_path.to_string(), ValidationError {
2444 rule_type: "pattern".to_string(),
2445 message: rule_message,
2446 code: error_code.clone(),
2447 pattern: Some(pattern.to_string()),
2448 field_value: Some(text.to_string()),
2449 data: None,
2450 });
2451 }
2452 }
2453 }
2454 }
2455 }
2456 }
2457 "evaluation" => {
2458 if let Value::Array(eval_array) = &evaluated_rule {
2461 for (idx, eval_item) in eval_array.iter().enumerate() {
2462 if let Value::Object(eval_obj) = eval_item {
2463 let eval_result = eval_obj.get("value").unwrap_or(&Value::Bool(true));
2465
2466 let is_falsy = match eval_result {
2468 Value::Bool(false) => true,
2469 Value::Null => true,
2470 Value::Number(n) => n.as_f64() == Some(0.0),
2471 Value::String(s) => s.is_empty(),
2472 Value::Array(a) => a.is_empty(),
2473 _ => false,
2474 };
2475
2476 if is_falsy {
2477 let eval_code = eval_obj.get("code")
2478 .and_then(|c| c.as_str())
2479 .map(|s| s.to_string())
2480 .or_else(|| Some(format!("{}.evaluation.{}", field_path, idx)));
2481
2482 let eval_message = eval_obj.get("message")
2483 .and_then(|m| m.as_str())
2484 .unwrap_or("Validation failed")
2485 .to_string();
2486
2487 let eval_data = eval_obj.get("data").cloned();
2488
2489 errors.insert(field_path.to_string(), ValidationError {
2490 rule_type: "evaluation".to_string(),
2491 message: eval_message,
2492 code: eval_code,
2493 pattern: None,
2494 field_value: None,
2495 data: eval_data,
2496 });
2497
2498 break;
2500 }
2501 }
2502 }
2503 }
2504 }
2505 _ => {
2506 if !is_empty {
2510 let is_falsy = match &rule_active {
2512 Value::Bool(false) => true,
2513 Value::Null => true,
2514 Value::Number(n) => n.as_f64() == Some(0.0),
2515 Value::String(s) => s.is_empty(),
2516 Value::Array(a) => a.is_empty(),
2517 _ => false,
2518 };
2519
2520 if is_falsy {
2521 errors.insert(field_path.to_string(), ValidationError {
2522 rule_type: "evaluation".to_string(),
2523 message: rule_message,
2524 code: error_code.clone(),
2525 pattern: None,
2526 field_value: None,
2527 data: rule_data,
2528 });
2529 }
2530 }
2531 }
2532 }
2533 }
2534}
2535
2536#[derive(Debug, Clone, Serialize, Deserialize)]
2538pub struct ValidationError {
2539 #[serde(rename = "type")]
2540 pub rule_type: String,
2541 pub message: String,
2542 #[serde(skip_serializing_if = "Option::is_none")]
2543 pub code: Option<String>,
2544 #[serde(skip_serializing_if = "Option::is_none")]
2545 pub pattern: Option<String>,
2546 #[serde(skip_serializing_if = "Option::is_none")]
2547 pub field_value: Option<String>,
2548 #[serde(skip_serializing_if = "Option::is_none")]
2549 pub data: Option<Value>,
2550}
2551
2552#[derive(Debug, Clone, Serialize, Deserialize)]
2554pub struct ValidationResult {
2555 pub has_error: bool,
2556 pub errors: IndexMap<String, ValidationError>,
2557}
2558