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