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 _;
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
54pub enum ReturnFormat {
55 #[default]
58 Nested,
59 Flat,
62 Array,
65}
66use serde_json::{Value};
67
68#[cfg(feature = "parallel")]
69use rayon::prelude::*;
70
71use std::mem;
72use std::sync::{Arc, Mutex};
73use std::time::Instant;
74use std::cell::RefCell;
75
76thread_local! {
78 static TIMING_ENABLED: RefCell<bool> = RefCell::new(std::env::var("JSONEVAL_TIMING").is_ok());
79 static TIMING_DATA: RefCell<Vec<(String, std::time::Duration)>> = RefCell::new(Vec::new());
80}
81
82#[inline]
84fn is_timing_enabled() -> bool {
85 TIMING_ENABLED.with(|enabled| *enabled.borrow())
86}
87
88pub fn enable_timing() {
90 TIMING_ENABLED.with(|enabled| {
91 *enabled.borrow_mut() = true;
92 });
93}
94
95pub fn disable_timing() {
97 TIMING_ENABLED.with(|enabled| {
98 *enabled.borrow_mut() = false;
99 });
100}
101
102#[inline]
104fn record_timing(label: &str, duration: std::time::Duration) {
105 if is_timing_enabled() {
106 TIMING_DATA.with(|data| {
107 data.borrow_mut().push((label.to_string(), duration));
108 });
109 }
110}
111
112pub fn print_timing_summary() {
114 if !is_timing_enabled() {
115 return;
116 }
117
118 TIMING_DATA.with(|data| {
119 let timings = data.borrow();
120 if timings.is_empty() {
121 return;
122 }
123
124 eprintln!("\nš Timing Summary (JSONEVAL_TIMING enabled)");
125 eprintln!("{}", "=".repeat(60));
126
127 let mut total = std::time::Duration::ZERO;
128 for (label, duration) in timings.iter() {
129 eprintln!("{:40} {:>12?}", label, duration);
130 total += *duration;
131 }
132
133 eprintln!("{}", "=".repeat(60));
134 eprintln!("{:40} {:>12?}", "TOTAL", total);
135 eprintln!();
136 });
137}
138
139pub fn clear_timing_data() {
141 TIMING_DATA.with(|data| {
142 data.borrow_mut().clear();
143 });
144}
145
146macro_rules! time_block {
148 ($label:expr, $block:block) => {{
149 let _start = if is_timing_enabled() {
150 Some(Instant::now())
151 } else {
152 None
153 };
154 let result = $block;
155 if let Some(start) = _start {
156 record_timing($label, start.elapsed());
157 }
158 result
159 }};
160}
161
162pub fn version() -> &'static str {
164 env!("CARGO_PKG_VERSION")
165}
166
167fn clean_float_noise(value: Value) -> Value {
170 const EPSILON: f64 = 1e-10;
171
172 match value {
173 Value::Number(n) => {
174 if let Some(f) = n.as_f64() {
175 if f.abs() < EPSILON {
176 Value::Number(serde_json::Number::from(0))
178 } else if f.fract().abs() < EPSILON {
179 Value::Number(serde_json::Number::from(f.round() as i64))
181 } else {
182 Value::Number(n)
183 }
184 } else {
185 Value::Number(n)
186 }
187 }
188 Value::Array(arr) => {
189 Value::Array(arr.into_iter().map(clean_float_noise).collect())
190 }
191 Value::Object(obj) => {
192 Value::Object(obj.into_iter().map(|(k, v)| (k, clean_float_noise(v))).collect())
193 }
194 _ => value,
195 }
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct DependentItem {
201 pub ref_path: String,
202 pub clear: Option<Value>, pub value: Option<Value>, }
205
206pub struct JSONEval {
207 pub schema: Arc<Value>,
208 pub engine: Arc<RLogic>,
209 pub evaluations: Arc<IndexMap<String, LogicId>>,
211 pub tables: Arc<IndexMap<String, Value>>,
212 pub table_metadata: Arc<IndexMap<String, TableMetadata>>,
214 pub dependencies: Arc<IndexMap<String, IndexSet<String>>>,
215 pub sorted_evaluations: Arc<Vec<Vec<String>>>,
218 pub dependents_evaluations: Arc<IndexMap<String, Vec<DependentItem>>>,
221 pub rules_evaluations: Arc<Vec<String>>,
223 pub fields_with_rules: Arc<Vec<String>>,
225 pub others_evaluations: Arc<Vec<String>>,
227 pub value_evaluations: Arc<Vec<String>>,
229 pub layout_paths: Arc<Vec<String>>,
231 pub options_templates: Arc<Vec<(String, String, String)>>,
233 pub subforms: IndexMap<String, Box<JSONEval>>,
236 pub context: Value,
237 pub data: Value,
238 pub evaluated_schema: Value,
239 pub eval_data: EvalData,
240 pub eval_cache: EvalCache,
242 pub cache_enabled: bool,
245 eval_lock: Mutex<()>,
247 cached_msgpack_schema: Option<Vec<u8>>,
250}
251
252impl Clone for JSONEval {
253 fn clone(&self) -> Self {
254 Self {
255 cache_enabled: self.cache_enabled,
256 schema: Arc::clone(&self.schema),
257 engine: Arc::clone(&self.engine),
258 evaluations: self.evaluations.clone(),
259 tables: self.tables.clone(),
260 table_metadata: self.table_metadata.clone(),
261 dependencies: self.dependencies.clone(),
262 sorted_evaluations: self.sorted_evaluations.clone(),
263 dependents_evaluations: self.dependents_evaluations.clone(),
264 rules_evaluations: self.rules_evaluations.clone(),
265 fields_with_rules: self.fields_with_rules.clone(),
266 others_evaluations: self.others_evaluations.clone(),
267 value_evaluations: self.value_evaluations.clone(),
268 layout_paths: self.layout_paths.clone(),
269 options_templates: self.options_templates.clone(),
270 subforms: self.subforms.clone(),
271 context: self.context.clone(),
272 data: self.data.clone(),
273 evaluated_schema: self.evaluated_schema.clone(),
274 eval_data: self.eval_data.clone(),
275 eval_cache: EvalCache::new(), eval_lock: Mutex::new(()), cached_msgpack_schema: self.cached_msgpack_schema.clone(),
278 }
279 }
280}
281
282impl JSONEval {
283 pub fn new(
284 schema: &str,
285 context: Option<&str>,
286 data: Option<&str>,
287 ) -> Result<Self, serde_json::Error> {
288 time_block!("JSONEval::new() [total]", {
289 let schema_val: Value = time_block!(" parse schema JSON", {
291 serde_json::from_str(schema)?
292 });
293 let context: Value = time_block!(" parse context JSON", {
294 json_parser::parse_json_str(context.unwrap_or("{}")).map_err(serde_json::Error::custom)?
295 });
296 let data: Value = time_block!(" parse data JSON", {
297 json_parser::parse_json_str(data.unwrap_or("{}")).map_err(serde_json::Error::custom)?
298 });
299 let evaluated_schema = schema_val.clone();
300 let engine_config = RLogicConfig::default();
302
303 let mut instance = time_block!(" create instance struct", {
304 Self {
305 schema: Arc::new(schema_val),
306 evaluations: Arc::new(IndexMap::new()),
307 tables: Arc::new(IndexMap::new()),
308 table_metadata: Arc::new(IndexMap::new()),
309 dependencies: Arc::new(IndexMap::new()),
310 sorted_evaluations: Arc::new(Vec::new()),
311 dependents_evaluations: Arc::new(IndexMap::new()),
312 rules_evaluations: Arc::new(Vec::new()),
313 fields_with_rules: Arc::new(Vec::new()),
314 others_evaluations: Arc::new(Vec::new()),
315 value_evaluations: Arc::new(Vec::new()),
316 layout_paths: Arc::new(Vec::new()),
317 options_templates: Arc::new(Vec::new()),
318 subforms: IndexMap::new(),
319 engine: Arc::new(RLogic::with_config(engine_config)),
320 context: context.clone(),
321 data: data.clone(),
322 evaluated_schema: evaluated_schema.clone(),
323 eval_data: EvalData::with_schema_data_context(&evaluated_schema, &data, &context),
324 eval_cache: EvalCache::new(),
325 cache_enabled: true, eval_lock: Mutex::new(()),
327 cached_msgpack_schema: None, }
329 });
330 time_block!(" parse_schema", {
331 parse_schema::legacy::parse_schema(&mut instance).map_err(serde_json::Error::custom)?
332 });
333 Ok(instance)
334 })
335 }
336
337 pub fn new_from_msgpack(
349 schema_msgpack: &[u8],
350 context: Option<&str>,
351 data: Option<&str>,
352 ) -> Result<Self, String> {
353 let cached_msgpack = schema_msgpack.to_vec();
355
356 let schema_val: Value = rmp_serde::from_slice(schema_msgpack)
358 .map_err(|e| format!("Failed to deserialize MessagePack schema: {}", e))?;
359
360 let context: Value = json_parser::parse_json_str(context.unwrap_or("{}"))
361 .map_err(|e| format!("Failed to parse context: {}", e))?;
362 let data: Value = json_parser::parse_json_str(data.unwrap_or("{}"))
363 .map_err(|e| format!("Failed to parse data: {}", e))?;
364 let evaluated_schema = schema_val.clone();
365 let engine_config = RLogicConfig::default();
366
367 let mut instance = Self {
368 schema: Arc::new(schema_val),
369 evaluations: Arc::new(IndexMap::new()),
370 tables: Arc::new(IndexMap::new()),
371 table_metadata: Arc::new(IndexMap::new()),
372 dependencies: Arc::new(IndexMap::new()),
373 sorted_evaluations: Arc::new(Vec::new()),
374 dependents_evaluations: Arc::new(IndexMap::new()),
375 rules_evaluations: Arc::new(Vec::new()),
376 fields_with_rules: Arc::new(Vec::new()),
377 others_evaluations: Arc::new(Vec::new()),
378 value_evaluations: Arc::new(Vec::new()),
379 layout_paths: Arc::new(Vec::new()),
380 options_templates: Arc::new(Vec::new()),
381 subforms: IndexMap::new(),
382 engine: Arc::new(RLogic::with_config(engine_config)),
383 context: context.clone(),
384 data: data.clone(),
385 evaluated_schema: evaluated_schema.clone(),
386 eval_data: EvalData::with_schema_data_context(&evaluated_schema, &data, &context),
387 eval_cache: EvalCache::new(),
388 cache_enabled: true, eval_lock: Mutex::new(()),
390 cached_msgpack_schema: Some(cached_msgpack), };
392 parse_schema::legacy::parse_schema(&mut instance)?;
393 Ok(instance)
394 }
395
396 pub fn with_parsed_schema(
424 parsed: Arc<ParsedSchema>,
425 context: Option<&str>,
426 data: Option<&str>,
427 ) -> Result<Self, String> {
428 let context: Value = json_parser::parse_json_str(context.unwrap_or("{}"))
429 .map_err(|e| format!("Failed to parse context: {}", e))?;
430 let data: Value = json_parser::parse_json_str(data.unwrap_or("{}"))
431 .map_err(|e| format!("Failed to parse data: {}", e))?;
432
433 let evaluated_schema = parsed.schema.clone();
434
435 let engine = parsed.engine.clone();
438
439 let mut subforms = IndexMap::new();
442 for (path, subform_parsed) in &parsed.subforms {
443 let subform_eval = JSONEval::with_parsed_schema(
445 subform_parsed.clone(),
446 Some("{}"),
447 None
448 )?;
449 subforms.insert(path.clone(), Box::new(subform_eval));
450 }
451
452 let instance = Self {
453 schema: Arc::clone(&parsed.schema),
454 evaluations: Arc::clone(&parsed.evaluations),
456 tables: Arc::clone(&parsed.tables),
457 table_metadata: Arc::clone(&parsed.table_metadata),
458 dependencies: Arc::clone(&parsed.dependencies),
459 sorted_evaluations: Arc::clone(&parsed.sorted_evaluations),
460 dependents_evaluations: Arc::clone(&parsed.dependents_evaluations),
461 rules_evaluations: Arc::clone(&parsed.rules_evaluations),
462 fields_with_rules: Arc::clone(&parsed.fields_with_rules),
463 others_evaluations: Arc::clone(&parsed.others_evaluations),
464 value_evaluations: Arc::clone(&parsed.value_evaluations),
465 layout_paths: Arc::clone(&parsed.layout_paths),
466 options_templates: Arc::clone(&parsed.options_templates),
467 subforms,
468 engine,
469 context: context.clone(),
470 data: data.clone(),
471 evaluated_schema: (*evaluated_schema).clone(),
472 eval_data: EvalData::with_schema_data_context(&evaluated_schema, &data, &context),
473 eval_cache: EvalCache::new(),
474 cache_enabled: true, eval_lock: Mutex::new(()),
476 cached_msgpack_schema: None, };
478
479 Ok(instance)
480 }
481
482 pub fn reload_schema(
483 &mut self,
484 schema: &str,
485 context: Option<&str>,
486 data: Option<&str>,
487 ) -> Result<(), String> {
488 let schema_val: Value = serde_json::from_str(schema).map_err(|e| format!("failed to parse schema: {e}"))?;
490 let context: Value = json_parser::parse_json_str(context.unwrap_or("{}"))?;
491 let data: Value = json_parser::parse_json_str(data.unwrap_or("{}"))?;
492 self.schema = Arc::new(schema_val);
493 self.context = context.clone();
494 self.data = data.clone();
495 self.evaluated_schema = (*self.schema).clone();
496 self.engine = Arc::new(RLogic::new());
497 self.dependents_evaluations = Arc::new(IndexMap::new());
498 self.rules_evaluations = Arc::new(Vec::new());
499 self.fields_with_rules = Arc::new(Vec::new());
500 self.others_evaluations = Arc::new(Vec::new());
501 self.value_evaluations = Arc::new(Vec::new());
502 self.layout_paths = Arc::new(Vec::new());
503 self.options_templates = Arc::new(Vec::new());
504 self.subforms.clear();
505 parse_schema::legacy::parse_schema(self)?;
506
507 self.eval_data = EvalData::with_schema_data_context(&self.evaluated_schema, &data, &context);
509
510 self.eval_cache.clear();
512
513 self.cached_msgpack_schema = None;
515
516 Ok(())
517 }
518
519 pub fn reload_schema_msgpack(
531 &mut self,
532 schema_msgpack: &[u8],
533 context: Option<&str>,
534 data: Option<&str>,
535 ) -> Result<(), String> {
536 let schema_val: Value = rmp_serde::from_slice(schema_msgpack)
538 .map_err(|e| format!("failed to deserialize MessagePack schema: {e}"))?;
539
540 let context: Value = json_parser::parse_json_str(context.unwrap_or("{}"))?;
541 let data: Value = json_parser::parse_json_str(data.unwrap_or("{}"))?;
542
543 self.schema = Arc::new(schema_val);
544 self.context = context.clone();
545 self.data = data.clone();
546 self.evaluated_schema = (*self.schema).clone();
547 self.engine = Arc::new(RLogic::new());
548 self.dependents_evaluations = Arc::new(IndexMap::new());
549 self.rules_evaluations = Arc::new(Vec::new());
550 self.fields_with_rules = Arc::new(Vec::new());
551 self.others_evaluations = Arc::new(Vec::new());
552 self.value_evaluations = Arc::new(Vec::new());
553 self.layout_paths = Arc::new(Vec::new());
554 self.options_templates = Arc::new(Vec::new());
555 self.subforms.clear();
556 parse_schema::legacy::parse_schema(self)?;
557
558 self.eval_data = EvalData::with_schema_data_context(&self.evaluated_schema, &data, &context);
560
561 self.eval_cache.clear();
563
564 self.cached_msgpack_schema = Some(schema_msgpack.to_vec());
566
567 Ok(())
568 }
569
570 pub fn reload_schema_parsed(
584 &mut self,
585 parsed: Arc<ParsedSchema>,
586 context: Option<&str>,
587 data: Option<&str>,
588 ) -> Result<(), String> {
589 let context: Value = json_parser::parse_json_str(context.unwrap_or("{}"))?;
590 let data: Value = json_parser::parse_json_str(data.unwrap_or("{}"))?;
591
592 self.schema = Arc::clone(&parsed.schema);
594 self.evaluations = parsed.evaluations.clone();
595 self.tables = parsed.tables.clone();
596 self.table_metadata = parsed.table_metadata.clone();
597 self.dependencies = parsed.dependencies.clone();
598 self.sorted_evaluations = parsed.sorted_evaluations.clone();
599 self.dependents_evaluations = parsed.dependents_evaluations.clone();
600 self.rules_evaluations = parsed.rules_evaluations.clone();
601 self.fields_with_rules = parsed.fields_with_rules.clone();
602 self.others_evaluations = parsed.others_evaluations.clone();
603 self.value_evaluations = parsed.value_evaluations.clone();
604 self.layout_paths = parsed.layout_paths.clone();
605 self.options_templates = parsed.options_templates.clone();
606
607 self.engine = parsed.engine.clone();
609
610 let mut subforms = IndexMap::new();
612 for (path, subform_parsed) in &parsed.subforms {
613 let subform_eval = JSONEval::with_parsed_schema(
614 subform_parsed.clone(),
615 Some("{}"),
616 None
617 )?;
618 subforms.insert(path.clone(), Box::new(subform_eval));
619 }
620 self.subforms = subforms;
621
622 self.context = context.clone();
623 self.data = data.clone();
624 self.evaluated_schema = (*self.schema).clone();
625
626 self.eval_data = EvalData::with_schema_data_context(&self.evaluated_schema, &data, &context);
628
629 self.eval_cache.clear();
631
632 self.cached_msgpack_schema = None;
634
635 Ok(())
636 }
637
638 pub fn reload_schema_from_cache(
652 &mut self,
653 cache_key: &str,
654 context: Option<&str>,
655 data: Option<&str>,
656 ) -> Result<(), String> {
657 let parsed = PARSED_SCHEMA_CACHE.get(cache_key)
659 .ok_or_else(|| format!("Schema '{}' not found in cache", cache_key))?;
660
661 self.reload_schema_parsed(parsed, context, data)
663 }
664
665 pub fn evaluate(&mut self, data: &str, context: Option<&str>) -> Result<(), String> {
676 time_block!("evaluate() [total]", {
677 let data: Value = time_block!(" parse data", {
679 json_parser::parse_json_str(data)?
680 });
681 let context: Value = time_block!(" parse context", {
682 json_parser::parse_json_str(context.unwrap_or("{}"))?
683 });
684
685 self.data = data.clone();
686
687 let changed_data_paths: Vec<String> = if let Some(obj) = data.as_object() {
689 obj.keys().map(|k| k.clone()).collect()
690 } else {
691 Vec::new()
692 };
693
694 time_block!(" replace_data_and_context", {
696 self.eval_data.replace_data_and_context(data, context);
697 });
698
699 time_block!(" purge_cache", {
702 self.purge_cache_for_changed_data(&changed_data_paths);
703 });
704
705 self.evaluate_internal()
707 })
708 }
709
710 fn evaluate_internal(&mut self) -> Result<(), String> {
713 time_block!(" evaluate_internal() [total]", {
714 let _lock = self.eval_lock.lock().unwrap();
716
717 let eval_batches: Vec<Vec<String>> = (*self.sorted_evaluations).clone();
719
720 time_block!(" process batches", {
723 for batch in eval_batches {
724 if batch.is_empty() {
726 continue;
727 }
728
729 let eval_data_snapshot = self.eval_data.clone();
736
737 #[cfg(feature = "parallel")]
739 if batch.len() > 1000 {
740 let results: Mutex<Vec<(String, String, Value)>> = Mutex::new(Vec::with_capacity(batch.len()));
741 batch.par_iter().for_each(|eval_key| {
742 let pointer_path = path_utils::normalize_to_json_pointer(eval_key);
743
744 if let Some(_) = self.try_get_cached(eval_key, &eval_data_snapshot) {
746 return;
747 }
748
749 let is_table = self.table_metadata.contains_key(eval_key);
751
752 if is_table {
753 if let Ok(rows) = table_evaluate::evaluate_table(self, eval_key, &eval_data_snapshot) {
755 let value = Value::Array(rows);
756 self.cache_result(eval_key, Value::Null, &eval_data_snapshot);
758 results.lock().unwrap().push((eval_key.clone(), pointer_path, value));
759 }
760 } else {
761 if let Some(logic_id) = self.evaluations.get(eval_key) {
762 if let Ok(val) = self.engine.run(logic_id, eval_data_snapshot.data()) {
764 let cleaned_val = clean_float_noise(val);
765 self.cache_result(eval_key, Value::Null, &eval_data_snapshot);
767 results.lock().unwrap().push((eval_key.clone(), pointer_path, cleaned_val));
768 }
769 }
770 }
771 });
772
773 for (_eval_key, path, value) in results.into_inner().unwrap() {
775 let cleaned_value = clean_float_noise(value);
776
777 self.eval_data.set(&path, cleaned_value.clone());
778 if let Some(schema_value) = self.evaluated_schema.pointer_mut(&path) {
780 *schema_value = cleaned_value;
781 }
782 }
783 continue;
784 }
785
786 #[cfg(not(feature = "parallel"))]
788 let batch_items = &batch;
789
790 #[cfg(feature = "parallel")]
791 let batch_items = if batch.len() > 1000 { &batch[0..0] } else { &batch }; for eval_key in batch_items {
794 let pointer_path = path_utils::normalize_to_json_pointer(eval_key);
795
796 if let Some(_) = self.try_get_cached(eval_key, &eval_data_snapshot) {
798 continue;
799 }
800
801 let is_table = self.table_metadata.contains_key(eval_key);
803
804 if is_table {
805 if let Ok(rows) = table_evaluate::evaluate_table(self, eval_key, &eval_data_snapshot) {
806 let value = Value::Array(rows);
807 self.cache_result(eval_key, Value::Null, &eval_data_snapshot);
809
810 let cleaned_value = clean_float_noise(value);
811 self.eval_data.set(&pointer_path, cleaned_value.clone());
812 if let Some(schema_value) = self.evaluated_schema.pointer_mut(&pointer_path) {
813 *schema_value = cleaned_value;
814 }
815 }
816 } else {
817 if let Some(logic_id) = self.evaluations.get(eval_key) {
818 if let Ok(val) = self.engine.run(logic_id, eval_data_snapshot.data()) {
819 let cleaned_val = clean_float_noise(val);
820 self.cache_result(eval_key, Value::Null, &eval_data_snapshot);
822
823 self.eval_data.set(&pointer_path, cleaned_val.clone());
824 if let Some(schema_value) = self.evaluated_schema.pointer_mut(&pointer_path) {
825 *schema_value = cleaned_val;
826 }
827 }
828 }
829 }
830 }
831 }
832 });
833
834 drop(_lock);
836
837 self.evaluate_others();
838
839 Ok(())
840 })
841 }
842
843 pub fn get_evaluated_schema(&mut self, skip_layout: bool) -> Value {
853 time_block!("get_evaluated_schema()", {
854 if !skip_layout {
855 self.resolve_layout_internal();
856 }
857
858 self.evaluated_schema.clone()
859 })
860 }
861
862 pub fn get_evaluated_schema_msgpack(&mut self, skip_layout: bool) -> Result<Vec<u8>, String> {
879 if !skip_layout {
880 self.resolve_layout_internal();
881 }
882
883 rmp_serde::to_vec(&self.evaluated_schema)
887 .map_err(|e| format!("Failed to serialize schema to MessagePack: {}", e))
888 }
889
890 pub fn get_schema_value(&mut self) -> Value {
894 if !self.data.is_object() {
896 self.data = Value::Object(serde_json::Map::new());
897 }
898
899 for eval_key in self.value_evaluations.iter() {
901 let clean_key = eval_key.replace("#", "");
902 let path = clean_key.replace("/properties", "").replace("/value", "");
903
904 let value = match self.evaluated_schema.pointer(&clean_key) {
906 Some(v) => v.clone(),
907 None => continue,
908 };
909
910 let path_parts: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
912
913 if path_parts.is_empty() {
914 continue;
915 }
916
917 let mut current = &mut self.data;
919 for (i, part) in path_parts.iter().enumerate() {
920 let is_last = i == path_parts.len() - 1;
921
922 if is_last {
923 if let Some(obj) = current.as_object_mut() {
925 obj.insert(part.to_string(), clean_float_noise(value.clone()));
926 }
927 } else {
928 if let Some(obj) = current.as_object_mut() {
930 current = obj.entry(part.to_string())
931 .or_insert_with(|| Value::Object(serde_json::Map::new()));
932 } else {
933 break;
935 }
936 }
937 }
938 }
939
940 clean_float_noise(self.data.clone())
941 }
942
943 pub fn get_evaluated_schema_without_params(&mut self, skip_layout: bool) -> Value {
954 if !skip_layout {
955 self.resolve_layout_internal();
956 }
957
958 if let Value::Object(mut map) = self.evaluated_schema.clone() {
960 map.remove("$params");
961 Value::Object(map)
962 } else {
963 self.evaluated_schema.clone()
964 }
965 }
966
967 pub fn get_evaluated_schema_by_path(&mut self, path: &str, skip_layout: bool) -> Option<Value> {
979 if !skip_layout {
980 self.resolve_layout_internal();
981 }
982
983 let pointer = if path.is_empty() {
985 "".to_string()
986 } else {
987 format!("/{}", path.replace(".", "/"))
988 };
989
990 self.evaluated_schema.pointer(&pointer).cloned()
991 }
992
993 pub fn get_evaluated_schema_by_paths(&mut self, paths: &[String], skip_layout: bool, format: Option<ReturnFormat>) -> Value {
1006 let format = format.unwrap_or_default();
1007 if !skip_layout {
1008 self.resolve_layout_internal();
1009 }
1010
1011 let mut result = serde_json::Map::new();
1012
1013 for path in paths {
1014 let pointer = if path.is_empty() {
1016 "".to_string()
1017 } else {
1018 format!("/{}", path.replace(".", "/"))
1019 };
1020
1021 if let Some(value) = self.evaluated_schema.pointer(&pointer) {
1023 self.insert_at_path(&mut result, path, value.clone());
1026 }
1027 }
1028
1029 self.convert_to_format(result, paths, format)
1030 }
1031
1032 fn insert_at_path(&self, obj: &mut serde_json::Map<String, Value>, path: &str, value: Value) {
1034 if path.is_empty() {
1035 if let Value::Object(map) = value {
1037 for (k, v) in map {
1038 obj.insert(k, v);
1039 }
1040 }
1041 return;
1042 }
1043
1044 let parts: Vec<&str> = path.split('.').collect();
1045 if parts.is_empty() {
1046 return;
1047 }
1048
1049 let mut current = obj;
1050 let last_index = parts.len() - 1;
1051
1052 for (i, part) in parts.iter().enumerate() {
1053 if i == last_index {
1054 current.insert(part.to_string(), value);
1056 break;
1057 } else {
1058 current = current
1060 .entry(part.to_string())
1061 .or_insert_with(|| Value::Object(serde_json::Map::new()))
1062 .as_object_mut()
1063 .unwrap();
1064 }
1065 }
1066 }
1067
1068 fn convert_to_format(&self, result: serde_json::Map<String, Value>, paths: &[String], format: ReturnFormat) -> Value {
1070 match format {
1071 ReturnFormat::Nested => Value::Object(result),
1072 ReturnFormat::Flat => {
1073 let mut flat = serde_json::Map::new();
1075 self.flatten_object(&result, String::new(), &mut flat);
1076 Value::Object(flat)
1077 }
1078 ReturnFormat::Array => {
1079 let values: Vec<Value> = paths.iter()
1081 .map(|path| {
1082 let pointer = if path.is_empty() {
1083 "".to_string()
1084 } else {
1085 format!("/{}", path.replace(".", "/"))
1086 };
1087 Value::Object(result.clone()).pointer(&pointer).cloned().unwrap_or(Value::Null)
1088 })
1089 .collect();
1090 Value::Array(values)
1091 }
1092 }
1093 }
1094
1095 fn flatten_object(&self, obj: &serde_json::Map<String, Value>, prefix: String, result: &mut serde_json::Map<String, Value>) {
1097 for (key, value) in obj {
1098 let new_key = if prefix.is_empty() {
1099 key.clone()
1100 } else {
1101 format!("{}.{}", prefix, key)
1102 };
1103
1104 if let Value::Object(nested) = value {
1105 self.flatten_object(nested, new_key, result);
1106 } else {
1107 result.insert(new_key, value.clone());
1108 }
1109 }
1110 }
1111
1112 pub fn get_schema_by_path(&self, path: &str) -> Option<Value> {
1123 let pointer = if path.is_empty() {
1125 "".to_string()
1126 } else {
1127 format!("/{}", path.replace(".", "/"))
1128 };
1129
1130 self.schema.pointer(&pointer).cloned()
1131 }
1132
1133 pub fn get_schema_by_paths(&self, paths: &[String], format: Option<ReturnFormat>) -> Value {
1145 let format = format.unwrap_or_default();
1146 let mut result = serde_json::Map::new();
1147
1148 for path in paths {
1149 let pointer = if path.is_empty() {
1151 "".to_string()
1152 } else {
1153 format!("/{}", path.replace(".", "/"))
1154 };
1155
1156 if let Some(value) = self.schema.pointer(&pointer) {
1158 self.insert_at_path(&mut result, path, value.clone());
1161 }
1162 }
1163
1164 self.convert_to_format(result, paths, format)
1165 }
1166
1167 #[inline]
1170 fn should_cache_dependency(key: &str) -> bool {
1171 if key.starts_with("/$") || key.starts_with('$') {
1172 key == "$context" || key.starts_with("$context.") || key.starts_with("/$context")
1174 } else {
1175 true
1176 }
1177 }
1178
1179 fn try_get_cached(&self, eval_key: &str, eval_data: &EvalData) -> Option<Value> {
1182 if !self.cache_enabled {
1184 return None;
1185 }
1186
1187 let deps = self.dependencies.get(eval_key)?;
1189
1190 let cache_key = if deps.is_empty() {
1192 CacheKey::simple(eval_key.to_string())
1193 } else {
1194 let filtered_deps: IndexSet<String> = deps
1196 .iter()
1197 .filter(|dep_key| JSONEval::should_cache_dependency(dep_key))
1198 .cloned()
1199 .collect();
1200
1201 let dep_values: Vec<(String, &Value)> = filtered_deps
1203 .iter()
1204 .filter_map(|dep_key| {
1205 eval_data.get(dep_key).map(|v| (dep_key.clone(), v))
1206 })
1207 .collect();
1208
1209 CacheKey::new(eval_key.to_string(), &filtered_deps, &dep_values)
1210 };
1211
1212 self.eval_cache.get(&cache_key).map(|arc_val| (*arc_val).clone())
1214 }
1215
1216 fn cache_result(&self, eval_key: &str, value: Value, eval_data: &EvalData) {
1218 if !self.cache_enabled {
1220 return;
1221 }
1222
1223 let deps = match self.dependencies.get(eval_key) {
1225 Some(d) => d,
1226 None => {
1227 let cache_key = CacheKey::simple(eval_key.to_string());
1229 self.eval_cache.insert(cache_key, value);
1230 return;
1231 }
1232 };
1233
1234 let filtered_deps: IndexSet<String> = deps
1236 .iter()
1237 .filter(|dep_key| JSONEval::should_cache_dependency(dep_key))
1238 .cloned()
1239 .collect();
1240
1241 let dep_values: Vec<(String, &Value)> = filtered_deps
1242 .iter()
1243 .filter_map(|dep_key| {
1244 eval_data.get(dep_key).map(|v| (dep_key.clone(), v))
1245 })
1246 .collect();
1247
1248 let cache_key = CacheKey::new(eval_key.to_string(), &filtered_deps, &dep_values);
1249 self.eval_cache.insert(cache_key, value);
1250 }
1251
1252 fn purge_cache_for_changed_data_with_comparison(
1256 &self,
1257 changed_data_paths: &[String],
1258 old_data: &Value,
1259 new_data: &Value
1260 ) {
1261 if changed_data_paths.is_empty() {
1262 return;
1263 }
1264
1265 let mut actually_changed_paths = Vec::new();
1267 for path in changed_data_paths {
1268 let old_val = old_data.pointer(path);
1269 let new_val = new_data.pointer(path);
1270
1271 if old_val != new_val {
1273 actually_changed_paths.push(path.clone());
1274 }
1275 }
1276
1277 if actually_changed_paths.is_empty() {
1279 return;
1280 }
1281
1282 let mut affected_eval_keys = IndexSet::new();
1284
1285 for (eval_key, deps) in self.dependencies.iter() {
1286 let is_affected = deps.iter().any(|dep| {
1288 actually_changed_paths.iter().any(|changed_path| {
1290 dep == changed_path ||
1292 dep.starts_with(&format!("{}/", changed_path)) ||
1293 changed_path.starts_with(&format!("{}/", dep))
1294 })
1295 });
1296
1297 if is_affected {
1298 affected_eval_keys.insert(eval_key.clone());
1299 }
1300 }
1301
1302 self.eval_cache.retain(|cache_key, _| {
1305 !affected_eval_keys.contains(&cache_key.eval_key)
1306 });
1307 }
1308
1309 fn purge_cache_for_changed_data(&self, changed_data_paths: &[String]) {
1312 if changed_data_paths.is_empty() {
1313 return;
1314 }
1315
1316 let mut affected_eval_keys = IndexSet::new();
1318
1319 for (eval_key, deps) in self.dependencies.iter() {
1320 let is_affected = deps.iter().any(|dep| {
1322 changed_data_paths.iter().any(|changed_path| {
1324 dep == changed_path ||
1326 dep.starts_with(&format!("{}/", changed_path)) ||
1327 changed_path.starts_with(&format!("{}/", dep))
1328 })
1329 });
1330
1331 if is_affected {
1332 affected_eval_keys.insert(eval_key.clone());
1333 }
1334 }
1335
1336 self.eval_cache.retain(|cache_key, _| {
1339 !affected_eval_keys.contains(&cache_key.eval_key)
1340 });
1341 }
1342
1343 pub fn cache_stats(&self) -> CacheStats {
1345 self.eval_cache.stats()
1346 }
1347
1348 pub fn clear_cache(&mut self) {
1350 self.eval_cache.clear();
1351 }
1352
1353 pub fn cache_len(&self) -> usize {
1355 self.eval_cache.len()
1356 }
1357
1358 pub fn enable_cache(&mut self) {
1361 self.cache_enabled = true;
1362 }
1363
1364 pub fn disable_cache(&mut self) {
1368 self.cache_enabled = false;
1369 self.eval_cache.clear(); }
1371
1372 pub fn is_cache_enabled(&self) -> bool {
1374 self.cache_enabled
1375 }
1376
1377 fn evaluate_others(&mut self) {
1378 time_block!(" evaluate_others()", {
1379 time_block!(" evaluate_options_templates", {
1381 self.evaluate_options_templates();
1382 });
1383
1384 let combined_count = self.rules_evaluations.len() + self.others_evaluations.len();
1387 if combined_count == 0 {
1388 return;
1389 }
1390
1391 time_block!(" evaluate rules+others", {
1392 let eval_data_snapshot = self.eval_data.clone();
1393
1394 #[cfg(feature = "parallel")]
1395 {
1396 let combined_results: Mutex<Vec<(String, Value)>> = Mutex::new(Vec::with_capacity(combined_count));
1397
1398 self.rules_evaluations
1399 .par_iter()
1400 .chain(self.others_evaluations.par_iter())
1401 .for_each(|eval_key| {
1402 let pointer_path = path_utils::normalize_to_json_pointer(eval_key);
1403
1404 if let Some(_) = self.try_get_cached(eval_key, &eval_data_snapshot) {
1406 return;
1407 }
1408
1409 if let Some(logic_id) = self.evaluations.get(eval_key) {
1411 if let Ok(val) = self.engine.run(logic_id, eval_data_snapshot.data()) {
1412 let cleaned_val = clean_float_noise(val);
1413 self.cache_result(eval_key, Value::Null, &eval_data_snapshot);
1415 combined_results.lock().unwrap().push((pointer_path, cleaned_val));
1416 }
1417 }
1418 });
1419
1420 for (result_path, value) in combined_results.into_inner().unwrap() {
1422 if let Some(pointer_value) = self.evaluated_schema.pointer_mut(&result_path) {
1423 if !result_path.starts_with("$") && result_path.contains("/rules/") && !result_path.ends_with("/value") {
1426 match pointer_value.as_object_mut() {
1427 Some(pointer_obj) => {
1428 pointer_obj.remove("$evaluation");
1429 pointer_obj.insert("value".to_string(), value);
1430 },
1431 None => continue,
1432 }
1433 } else {
1434 *pointer_value = value;
1435 }
1436 }
1437 }
1438 }
1439
1440 #[cfg(not(feature = "parallel"))]
1441 {
1442 for eval_key in self.rules_evaluations.iter().chain(self.others_evaluations.iter()) {
1444 let pointer_path = path_utils::normalize_to_json_pointer(eval_key);
1445
1446 if let Some(_) = self.try_get_cached(eval_key, &eval_data_snapshot) {
1448 continue;
1449 }
1450
1451 if let Some(logic_id) = self.evaluations.get(eval_key) {
1453 if let Ok(val) = self.engine.run(logic_id, eval_data_snapshot.data()) {
1454 let cleaned_val = clean_float_noise(val);
1455 self.cache_result(eval_key, Value::Null, &eval_data_snapshot);
1457
1458 if let Some(pointer_value) = self.evaluated_schema.pointer_mut(&pointer_path) {
1459 if !pointer_path.starts_with("$") && pointer_path.contains("/rules/") && !pointer_path.ends_with("/value") {
1460 match pointer_value.as_object_mut() {
1461 Some(pointer_obj) => {
1462 pointer_obj.remove("$evaluation");
1463 pointer_obj.insert("value".to_string(), cleaned_val);
1464 },
1465 None => continue,
1466 }
1467 } else {
1468 *pointer_value = cleaned_val;
1469 }
1470 }
1471 }
1472 }
1473 }
1474 }
1475 });
1476 });
1477 }
1478
1479 fn evaluate_options_templates(&mut self) {
1481 let templates_to_eval = self.options_templates.clone();
1483
1484 for (path, template_str, params_path) in templates_to_eval.iter() {
1486 if let Some(params) = self.evaluated_schema.pointer(¶ms_path) {
1487 if let Ok(evaluated) = self.evaluate_template(&template_str, params) {
1488 if let Some(target) = self.evaluated_schema.pointer_mut(&path) {
1489 *target = Value::String(evaluated);
1490 }
1491 }
1492 }
1493 }
1494 }
1495
1496 fn evaluate_template(&self, template: &str, params: &Value) -> Result<String, String> {
1498 let mut result = template.to_string();
1499
1500 if let Value::Object(params_map) = params {
1502 for (key, value) in params_map {
1503 let placeholder = format!("{{{}}}", key);
1504 if let Some(str_val) = value.as_str() {
1505 result = result.replace(&placeholder, str_val);
1506 } else {
1507 result = result.replace(&placeholder, &value.to_string());
1509 }
1510 }
1511 }
1512
1513 Ok(result)
1514 }
1515
1516 pub fn compile_logic(&self, logic_str: &str) -> Result<CompiledLogicId, String> {
1532 rlogic::compiled_logic_store::compile_logic(logic_str)
1533 }
1534
1535 pub fn compile_logic_value(&self, logic: &Value) -> Result<CompiledLogicId, String> {
1552 rlogic::compiled_logic_store::compile_logic_value(logic)
1553 }
1554
1555 pub fn run_logic(&mut self, logic_id: CompiledLogicId, data: Option<&Value>, context: Option<&Value>) -> Result<Value, String> {
1572 let compiled_logic = rlogic::compiled_logic_store::get_compiled_logic(logic_id)
1574 .ok_or_else(|| format!("Compiled logic ID {:?} not found in store", logic_id))?;
1575
1576 let eval_data_value = if let Some(input_data) = data {
1580 let context_value = context.unwrap_or(&self.context);
1581
1582 self.eval_data.replace_data_and_context(input_data.clone(), context_value.clone());
1583 self.eval_data.data()
1584 } else {
1585 self.eval_data.data()
1586 };
1587
1588 let evaluator = Evaluator::new();
1590 let result = evaluator.evaluate(&compiled_logic, &eval_data_value)?;
1591
1592 Ok(clean_float_noise(result))
1593 }
1594
1595 pub fn compile_and_run_logic(&mut self, logic_str: &str, data: Option<&str>, context: Option<&str>) -> Result<Value, String> {
1611 let compiled_logic = self.compile_logic(logic_str)?;
1613
1614 let data_value = if let Some(data_str) = data {
1616 Some(json_parser::parse_json_str(data_str)?)
1617 } else {
1618 None
1619 };
1620
1621 let context_value = if let Some(ctx_str) = context {
1622 Some(json_parser::parse_json_str(ctx_str)?)
1623 } else {
1624 None
1625 };
1626
1627 self.run_logic(compiled_logic, data_value.as_ref(), context_value.as_ref())
1629 }
1630
1631 pub fn resolve_layout(&mut self, evaluate: bool) -> Result<(), String> {
1641 if evaluate {
1642 let data_str = serde_json::to_string(&self.data)
1644 .map_err(|e| format!("Failed to serialize data: {}", e))?;
1645 self.evaluate(&data_str, None)?;
1646 }
1647
1648 self.resolve_layout_internal();
1649 Ok(())
1650 }
1651
1652 fn resolve_layout_internal(&mut self) {
1653 time_block!(" resolve_layout_internal()", {
1654 let layout_paths = self.layout_paths.clone();
1657
1658 time_block!(" resolve_layout_elements", {
1659 for layout_path in layout_paths.iter() {
1660 self.resolve_layout_elements(layout_path);
1661 }
1662 });
1663
1664 time_block!(" propagate_parent_conditions", {
1666 for layout_path in layout_paths.iter() {
1667 self.propagate_parent_conditions(layout_path);
1668 }
1669 });
1670 });
1671 }
1672
1673 fn propagate_parent_conditions(&mut self, layout_elements_path: &str) {
1675 let normalized_path = path_utils::normalize_to_json_pointer(layout_elements_path);
1677
1678 let elements = if let Some(Value::Array(arr)) = self.evaluated_schema.pointer_mut(&normalized_path) {
1680 mem::take(arr)
1681 } else {
1682 return;
1683 };
1684
1685 let mut updated_elements = Vec::with_capacity(elements.len());
1687 for element in elements {
1688 updated_elements.push(self.apply_parent_conditions(element, false, false));
1689 }
1690
1691 if let Some(target) = self.evaluated_schema.pointer_mut(&normalized_path) {
1693 *target = Value::Array(updated_elements);
1694 }
1695 }
1696
1697 fn apply_parent_conditions(&self, element: Value, parent_hidden: bool, parent_disabled: bool) -> Value {
1699 if let Value::Object(mut map) = element {
1700 let mut element_hidden = parent_hidden;
1702 let mut element_disabled = parent_disabled;
1703
1704 if let Some(Value::Object(condition)) = map.get("condition") {
1706 if let Some(Value::Bool(hidden)) = condition.get("hidden") {
1707 element_hidden = element_hidden || *hidden;
1708 }
1709 if let Some(Value::Bool(disabled)) = condition.get("disabled") {
1710 element_disabled = element_disabled || *disabled;
1711 }
1712 }
1713
1714 if let Some(Value::Object(hide_layout)) = map.get("hideLayout") {
1716 if let Some(Value::Bool(all_hidden)) = hide_layout.get("all") {
1718 if *all_hidden {
1719 element_hidden = true;
1720 }
1721 }
1722 }
1723
1724 if parent_hidden || parent_disabled {
1726 if map.contains_key("condition") || map.contains_key("$ref") || map.contains_key("$fullpath") {
1728 let mut condition = if let Some(Value::Object(c)) = map.get("condition") {
1729 c.clone()
1730 } else {
1731 serde_json::Map::new()
1732 };
1733
1734 if parent_hidden {
1735 condition.insert("hidden".to_string(), Value::Bool(true));
1736 }
1737 if parent_disabled {
1738 condition.insert("disabled".to_string(), Value::Bool(true));
1739 }
1740
1741 map.insert("condition".to_string(), Value::Object(condition));
1742 }
1743
1744 if parent_hidden && (map.contains_key("hideLayout") || map.contains_key("type")) {
1746 let mut hide_layout = if let Some(Value::Object(h)) = map.get("hideLayout") {
1747 h.clone()
1748 } else {
1749 serde_json::Map::new()
1750 };
1751
1752 hide_layout.insert("all".to_string(), Value::Bool(true));
1754 map.insert("hideLayout".to_string(), Value::Object(hide_layout));
1755 }
1756 }
1757
1758 if map.contains_key("$parentHide") {
1761 map.insert("$parentHide".to_string(), Value::Bool(parent_hidden));
1762 }
1763
1764 if let Some(Value::Array(elements)) = map.get("elements") {
1766 let mut updated_children = Vec::with_capacity(elements.len());
1767 for child in elements {
1768 updated_children.push(self.apply_parent_conditions(
1769 child.clone(),
1770 element_hidden,
1771 element_disabled,
1772 ));
1773 }
1774 map.insert("elements".to_string(), Value::Array(updated_children));
1775 }
1776
1777 return Value::Object(map);
1778 }
1779
1780 element
1781 }
1782
1783 fn resolve_layout_elements(&mut self, layout_elements_path: &str) {
1785 let normalized_path = path_utils::normalize_to_json_pointer(layout_elements_path);
1787
1788 let elements = if let Some(Value::Array(arr)) = self.schema.pointer(&normalized_path) {
1792 arr.clone()
1793 } else {
1794 return;
1795 };
1796
1797 let parent_path = normalized_path
1799 .trim_start_matches('/')
1800 .replace("/elements", "")
1801 .replace('/', ".");
1802
1803 let mut resolved_elements = Vec::with_capacity(elements.len());
1805 for (index, element) in elements.iter().enumerate() {
1806 let element_path = if parent_path.is_empty() {
1807 format!("elements.{}", index)
1808 } else {
1809 format!("{}.elements.{}", parent_path, index)
1810 };
1811 let resolved = self.resolve_element_ref_recursive(element.clone(), &element_path);
1812 resolved_elements.push(resolved);
1813 }
1814
1815 if let Some(target) = self.evaluated_schema.pointer_mut(&normalized_path) {
1817 *target = Value::Array(resolved_elements);
1818 }
1819 }
1820
1821 fn resolve_element_ref_recursive(&self, element: Value, path_context: &str) -> Value {
1824 let resolved = self.resolve_element_ref(element);
1826
1827 if let Value::Object(mut map) = resolved {
1829 if !map.contains_key("$parentHide") {
1833 map.insert("$parentHide".to_string(), Value::Bool(false));
1834 }
1835
1836 if !map.contains_key("$fullpath") {
1838 map.insert("$fullpath".to_string(), Value::String(path_context.to_string()));
1839 }
1840
1841 if !map.contains_key("$path") {
1842 let last_segment = path_context.split('.').last().unwrap_or(path_context);
1844 map.insert("$path".to_string(), Value::String(last_segment.to_string()));
1845 }
1846
1847 if let Some(Value::Array(elements)) = map.get("elements") {
1849 let mut resolved_nested = Vec::with_capacity(elements.len());
1850 for (index, nested_element) in elements.iter().enumerate() {
1851 let nested_path = format!("{}.elements.{}", path_context, index);
1852 resolved_nested.push(self.resolve_element_ref_recursive(nested_element.clone(), &nested_path));
1853 }
1854 map.insert("elements".to_string(), Value::Array(resolved_nested));
1855 }
1856
1857 return Value::Object(map);
1858 }
1859
1860 resolved
1861 }
1862
1863 fn resolve_element_ref(&self, element: Value) -> Value {
1865 match element {
1866 Value::Object(mut map) => {
1867 if let Some(Value::String(ref_path)) = map.get("$ref").cloned() {
1869 let dotted_path = path_utils::pointer_to_dot_notation(&ref_path);
1871
1872 let last_segment = dotted_path.split('.').last().unwrap_or(&dotted_path);
1874
1875 map.insert("$fullpath".to_string(), Value::String(dotted_path.clone()));
1877 map.insert("$path".to_string(), Value::String(last_segment.to_string()));
1878 map.insert("$parentHide".to_string(), Value::Bool(false));
1879
1880 let normalized_path = if ref_path.starts_with('#') || ref_path.starts_with('/') {
1883 path_utils::normalize_to_json_pointer(&ref_path)
1885 } else {
1886 let schema_pointer = path_utils::dot_notation_to_schema_pointer(&ref_path);
1888 let schema_path = path_utils::normalize_to_json_pointer(&schema_pointer);
1889
1890 if self.evaluated_schema.pointer(&schema_path).is_some() {
1892 schema_path
1893 } else {
1894 let with_properties = format!("/properties/{}", ref_path.replace('.', "/properties/"));
1896 with_properties
1897 }
1898 };
1899
1900 if let Some(referenced_value) = self.evaluated_schema.pointer(&normalized_path) {
1902 let resolved = referenced_value.clone();
1904
1905 if let Value::Object(mut resolved_map) = resolved {
1907 map.remove("$ref");
1909
1910 if let Some(Value::Object(layout_obj)) = resolved_map.remove("$layout") {
1913 let mut result = layout_obj.clone();
1915
1916 resolved_map.remove("properties");
1918
1919 for (key, value) in resolved_map {
1921 if key != "type" || !result.contains_key("type") {
1922 result.insert(key, value);
1923 }
1924 }
1925
1926 for (key, value) in map {
1928 result.insert(key, value);
1929 }
1930
1931 return Value::Object(result);
1932 } else {
1933 for (key, value) in map {
1935 resolved_map.insert(key, value);
1936 }
1937
1938 return Value::Object(resolved_map);
1939 }
1940 } else {
1941 return resolved;
1943 }
1944 }
1945 }
1946
1947 Value::Object(map)
1949 }
1950 _ => element,
1951 }
1952 }
1953
1954 pub fn evaluate_dependents(
1963 &mut self,
1964 changed_paths: &[String],
1965 data: Option<&str>,
1966 context: Option<&str>,
1967 re_evaluate: bool,
1968 ) -> Result<Value, String> {
1969 let _lock = self.eval_lock.lock().unwrap();
1971
1972 if let Some(data_str) = data {
1974 let old_data = self.eval_data.clone_data_without(&["$params"]);
1976
1977 let data_value = json_parser::parse_json_str(data_str)?;
1978 let context_value = if let Some(ctx) = context {
1979 json_parser::parse_json_str(ctx)?
1980 } else {
1981 Value::Object(serde_json::Map::new())
1982 };
1983 self.eval_data.replace_data_and_context(data_value.clone(), context_value);
1984
1985 let data_paths: Vec<String> = changed_paths
1989 .iter()
1990 .map(|path| {
1991 format!("/{}", path.replace('.', "/"))
1993 })
1994 .collect();
1995 self.purge_cache_for_changed_data_with_comparison(&data_paths, &old_data, &data_value);
1996 }
1997
1998 let mut result = Vec::new();
1999 let mut processed = IndexSet::new();
2000
2001 let mut to_process: Vec<(String, bool)> = changed_paths
2004 .iter()
2005 .map(|path| (path_utils::dot_notation_to_schema_pointer(path), false))
2006 .collect(); while let Some((current_path, is_transitive)) = to_process.pop() {
2010 if processed.contains(¤t_path) {
2011 continue;
2012 }
2013 processed.insert(current_path.clone());
2014
2015 let current_data_path = path_utils::normalize_to_json_pointer(¤t_path)
2017 .replace("/properties/", "/")
2018 .trim_start_matches('#')
2019 .to_string();
2020 let mut current_value = self.eval_data.data().pointer(¤t_data_path)
2021 .cloned()
2022 .unwrap_or(Value::Null);
2023
2024 if let Some(dependent_items) = self.dependents_evaluations.get(¤t_path) {
2026 for dep_item in dependent_items {
2027 let ref_path = &dep_item.ref_path;
2028 let pointer_path = path_utils::normalize_to_json_pointer(ref_path);
2029 let data_path = pointer_path.replace("/properties/", "/");
2031
2032 let current_ref_value = self.eval_data.data().pointer(&data_path)
2033 .cloned()
2034 .unwrap_or(Value::Null);
2035
2036 let field = self.evaluated_schema.pointer(&pointer_path).cloned();
2038
2039 let parent_path = if let Some(last_slash) = pointer_path.rfind("/properties") {
2041 &pointer_path[..last_slash]
2042 } else {
2043 "/"
2044 };
2045 let mut parent_field = if parent_path.is_empty() || parent_path == "/" {
2046 self.evaluated_schema.clone()
2047 } else {
2048 self.evaluated_schema.pointer(parent_path).cloned()
2049 .unwrap_or_else(|| Value::Object(serde_json::Map::new()))
2050 };
2051
2052 if let Value::Object(ref mut map) = parent_field {
2054 map.remove("properties");
2055 map.remove("$layout");
2056 }
2057
2058 let mut change_obj = serde_json::Map::new();
2059 change_obj.insert("$ref".to_string(), Value::String(path_utils::pointer_to_dot_notation(&data_path)));
2060 if let Some(f) = field {
2061 change_obj.insert("$field".to_string(), f);
2062 }
2063 change_obj.insert("$parentField".to_string(), parent_field);
2064 change_obj.insert("transitive".to_string(), Value::Bool(is_transitive));
2065
2066 let mut add_transitive = false;
2067 let mut add_deps = false;
2068 if let Some(clear_val) = &dep_item.clear {
2070 let clear_val_clone = clear_val.clone();
2071 let should_clear = Self::evaluate_dependent_value_static(&self.engine, &self.evaluations, &self.eval_data, &clear_val_clone, ¤t_value, ¤t_ref_value)?;
2072 let clear_bool = match should_clear {
2073 Value::Bool(b) => b,
2074 _ => false,
2075 };
2076
2077 if clear_bool {
2078 if data_path == current_data_path {
2080 current_value = Value::Null;
2081 }
2082 self.eval_data.set(&data_path, Value::Null);
2083 change_obj.insert("clear".to_string(), Value::Bool(true));
2084 add_transitive = true;
2085 add_deps = true;
2086 }
2087 }
2088
2089 if let Some(value_val) = &dep_item.value {
2091 let value_val_clone = value_val.clone();
2092 let computed_value = Self::evaluate_dependent_value_static(&self.engine, &self.evaluations, &self.eval_data, &value_val_clone, ¤t_value, ¤t_ref_value)?;
2093 let cleaned_val = clean_float_noise(computed_value.clone());
2094
2095 if cleaned_val != current_ref_value && cleaned_val != Value::Null {
2096 if data_path == current_data_path {
2098 current_value = cleaned_val.clone();
2099 }
2100 self.eval_data.set(&data_path, cleaned_val.clone());
2101 change_obj.insert("value".to_string(), cleaned_val);
2102 add_transitive = true;
2103 add_deps = true;
2104 }
2105 }
2106
2107 if add_deps {
2109 result.push(Value::Object(change_obj));
2110 }
2111
2112 if add_transitive {
2114 to_process.push((ref_path.clone(), true));
2115 }
2116 }
2117 }
2118 }
2119
2120 if re_evaluate {
2124 drop(_lock); self.evaluate_internal()?;
2126 }
2127
2128 Ok(Value::Array(result))
2129 }
2130
2131 fn evaluate_dependent_value_static(
2133 engine: &RLogic,
2134 evaluations: &IndexMap<String, LogicId>,
2135 eval_data: &EvalData,
2136 value: &Value,
2137 changed_field_value: &Value,
2138 changed_field_ref_value: &Value
2139 ) -> Result<Value, String> {
2140 match value {
2141 Value::String(eval_key) => {
2143 if let Some(logic_id) = evaluations.get(eval_key) {
2144 let mut internal_context = serde_json::Map::new();
2147 internal_context.insert("$value".to_string(), changed_field_value.clone());
2148 internal_context.insert("$refValue".to_string(), changed_field_ref_value.clone());
2149 let context_value = Value::Object(internal_context);
2150
2151 let result = engine.run_with_context(logic_id, eval_data.data(), &context_value)
2152 .map_err(|e| format!("Failed to evaluate dependent logic '{}': {}", eval_key, e))?;
2153 Ok(result)
2154 } else {
2155 Ok(value.clone())
2157 }
2158 }
2159 Value::Object(map) if map.contains_key("$evaluation") => {
2162 Err("Dependent evaluation contains unparsed $evaluation - schema was not properly parsed".to_string())
2163 }
2164 _ => Ok(value.clone()),
2166 }
2167 }
2168
2169 pub fn validate(
2172 &mut self,
2173 data: &str,
2174 context: Option<&str>,
2175 paths: Option<&[String]>
2176 ) -> Result<ValidationResult, String> {
2177 let _lock = self.eval_lock.lock().unwrap();
2179
2180 let old_data = self.eval_data.clone_data_without(&["$params"]);
2182
2183 let data_value = json_parser::parse_json_str(data)?;
2185 let context_value = if let Some(ctx) = context {
2186 json_parser::parse_json_str(ctx)?
2187 } else {
2188 Value::Object(serde_json::Map::new())
2189 };
2190
2191 self.eval_data.replace_data_and_context(data_value.clone(), context_value);
2193
2194 let changed_data_paths: Vec<String> = if let Some(obj) = data_value.as_object() {
2197 obj.keys().map(|k| format!("/{}", k)).collect()
2198 } else {
2199 Vec::new()
2200 };
2201 self.purge_cache_for_changed_data_with_comparison(&changed_data_paths, &old_data, &data_value);
2202
2203 drop(_lock);
2205
2206 self.evaluate_others();
2209
2210 self.evaluated_schema = self.get_evaluated_schema(false);
2212
2213 let mut errors: IndexMap<String, ValidationError> = IndexMap::new();
2214
2215 for field_path in self.fields_with_rules.iter() {
2218 if let Some(filter_paths) = paths {
2220 if !filter_paths.is_empty() && !filter_paths.iter().any(|p| field_path.starts_with(p.as_str()) || p.starts_with(field_path.as_str())) {
2221 continue;
2222 }
2223 }
2224
2225 self.validate_field(field_path, &data_value, &mut errors);
2226 }
2227
2228 let has_error = !errors.is_empty();
2229
2230 Ok(ValidationResult {
2231 has_error,
2232 errors,
2233 })
2234 }
2235
2236 fn validate_field(
2238 &self,
2239 field_path: &str,
2240 data: &Value,
2241 errors: &mut IndexMap<String, ValidationError>
2242 ) {
2243 if errors.contains_key(field_path) {
2245 return;
2246 }
2247
2248 let schema_path = path_utils::dot_notation_to_schema_pointer(field_path);
2250
2251 let pointer_path = schema_path.trim_start_matches('#');
2253
2254 let field_schema = match self.evaluated_schema.pointer(pointer_path) {
2256 Some(s) => s,
2257 None => {
2258 let alt_path = format!("/properties{}", pointer_path);
2260 match self.evaluated_schema.pointer(&alt_path) {
2261 Some(s) => s,
2262 None => return,
2263 }
2264 }
2265 };
2266
2267 if let Value::Object(schema_map) = field_schema {
2269 if let Some(Value::Object(condition)) = schema_map.get("condition") {
2270 if let Some(Value::Bool(true)) = condition.get("hidden") {
2271 return;
2272 }
2273 }
2274
2275 let rules = match schema_map.get("rules") {
2277 Some(Value::Object(r)) => r,
2278 _ => return,
2279 };
2280
2281 let field_data = self.get_field_data(field_path, data);
2283
2284 for (rule_name, rule_value) in rules {
2286 self.validate_rule(
2287 field_path,
2288 rule_name,
2289 rule_value,
2290 &field_data,
2291 schema_map,
2292 field_schema,
2293 errors
2294 );
2295 }
2296 }
2297 }
2298
2299 fn get_field_data(&self, field_path: &str, data: &Value) -> Value {
2301 let parts: Vec<&str> = field_path.split('.').collect();
2302 let mut current = data;
2303
2304 for part in parts {
2305 match current {
2306 Value::Object(map) => {
2307 current = map.get(part).unwrap_or(&Value::Null);
2308 }
2309 _ => return Value::Null,
2310 }
2311 }
2312
2313 current.clone()
2314 }
2315
2316 fn validate_rule(
2318 &self,
2319 field_path: &str,
2320 rule_name: &str,
2321 rule_value: &Value,
2322 field_data: &Value,
2323 schema_map: &serde_json::Map<String, Value>,
2324 _schema: &Value,
2325 errors: &mut IndexMap<String, ValidationError>
2326 ) {
2327 if errors.contains_key(field_path) {
2329 return;
2330 }
2331
2332 if let Some(Value::Object(condition)) = schema_map.get("condition") {
2334 if let Some(Value::Bool(true)) = condition.get("disabled") {
2335 return;
2336 }
2337 }
2338
2339 let schema_path = path_utils::dot_notation_to_schema_pointer(field_path);
2342 let rule_path = format!("{}/rules/{}", schema_path.trim_start_matches('#'), rule_name);
2343
2344 let evaluated_rule = if let Some(eval_rule) = self.evaluated_schema.pointer(&rule_path) {
2346 eval_rule.clone()
2347 } else {
2348 rule_value.clone()
2349 };
2350
2351 let (rule_active, rule_message, rule_code, rule_data) = match &evaluated_rule {
2353 Value::Object(rule_obj) => {
2354 let active = rule_obj.get("value").unwrap_or(&Value::Bool(false));
2355
2356 let message = match rule_obj.get("message") {
2358 Some(Value::String(s)) => s.clone(),
2359 Some(Value::Object(msg_obj)) if msg_obj.contains_key("value") => {
2360 msg_obj.get("value")
2361 .and_then(|v| v.as_str())
2362 .unwrap_or("Validation failed")
2363 .to_string()
2364 }
2365 Some(msg_val) => msg_val.as_str().unwrap_or("Validation failed").to_string(),
2366 None => "Validation failed".to_string()
2367 };
2368
2369 let code = rule_obj.get("code")
2370 .and_then(|c| c.as_str())
2371 .map(|s| s.to_string());
2372
2373 let data = rule_obj.get("data").map(|d| {
2375 if let Value::Object(data_obj) = d {
2376 let mut cleaned_data = serde_json::Map::new();
2377 for (key, value) in data_obj {
2378 if let Value::Object(val_obj) = value {
2380 if val_obj.len() == 1 && val_obj.contains_key("value") {
2381 cleaned_data.insert(key.clone(), val_obj["value"].clone());
2382 } else {
2383 cleaned_data.insert(key.clone(), value.clone());
2384 }
2385 } else {
2386 cleaned_data.insert(key.clone(), value.clone());
2387 }
2388 }
2389 Value::Object(cleaned_data)
2390 } else {
2391 d.clone()
2392 }
2393 });
2394
2395 (active.clone(), message, code, data)
2396 }
2397 _ => (evaluated_rule.clone(), "Validation failed".to_string(), None, None)
2398 };
2399
2400 let error_code = rule_code.or_else(|| Some(format!("{}.{}", field_path, rule_name)));
2402
2403 let is_empty = matches!(field_data, Value::Null) ||
2404 (field_data.is_string() && field_data.as_str().unwrap_or("").is_empty()) ||
2405 (field_data.is_array() && field_data.as_array().unwrap().is_empty());
2406
2407 match rule_name {
2408 "required" => {
2409 if let Value::Bool(true) = rule_active {
2410 if is_empty {
2411 errors.insert(field_path.to_string(), ValidationError {
2412 rule_type: "required".to_string(),
2413 message: rule_message,
2414 code: error_code.clone(),
2415 pattern: None,
2416 field_value: None,
2417 data: None,
2418 });
2419 }
2420 }
2421 }
2422 "minLength" => {
2423 if !is_empty {
2424 if let Some(min) = rule_active.as_u64() {
2425 let len = match field_data {
2426 Value::String(s) => s.len(),
2427 Value::Array(a) => a.len(),
2428 _ => 0
2429 };
2430 if len < min as usize {
2431 errors.insert(field_path.to_string(), ValidationError {
2432 rule_type: "minLength".to_string(),
2433 message: rule_message,
2434 code: error_code.clone(),
2435 pattern: None,
2436 field_value: None,
2437 data: None,
2438 });
2439 }
2440 }
2441 }
2442 }
2443 "maxLength" => {
2444 if !is_empty {
2445 if let Some(max) = rule_active.as_u64() {
2446 let len = match field_data {
2447 Value::String(s) => s.len(),
2448 Value::Array(a) => a.len(),
2449 _ => 0
2450 };
2451 if len > max as usize {
2452 errors.insert(field_path.to_string(), ValidationError {
2453 rule_type: "maxLength".to_string(),
2454 message: rule_message,
2455 code: error_code.clone(),
2456 pattern: None,
2457 field_value: None,
2458 data: None,
2459 });
2460 }
2461 }
2462 }
2463 }
2464 "minValue" => {
2465 if !is_empty {
2466 if let Some(min) = rule_active.as_f64() {
2467 if let Some(val) = field_data.as_f64() {
2468 if val < min {
2469 errors.insert(field_path.to_string(), ValidationError {
2470 rule_type: "minValue".to_string(),
2471 message: rule_message,
2472 code: error_code.clone(),
2473 pattern: None,
2474 field_value: None,
2475 data: None,
2476 });
2477 }
2478 }
2479 }
2480 }
2481 }
2482 "maxValue" => {
2483 if !is_empty {
2484 if let Some(max) = rule_active.as_f64() {
2485 if let Some(val) = field_data.as_f64() {
2486 if val > max {
2487 errors.insert(field_path.to_string(), ValidationError {
2488 rule_type: "maxValue".to_string(),
2489 message: rule_message,
2490 code: error_code.clone(),
2491 pattern: None,
2492 field_value: None,
2493 data: None,
2494 });
2495 }
2496 }
2497 }
2498 }
2499 }
2500 "pattern" => {
2501 if !is_empty {
2502 if let Some(pattern) = rule_active.as_str() {
2503 if let Some(text) = field_data.as_str() {
2504 if let Ok(regex) = regex::Regex::new(pattern) {
2505 if !regex.is_match(text) {
2506 errors.insert(field_path.to_string(), ValidationError {
2507 rule_type: "pattern".to_string(),
2508 message: rule_message,
2509 code: error_code.clone(),
2510 pattern: Some(pattern.to_string()),
2511 field_value: Some(text.to_string()),
2512 data: None,
2513 });
2514 }
2515 }
2516 }
2517 }
2518 }
2519 }
2520 "evaluation" => {
2521 if let Value::Array(eval_array) = &evaluated_rule {
2524 for (idx, eval_item) in eval_array.iter().enumerate() {
2525 if let Value::Object(eval_obj) = eval_item {
2526 let eval_result = eval_obj.get("value").unwrap_or(&Value::Bool(true));
2528
2529 let is_falsy = match eval_result {
2531 Value::Bool(false) => true,
2532 Value::Null => true,
2533 Value::Number(n) => n.as_f64() == Some(0.0),
2534 Value::String(s) => s.is_empty(),
2535 Value::Array(a) => a.is_empty(),
2536 _ => false,
2537 };
2538
2539 if is_falsy {
2540 let eval_code = eval_obj.get("code")
2541 .and_then(|c| c.as_str())
2542 .map(|s| s.to_string())
2543 .or_else(|| Some(format!("{}.evaluation.{}", field_path, idx)));
2544
2545 let eval_message = eval_obj.get("message")
2546 .and_then(|m| m.as_str())
2547 .unwrap_or("Validation failed")
2548 .to_string();
2549
2550 let eval_data = eval_obj.get("data").cloned();
2551
2552 errors.insert(field_path.to_string(), ValidationError {
2553 rule_type: "evaluation".to_string(),
2554 message: eval_message,
2555 code: eval_code,
2556 pattern: None,
2557 field_value: None,
2558 data: eval_data,
2559 });
2560
2561 break;
2563 }
2564 }
2565 }
2566 }
2567 }
2568 _ => {
2569 if !is_empty {
2573 let is_falsy = match &rule_active {
2575 Value::Bool(false) => true,
2576 Value::Null => true,
2577 Value::Number(n) => n.as_f64() == Some(0.0),
2578 Value::String(s) => s.is_empty(),
2579 Value::Array(a) => a.is_empty(),
2580 _ => false,
2581 };
2582
2583 if is_falsy {
2584 errors.insert(field_path.to_string(), ValidationError {
2585 rule_type: "evaluation".to_string(),
2586 message: rule_message,
2587 code: error_code.clone(),
2588 pattern: None,
2589 field_value: None,
2590 data: rule_data,
2591 });
2592 }
2593 }
2594 }
2595 }
2596 }
2597}
2598
2599#[derive(Debug, Clone, Serialize, Deserialize)]
2601pub struct ValidationError {
2602 #[serde(rename = "type")]
2603 pub rule_type: String,
2604 pub message: String,
2605 #[serde(skip_serializing_if = "Option::is_none")]
2606 pub code: Option<String>,
2607 #[serde(skip_serializing_if = "Option::is_none")]
2608 pub pattern: Option<String>,
2609 #[serde(skip_serializing_if = "Option::is_none")]
2610 pub field_value: Option<String>,
2611 #[serde(skip_serializing_if = "Option::is_none")]
2612 pub data: Option<Value>,
2613}
2614
2615#[derive(Debug, Clone, Serialize, Deserialize)]
2617pub struct ValidationResult {
2618 pub has_error: bool,
2619 pub errors: IndexMap<String, ValidationError>,
2620}
2621