Skip to main content

json_eval_rs/jsoneval/
core.rs

1use super::JSONEval;
2use crate::jsoneval::eval_data::EvalData;
3use crate::jsoneval::json_parser;
4use crate::jsoneval::parsed_schema::ParsedSchema;
5use crate::jsoneval::parsed_schema_cache::PARSED_SCHEMA_CACHE;
6use crate::parse_schema;
7use crate::rlogic::{RLogic, RLogicConfig};
8
9use crate::time_block;
10
11use indexmap::IndexMap;
12use serde::de::Error as _;
13use serde_json::Value;
14use std::collections::HashMap;
15use std::sync::{Arc, Mutex, RwLock};
16
17impl Clone for JSONEval {
18    fn clone(&self) -> Self {
19        Self {
20            schema: Arc::clone(&self.schema),
21            engine: Arc::clone(&self.engine),
22            evaluations: self.evaluations.clone(),
23            tables: self.tables.clone(),
24            table_metadata: self.table_metadata.clone(),
25            dependencies: self.dependencies.clone(),
26            sorted_evaluations: self.sorted_evaluations.clone(),
27            dependents_evaluations: self.dependents_evaluations.clone(),
28            rules_evaluations: self.rules_evaluations.clone(),
29            fields_with_rules: self.fields_with_rules.clone(),
30            others_evaluations: self.others_evaluations.clone(),
31            value_evaluations: self.value_evaluations.clone(),
32            layout_paths: self.layout_paths.clone(),
33            root_layout_paths: self.root_layout_paths.clone(),
34            layout_field_refs: self.layout_field_refs.clone(),
35            options_templates: self.options_templates.clone(),
36            subforms: self.subforms.clone(),
37            reffed_by: self.reffed_by.clone(),
38            dep_formula_triggers: self.dep_formula_triggers.clone(),
39            context: self.context.clone(),
40            data: self.data.clone(),
41            evaluated_schema: self.evaluated_schema.clone(),
42            eval_data: self.eval_data.clone(),
43            eval_cache: self.eval_cache.clone(),
44            eval_lock: Mutex::new(()), // Create fresh mutex for the clone
45            cached_msgpack_schema: self.cached_msgpack_schema.clone(),
46            conditional_hidden_fields: self.conditional_hidden_fields.clone(),
47            conditional_readonly_fields: self.conditional_readonly_fields.clone(),
48            static_arrays: self.static_arrays.clone(),
49            regex_cache: RwLock::new(HashMap::new()),
50            layout_state: RwLock::new(crate::jsoneval::layout::LayoutResolutionState::default()),
51            validation_cache: RwLock::new(self.validation_cache.read().unwrap().clone()),
52        }
53    }
54}
55
56impl JSONEval {
57    pub fn new(
58        schema: &str,
59        context: Option<&str>,
60        data: Option<&str>,
61    ) -> Result<Self, serde_json::Error> {
62        time_block!("JSONEval::new() [total]", {
63            // Use serde_json for schema (needs arbitrary_precision) and SIMD for data (needs speed)
64            let mut schema_val: Value =
65                time_block!("  parse schema JSON", { serde_json::from_str(schema)? });
66            let context: Value = time_block!("  parse context JSON", {
67                json_parser::parse_json_str(context.unwrap_or("{}"))
68                    .map_err(serde_json::Error::custom)?
69            });
70            let data: Value = time_block!("  parse data JSON", {
71                json_parser::parse_json_str(data.unwrap_or("{}"))
72                    .map_err(serde_json::Error::custom)?
73            });
74
75            // Extract large static arrays
76            let static_arrays = if let Some(params) = schema_val
77                .get_mut("$params")
78                .and_then(|v| v.as_object_mut())
79            {
80                crate::jsoneval::static_arrays::extract_from_params(params)
81            } else {
82                IndexMap::new()
83            };
84            let static_arrays = Arc::new(static_arrays);
85            let evaluated_schema = schema_val.clone();
86
87            // Use default config: tracking enabled
88            let engine_config = RLogicConfig::default();
89            let engine = RLogic::with_config(engine_config);
90            engine.set_static_arrays(Arc::clone(&static_arrays));
91
92            let mut instance = time_block!("  create instance struct", {
93                Self {
94                    schema: Arc::new(schema_val),
95                    evaluations: Arc::new(IndexMap::new()),
96                    tables: Arc::new(IndexMap::new()),
97                    table_metadata: Arc::new(IndexMap::new()),
98                    dependencies: Arc::new(IndexMap::new()),
99                    sorted_evaluations: Arc::new(Vec::new()),
100                    dependents_evaluations: Arc::new(IndexMap::new()),
101                    rules_evaluations: Arc::new(Vec::new()),
102                    fields_with_rules: Arc::new(Vec::new()),
103                    others_evaluations: Arc::new(Vec::new()),
104                    value_evaluations: Arc::new(Vec::new()),
105                    layout_paths: Arc::new(Vec::new()),
106                    root_layout_paths: Arc::new(Vec::new()),
107                    layout_field_refs: Arc::new(indexmap::IndexSet::new()),
108                    options_templates: Arc::new(Vec::new()),
109                    subforms: IndexMap::new(),
110                    engine: Arc::new(engine),
111                    reffed_by: Arc::new(IndexMap::new()),
112                    dep_formula_triggers: Arc::new(IndexMap::new()),
113                    context: context.clone(),
114                    data: data.clone(),
115                    evaluated_schema: evaluated_schema.clone(),
116                    eval_data: EvalData::with_schema_data_context(
117                        &evaluated_schema,
118                        &data,
119                        &context,
120                    ),
121                    eval_cache: crate::jsoneval::eval_cache::EvalCache::new(),
122                    eval_lock: Mutex::new(()),
123                    cached_msgpack_schema: None,
124                    conditional_hidden_fields: Arc::new(Vec::new()),
125                    conditional_readonly_fields: Arc::new(Vec::new()),
126                    static_arrays,
127                    regex_cache: RwLock::new(HashMap::new()),
128                    layout_state: RwLock::new(
129                        crate::jsoneval::layout::LayoutResolutionState::default(),
130                    ),
131                    validation_cache: RwLock::new(
132                        crate::jsoneval::validation_cache::ValidationCache::default(),
133                    ),
134                }
135            });
136            time_block!("  parse_schema", {
137                parse_schema::legacy::parse_schema(&mut instance)
138                    .map_err(serde_json::Error::custom)?
139            });
140            Ok(instance)
141        })
142    }
143
144    /// Create a new JSONEval instance for a subform, avoiding string serialization
145    pub(crate) fn new_subform(
146        schema_val: Value,
147        context: Value,
148        static_arrays: Arc<IndexMap<String, Arc<Value>>>,
149    ) -> Result<Self, serde_json::Error> {
150        time_block!("JSONEval::new_subform() [total]", {
151            // Data is empty for a subform initially
152            let data = Value::Object(serde_json::Map::new());
153            let evaluated_schema = schema_val.clone();
154
155            // Use default config: tracking enabled
156            let engine_config = RLogicConfig::default();
157            let engine = RLogic::with_config(engine_config);
158            engine.set_static_arrays(Arc::clone(&static_arrays));
159
160            let mut instance = time_block!("  create instance struct", {
161                Self {
162                    schema: Arc::new(schema_val),
163                    evaluations: Arc::new(IndexMap::new()),
164                    tables: Arc::new(IndexMap::new()),
165                    table_metadata: Arc::new(IndexMap::new()),
166                    dependencies: Arc::new(IndexMap::new()),
167                    sorted_evaluations: Arc::new(Vec::new()),
168                    dependents_evaluations: Arc::new(IndexMap::new()),
169                    rules_evaluations: Arc::new(Vec::new()),
170                    fields_with_rules: Arc::new(Vec::new()),
171                    others_evaluations: Arc::new(Vec::new()),
172                    value_evaluations: Arc::new(Vec::new()),
173                    layout_paths: Arc::new(Vec::new()),
174                    root_layout_paths: Arc::new(Vec::new()),
175                    layout_field_refs: Arc::new(indexmap::IndexSet::new()),
176                    options_templates: Arc::new(Vec::new()),
177                    subforms: IndexMap::new(),
178                    engine: Arc::new(engine),
179                    reffed_by: Arc::new(IndexMap::new()),
180                    dep_formula_triggers: Arc::new(IndexMap::new()),
181                    context: context.clone(),
182                    data: data.clone(),
183                    evaluated_schema: evaluated_schema.clone(),
184                    eval_data: EvalData::with_schema_data_context(
185                        &evaluated_schema,
186                        &data,
187                        &context,
188                    ),
189                    eval_cache: crate::jsoneval::eval_cache::EvalCache::new(),
190                    eval_lock: Mutex::new(()),
191                    cached_msgpack_schema: None,
192                    conditional_hidden_fields: Arc::new(Vec::new()),
193                    conditional_readonly_fields: Arc::new(Vec::new()),
194                    static_arrays,
195                    regex_cache: RwLock::new(HashMap::new()),
196                    layout_state: RwLock::new(
197                        crate::jsoneval::layout::LayoutResolutionState::default(),
198                    ),
199                    validation_cache: RwLock::new(
200                        crate::jsoneval::validation_cache::ValidationCache::default(),
201                    ),
202                }
203            });
204            time_block!("  parse_schema", {
205                parse_schema::legacy::parse_schema(&mut instance)
206                    .map_err(serde_json::Error::custom)?
207            });
208            Ok(instance)
209        })
210    }
211
212    /// Create a new JSONEval instance from MessagePack-encoded schema
213    ///
214    /// # Arguments
215    ///
216    /// * `schema_msgpack` - MessagePack-encoded schema bytes
217    /// * `context` - Optional JSON context string
218    /// * `data` - Optional JSON data string
219    ///
220    /// # Returns
221    ///
222    /// A Result containing the JSONEval instance or an error
223    pub fn new_from_msgpack(
224        schema_msgpack: &[u8],
225        context: Option<&str>,
226        data: Option<&str>,
227    ) -> Result<Self, String> {
228        // Store original MessagePack bytes for zero-copy retrieval
229        let cached_msgpack = schema_msgpack.to_vec();
230
231        // Deserialize MessagePack schema to Value
232        let mut schema_val: Value = rmp_serde::from_slice(schema_msgpack)
233            .map_err(|e| format!("Failed to deserialize MessagePack schema: {}", e))?;
234
235        let context: Value = json_parser::parse_json_str(context.unwrap_or("{}"))
236            .map_err(|e| format!("Failed to parse context: {}", e))?;
237        let data: Value = json_parser::parse_json_str(data.unwrap_or("{}"))
238            .map_err(|e| format!("Failed to parse data: {}", e))?;
239
240        // Extract large static arrays
241        let static_arrays = if let Some(params) = schema_val
242            .get_mut("$params")
243            .and_then(|v| v.as_object_mut())
244        {
245            crate::jsoneval::static_arrays::extract_from_params(params)
246        } else {
247            IndexMap::new()
248        };
249        let static_arrays = Arc::new(static_arrays);
250        let evaluated_schema = schema_val.clone();
251
252        let engine_config = RLogicConfig::default();
253        let engine = RLogic::with_config(engine_config);
254        engine.set_static_arrays(Arc::clone(&static_arrays));
255
256        let mut instance = Self {
257            schema: Arc::new(schema_val),
258            evaluations: Arc::new(IndexMap::new()),
259            tables: Arc::new(IndexMap::new()),
260            table_metadata: Arc::new(IndexMap::new()),
261            dependencies: Arc::new(IndexMap::new()),
262            sorted_evaluations: Arc::new(Vec::new()),
263            dependents_evaluations: Arc::new(IndexMap::new()),
264            rules_evaluations: Arc::new(Vec::new()),
265            fields_with_rules: Arc::new(Vec::new()),
266            others_evaluations: Arc::new(Vec::new()),
267            value_evaluations: Arc::new(Vec::new()),
268            layout_paths: Arc::new(Vec::new()),
269            root_layout_paths: Arc::new(Vec::new()),
270            layout_field_refs: Arc::new(indexmap::IndexSet::new()),
271            options_templates: Arc::new(Vec::new()),
272            subforms: IndexMap::new(),
273            engine: Arc::new(engine),
274            reffed_by: Arc::new(IndexMap::new()),
275            dep_formula_triggers: Arc::new(IndexMap::new()),
276            context: context.clone(),
277            data: data.clone(),
278            evaluated_schema: evaluated_schema.clone(),
279            eval_data: EvalData::with_schema_data_context(&evaluated_schema, &data, &context),
280            eval_cache: crate::jsoneval::eval_cache::EvalCache::new(),
281            eval_lock: Mutex::new(()),
282            cached_msgpack_schema: Some(cached_msgpack),
283            conditional_hidden_fields: Arc::new(Vec::new()),
284            conditional_readonly_fields: Arc::new(Vec::new()),
285            static_arrays,
286            regex_cache: RwLock::new(HashMap::new()),
287            layout_state: RwLock::new(crate::jsoneval::layout::LayoutResolutionState::default()),
288            validation_cache: RwLock::new(
289                crate::jsoneval::validation_cache::ValidationCache::default(),
290            ),
291        };
292        parse_schema::legacy::parse_schema(&mut instance)?;
293        Ok(instance)
294    }
295
296    /// Create a new JSONEval instance from a pre-parsed ParsedSchema
297    ///
298    /// This enables schema caching: parse once, reuse across multiple evaluations with different data/context.
299    ///
300    /// # Arguments
301    ///
302    /// * `parsed` - Arc-wrapped pre-parsed schema (can be cloned and cached)
303    /// * `context` - Optional JSON context string
304    /// * `data` - Optional JSON data string
305    ///
306    /// # Returns
307    ///
308    /// A Result containing the JSONEval instance or an error
309    ///
310    /// # Example
311    ///
312    /// ```ignore
313    /// use std::sync::Arc;
314    ///
315    /// // Parse schema once and wrap in Arc for caching
316    /// let parsed = Arc::new(ParsedSchema::parse(schema_str)?);
317    /// cache.insert(schema_key, parsed.clone());
318    ///
319    /// // Reuse across multiple evaluations (Arc::clone is cheap)
320    /// let eval1 = JSONEval::with_parsed_schema(parsed.clone(), Some(context1), Some(data1))?;
321    /// let eval2 = JSONEval::with_parsed_schema(parsed.clone(), Some(context2), Some(data2))?;
322    /// ```
323    pub fn with_parsed_schema(
324        parsed: Arc<ParsedSchema>,
325        context: Option<&str>,
326        data: Option<&str>,
327    ) -> Result<Self, String> {
328        let context: Value = json_parser::parse_json_str(context.unwrap_or("{}"))
329            .map_err(|e| format!("Failed to parse context: {}", e))?;
330        let data: Value = json_parser::parse_json_str(data.unwrap_or("{}"))
331            .map_err(|e| format!("Failed to parse data: {}", e))?;
332
333        let evaluated_schema = parsed.schema.clone();
334
335        // Share the engine Arc (cheap pointer clone, not data clone)
336        // Multiple JSONEval instances created from the same ParsedSchema will share the compiled RLogic
337        let engine = parsed.engine.clone();
338
339        // Convert Arc<ParsedSchema> subforms to Box<JSONEval> subforms
340        // This is a one-time conversion when creating JSONEval from ParsedSchema
341        let mut subforms = IndexMap::new();
342        for (path, subform_parsed) in &parsed.subforms {
343            // Create JSONEval from the cached ParsedSchema
344            let subform_eval =
345                JSONEval::with_parsed_schema(subform_parsed.clone(), Some("{}"), None)?;
346            subforms.insert(path.clone(), Box::new(subform_eval));
347        }
348
349        let instance = Self {
350            schema: Arc::clone(&parsed.schema),
351            evaluations: Arc::clone(&parsed.evaluations),
352            tables: Arc::clone(&parsed.tables),
353            table_metadata: Arc::clone(&parsed.table_metadata),
354            dependencies: Arc::clone(&parsed.dependencies),
355            sorted_evaluations: Arc::clone(&parsed.sorted_evaluations),
356            dependents_evaluations: Arc::clone(&parsed.dependents_evaluations),
357            rules_evaluations: Arc::clone(&parsed.rules_evaluations),
358            fields_with_rules: Arc::clone(&parsed.fields_with_rules),
359            others_evaluations: Arc::clone(&parsed.others_evaluations),
360            value_evaluations: Arc::clone(&parsed.value_evaluations),
361            layout_paths: Arc::clone(&parsed.layout_paths),
362            root_layout_paths: Arc::clone(&parsed.root_layout_paths),
363            layout_field_refs: Arc::clone(&parsed.layout_field_refs),
364            options_templates: Arc::clone(&parsed.options_templates),
365            subforms,
366            engine,
367            reffed_by: Arc::clone(&parsed.reffed_by),
368            dep_formula_triggers: Arc::clone(&parsed.dep_formula_triggers),
369            context: context.clone(),
370            data: data.clone(),
371            evaluated_schema: (*evaluated_schema).clone(),
372            eval_data: EvalData::with_schema_data_context(&evaluated_schema, &data, &context),
373            eval_cache: crate::jsoneval::eval_cache::EvalCache::new(),
374            eval_lock: Mutex::new(()),
375            cached_msgpack_schema: None,
376            conditional_hidden_fields: Arc::clone(&parsed.conditional_hidden_fields),
377            conditional_readonly_fields: Arc::clone(&parsed.conditional_readonly_fields),
378            static_arrays: Arc::clone(&parsed.static_arrays),
379            regex_cache: RwLock::new(HashMap::new()),
380            layout_state: RwLock::new(crate::jsoneval::layout::LayoutResolutionState::default()),
381            validation_cache: RwLock::new(
382                crate::jsoneval::validation_cache::ValidationCache::default(),
383            ),
384        };
385        Ok(instance)
386    }
387
388    pub fn reload_schema(
389        &mut self,
390        schema: &str,
391        context: Option<&str>,
392        data: Option<&str>,
393    ) -> Result<(), String> {
394        // Use serde_json for schema (precision) and SIMD for data (speed)
395        let mut schema_val: Value =
396            serde_json::from_str(schema).map_err(|e| format!("failed to parse schema: {e}"))?;
397        let context: Value = json_parser::parse_json_str(context.unwrap_or("{}"))?;
398        let data: Value = json_parser::parse_json_str(data.unwrap_or("{}"))?;
399        self.context = context.clone();
400        self.data = data.clone();
401
402        let static_arrays = if let Some(params) = schema_val
403            .get_mut("$params")
404            .and_then(|v| v.as_object_mut())
405        {
406            crate::jsoneval::static_arrays::extract_from_params(params)
407        } else {
408            IndexMap::new()
409        };
410        let static_arrays = Arc::new(static_arrays);
411        self.static_arrays = Arc::clone(&static_arrays);
412        self.schema = Arc::new(schema_val);
413        self.evaluated_schema = (*self.schema).clone();
414
415        let engine = RLogic::new();
416        engine.set_static_arrays(static_arrays);
417        self.engine = Arc::new(engine);
418
419        self.evaluations = Arc::new(IndexMap::new());
420        self.tables = Arc::new(IndexMap::new());
421        self.table_metadata = Arc::new(IndexMap::new());
422        self.dependencies = Arc::new(IndexMap::new());
423        self.sorted_evaluations = Arc::new(Vec::new());
424        self.dependents_evaluations = Arc::new(IndexMap::new());
425        self.rules_evaluations = Arc::new(Vec::new());
426        self.fields_with_rules = Arc::new(Vec::new());
427        self.others_evaluations = Arc::new(Vec::new());
428        self.value_evaluations = Arc::new(Vec::new());
429        self.layout_paths = Arc::new(Vec::new());
430        self.root_layout_paths = Arc::new(Vec::new());
431        self.options_templates = Arc::new(Vec::new());
432        self.reffed_by = Arc::new(IndexMap::new());
433        self.dep_formula_triggers = Arc::new(IndexMap::new());
434        self.conditional_hidden_fields = Arc::new(Vec::new());
435        self.conditional_readonly_fields = Arc::new(Vec::new());
436        self.subforms.clear();
437        parse_schema::legacy::parse_schema(self)?;
438
439        // Re-initialize eval_data with new schema, data, and context
440        self.eval_data =
441            EvalData::with_schema_data_context(&self.evaluated_schema, &data, &context);
442        self.eval_cache.clear();
443        if let Ok(mut cache) = self.regex_cache.write() {
444            cache.clear();
445        }
446
447        // Clear MessagePack cache since schema has been mutated
448        self.cached_msgpack_schema = None;
449        self.invalidate_layout_cache();
450        self.invalidate_validation_cache();
451
452        Ok(())
453    }
454
455    /// Set the timezone offset for datetime operations (TODAY, NOW)
456    ///
457    /// This method updates the RLogic engine configuration with a new timezone offset.
458    /// The offset will be applied to all subsequent datetime evaluations.
459    ///
460    /// # Arguments
461    ///
462    /// * `offset_minutes` - Timezone offset in minutes from UTC (e.g., 420 for UTC+7, -300 for UTC-5)
463    ///   Pass `None` to reset to UTC (no offset)
464    ///
465    /// # Example
466    ///
467    /// ```ignore
468    /// let mut eval = JSONEval::new(schema, None, None)?;
469    ///
470    /// // Set to UTC+7 (Jakarta, Bangkok)
471    /// eval.set_timezone_offset(Some(420));
472    ///
473    /// // Reset to UTC
474    /// eval.set_timezone_offset(None);
475    /// ```
476    pub fn set_timezone_offset(&mut self, offset_minutes: Option<i32>) {
477        // Create new config with the timezone offset
478        let mut config = RLogicConfig::default();
479        if let Some(offset) = offset_minutes {
480            config = config.with_timezone_offset(offset);
481        }
482
483        // Recreate the engine with the new configuration
484        // This is necessary because RLogic is wrapped in Arc and config is part of the evaluator
485        let engine = RLogic::with_config(config);
486        engine.set_static_arrays(Arc::clone(&self.static_arrays));
487        self.engine = Arc::new(engine);
488
489        let _ = parse_schema::legacy::parse_schema(self);
490    }
491
492    /// Reload schema from MessagePack-encoded bytes
493    ///
494    /// # Arguments
495    ///
496    /// * `schema_msgpack` - MessagePack-encoded schema bytes
497    /// * `context` - Optional context data JSON string
498    /// * `data` - Optional initial data JSON string
499    ///
500    /// # Returns
501    ///
502    /// A `Result` indicating success or an error message
503    pub fn reload_schema_msgpack(
504        &mut self,
505        schema_msgpack: &[u8],
506        context: Option<&str>,
507        data: Option<&str>,
508    ) -> Result<(), String> {
509        // Deserialize MessagePack to Value
510        let mut schema_val: Value = rmp_serde::from_slice(schema_msgpack)
511            .map_err(|e| format!("failed to deserialize MessagePack schema: {e}"))?;
512
513        let context: Value = json_parser::parse_json_str(context.unwrap_or("{}"))?;
514        let data: Value = json_parser::parse_json_str(data.unwrap_or("{}"))?;
515
516        self.context = context.clone();
517        self.data = data.clone();
518
519        let static_arrays = if let Some(params) = schema_val
520            .get_mut("$params")
521            .and_then(|v| v.as_object_mut())
522        {
523            crate::jsoneval::static_arrays::extract_from_params(params)
524        } else {
525            IndexMap::new()
526        };
527        let static_arrays = Arc::new(static_arrays);
528        self.static_arrays = Arc::clone(&static_arrays);
529        self.schema = Arc::new(schema_val);
530        self.evaluated_schema = (*self.schema).clone();
531
532        let engine = RLogic::new();
533        engine.set_static_arrays(static_arrays);
534        self.engine = Arc::new(engine);
535        self.evaluations = Arc::new(IndexMap::new());
536        self.tables = Arc::new(IndexMap::new());
537        self.table_metadata = Arc::new(IndexMap::new());
538        self.dependencies = Arc::new(IndexMap::new());
539        self.sorted_evaluations = Arc::new(Vec::new());
540        self.dependents_evaluations = Arc::new(IndexMap::new());
541        self.rules_evaluations = Arc::new(Vec::new());
542        self.fields_with_rules = Arc::new(Vec::new());
543        self.others_evaluations = Arc::new(Vec::new());
544        self.value_evaluations = Arc::new(Vec::new());
545        self.layout_paths = Arc::new(Vec::new());
546        self.root_layout_paths = Arc::new(Vec::new());
547        self.options_templates = Arc::new(Vec::new());
548        self.reffed_by = Arc::new(IndexMap::new());
549        self.dep_formula_triggers = Arc::new(IndexMap::new());
550        self.conditional_hidden_fields = Arc::new(Vec::new());
551        self.conditional_readonly_fields = Arc::new(Vec::new());
552        self.subforms.clear();
553        parse_schema::legacy::parse_schema(self)?;
554
555        // Re-initialize eval_data
556        self.eval_data =
557            EvalData::with_schema_data_context(&self.evaluated_schema, &data, &context);
558        self.eval_cache.clear();
559        if let Ok(mut cache) = self.regex_cache.write() {
560            cache.clear();
561        }
562
563        // Cache the MessagePack for future retrievals
564        self.cached_msgpack_schema = Some(schema_msgpack.to_vec());
565        self.invalidate_layout_cache();
566        self.invalidate_validation_cache();
567
568        Ok(())
569    }
570
571    /// Reload schema from a cached ParsedSchema
572    ///
573    /// This is the most efficient way to reload as it reuses pre-parsed schema compilation.
574    ///
575    /// # Arguments
576    ///
577    /// * `parsed` - Arc reference to a cached ParsedSchema
578    /// * `context` - Optional context data JSON string
579    /// * `data` - Optional initial data JSON string
580    ///
581    /// # Returns
582    ///
583    /// A `Result` indicating success or an error message
584    pub fn reload_schema_parsed(
585        &mut self,
586        parsed: Arc<ParsedSchema>,
587        context: Option<&str>,
588        data: Option<&str>,
589    ) -> Result<(), String> {
590        let context: Value = json_parser::parse_json_str(context.unwrap_or("{}"))?;
591        let data: Value = json_parser::parse_json_str(data.unwrap_or("{}"))?;
592
593        // Share all the pre-compiled data from ParsedSchema
594        self.schema = Arc::clone(&parsed.schema);
595        self.evaluations = parsed.evaluations.clone();
596        self.tables = parsed.tables.clone();
597        self.table_metadata = parsed.table_metadata.clone();
598        self.dependencies = parsed.dependencies.clone();
599        self.sorted_evaluations = parsed.sorted_evaluations.clone();
600        self.dependents_evaluations = parsed.dependents_evaluations.clone();
601        self.rules_evaluations = parsed.rules_evaluations.clone();
602        self.fields_with_rules = parsed.fields_with_rules.clone();
603        self.others_evaluations = parsed.others_evaluations.clone();
604        self.value_evaluations = parsed.value_evaluations.clone();
605        self.layout_paths = parsed.layout_paths.clone();
606        self.root_layout_paths = parsed.root_layout_paths.clone();
607        self.layout_field_refs = parsed.layout_field_refs.clone();
608        self.options_templates = parsed.options_templates.clone();
609        self.reffed_by = parsed.reffed_by.clone();
610        self.dep_formula_triggers = parsed.dep_formula_triggers.clone();
611        self.conditional_hidden_fields = parsed.conditional_hidden_fields.clone();
612        self.conditional_readonly_fields = parsed.conditional_readonly_fields.clone();
613        self.static_arrays = parsed.static_arrays.clone();
614
615        // Share the engine Arc (cheap pointer clone, not data clone)
616        self.engine = parsed.engine.clone();
617
618        // Convert Arc<ParsedSchema> subforms to Box<JSONEval> subforms
619        let mut subforms = IndexMap::new();
620        for (path, subform_parsed) in &parsed.subforms {
621            let subform_eval =
622                JSONEval::with_parsed_schema(subform_parsed.clone(), Some("{}"), None)?;
623            subforms.insert(path.clone(), Box::new(subform_eval));
624        }
625        self.subforms = subforms;
626
627        self.context = context.clone();
628        self.data = data.clone();
629        self.evaluated_schema = (*self.schema).clone();
630
631        // Re-initialize eval_data
632        self.eval_data =
633            EvalData::with_schema_data_context(&self.evaluated_schema, &data, &context);
634        self.eval_cache.clear();
635        self.engine.clear_indices();
636        if let Ok(mut cache) = self.regex_cache.write() {
637            cache.clear();
638        }
639
640        // Clear MessagePack cache since we're loading from ParsedSchema
641        self.cached_msgpack_schema = None;
642        self.invalidate_layout_cache();
643        self.invalidate_validation_cache();
644
645        Ok(())
646    }
647
648    /// Reload schema from ParsedSchemaCache using a cache key
649    ///
650    /// This is the recommended way for cross-platform cached schema reloading.
651    ///
652    /// # Arguments
653    ///
654    /// * `cache_key` - Key to lookup in the global ParsedSchemaCache
655    /// * `context` - Optional context data JSON string
656    /// * `data` - Optional initial data JSON string
657    ///
658    /// # Returns
659    ///
660    /// A `Result` indicating success or an error message
661    pub fn reload_schema_from_cache(
662        &mut self,
663        cache_key: &str,
664        context: Option<&str>,
665        data: Option<&str>,
666    ) -> Result<(), String> {
667        // Get the cached ParsedSchema from global cache
668        let parsed = PARSED_SCHEMA_CACHE
669            .get(cache_key)
670            .ok_or_else(|| format!("Schema '{}' not found in cache", cache_key))?;
671
672        // Use reload_schema_parsed with the cached schema
673        self.reload_schema_parsed(parsed, context, data)
674    }
675}