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