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