Skip to main content

JSONEval

Struct JSONEval 

Source
pub struct JSONEval {
Show 24 fields pub schema: Arc<Value>, pub engine: Arc<RLogic>, pub evaluations: Arc<IndexMap<String, LogicId>>, pub tables: Arc<IndexMap<String, Value>>, pub table_metadata: Arc<IndexMap<String, TableMetadata>>, pub dependencies: Arc<IndexMap<String, IndexSet<String>>>, pub sorted_evaluations: Arc<Vec<Vec<String>>>, pub dependents_evaluations: Arc<IndexMap<String, Vec<DependentItem>>>, pub rules_evaluations: Arc<Vec<String>>, pub fields_with_rules: Arc<Vec<String>>, pub others_evaluations: Arc<Vec<String>>, pub value_evaluations: Arc<Vec<String>>, pub layout_paths: Arc<Vec<String>>, pub options_templates: Arc<Vec<(String, String, String)>>, pub subforms: IndexMap<String, Box<JSONEval>>, pub reffed_by: Arc<IndexMap<String, Vec<String>>>, pub conditional_hidden_fields: Arc<Vec<String>>, pub conditional_readonly_fields: Arc<Vec<String>>, pub context: Value, pub data: Value, pub evaluated_schema: Value, pub eval_data: EvalData, pub eval_cache: EvalCache, pub cache_enabled: bool, /* private fields */
}

Fields§

§schema: Arc<Value>§engine: Arc<RLogic>§evaluations: Arc<IndexMap<String, LogicId>>§tables: Arc<IndexMap<String, Value>>§table_metadata: Arc<IndexMap<String, TableMetadata>>§dependencies: Arc<IndexMap<String, IndexSet<String>>>§sorted_evaluations: Arc<Vec<Vec<String>>>§dependents_evaluations: Arc<IndexMap<String, Vec<DependentItem>>>§rules_evaluations: Arc<Vec<String>>§fields_with_rules: Arc<Vec<String>>§others_evaluations: Arc<Vec<String>>§value_evaluations: Arc<Vec<String>>§layout_paths: Arc<Vec<String>>§options_templates: Arc<Vec<(String, String, String)>>§subforms: IndexMap<String, Box<JSONEval>>§reffed_by: Arc<IndexMap<String, Vec<String>>>§conditional_hidden_fields: Arc<Vec<String>>§conditional_readonly_fields: Arc<Vec<String>>§context: Value§data: Value§evaluated_schema: Value§eval_data: EvalData§eval_cache: EvalCache§cache_enabled: bool

Implementations§

Source§

impl JSONEval

Source

pub fn should_cache_dependency(&self, key: &str) -> bool

Check if a dependency should be part of the cache key Check if a dependency should be cached Caches everything except keys starting with $ (except $context)

Source

pub fn purge_cache_for_changed_data_with_comparison( &self, old_data: &Value, new_data: &Value, )

Compares old vs new values by deep diffing and purges affected entries

Source

pub fn purge_cache_for_changed_data(&self, changed_data_paths: &[String])

Selectively purge cache entries that depend on changed data paths Finds all eval_keys that depend on the changed paths and removes them Selectively purge cache entries that depend on changed data paths Simpler version without value comparison for cases where we don’t have old data

Source

pub fn purge_cache_for_context_change(&self)

Purge cache entries affected by context changes Purge cache entries that depend on context

Source

pub fn cache_stats(&self) -> CacheStats

Get cache statistics

Examples found in repository?
examples/cache_disable.rs (line 51)
4fn main() {
5    let schema = json!({
6        "type": "object",
7        "properties": {
8            "price": {
9                "type": "number"
10            },
11            "tax": {
12                "type": "number",
13                "value": {
14                    "$evaluation": {
15                        "*": [
16                            { "$ref": "#/properties/price" },
17                            0.1
18                        ]
19                    }
20                }
21            },
22            "total": {
23                "type": "number",
24                "value": {
25                    "$evaluation": {
26                        "+": [
27                            { "$ref": "#/properties/price" },
28                            { "$ref": "#/properties/tax" }
29                        ]
30                    }
31                }
32            }
33        }
34    });
35
36    let schema_str = serde_json::to_string(&schema).unwrap();
37    
38    println!("=== Example 1: With Caching (Default) ===");
39    {
40        let data = json!({ "price": 100 });
41        let data_str = serde_json::to_string(&data).unwrap();
42        
43        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
44        
45        println!("Cache enabled: {}", eval.is_cache_enabled());
46        println!("Initial cache size: {}", eval.cache_len());
47        
48        eval.evaluate(&data_str, None, None, None).unwrap();
49        
50        println!("After evaluation cache size: {}", eval.cache_len());
51        let stats = eval.cache_stats();
52        println!("Cache stats: {}", stats);
53    }
54    
55    println!("\n=== Example 2: Without Caching (Web API Mode) ===");
56    {
57        let data = json!({ "price": 200 });
58        let data_str = serde_json::to_string(&data).unwrap();
59        
60        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
61        
62        // Disable caching for single-use web API scenario
63        eval.disable_cache();
64        
65        println!("Cache enabled: {}", eval.is_cache_enabled());
66        println!("Initial cache size: {}", eval.cache_len());
67        
68        eval.evaluate(&data_str, None, None, None).unwrap();
69        
70        println!("After evaluation cache size: {}", eval.cache_len());
71        let stats = eval.cache_stats();
72        println!("Cache stats: {}", stats);
73        
74        println!("\n✅ No cache overhead - perfect for web APIs!");
75    }
76    
77    println!("\n=== Example 3: Re-enabling Cache ===");
78    {
79        let data = json!({ "price": 300 });
80        let data_str = serde_json::to_string(&data).unwrap();
81        
82        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
83        
84        // Disable then re-enable
85        eval.disable_cache();
86        eval.enable_cache();
87        
88        println!("Cache enabled: {}", eval.is_cache_enabled());
89        eval.evaluate(&data_str, None, None, None).unwrap();
90        
91        println!("Cache size after evaluation: {}", eval.cache_len());
92        println!("\n✅ Cache can be toggled as needed!");
93    }
94}
More examples
Hide additional examples
examples/benchmark.rs (line 393)
32fn main() {
33    let args: Vec<String> = std::env::args().collect();
34    let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("benchmark");
35    
36    let mut iterations = 1usize;
37    let mut scenario_filter: Option<String> = None;
38    let mut show_cpu_info = false;
39    let mut use_parsed_schema = false;
40    let mut use_cache = false;
41    let mut concurrent_count: Option<usize> = None;
42    let mut enable_comparison = false;
43    let mut show_timing = false;
44    let mut i = 1;
45    
46    // Parse arguments
47    while i < args.len() {
48        let arg = &args[i];
49        
50        if arg == "-h" || arg == "--help" {
51            print_help(program_name);
52            return;
53        } else if arg == "--cpu-info" {
54            show_cpu_info = true;
55        } else if arg == "--parsed" {
56            use_parsed_schema = true;
57        } else if arg == "--cache" {
58            use_cache = true;
59        } else if arg == "--compare" {
60            enable_comparison = true;
61        } else if arg == "--timing" {
62            show_timing = true;
63        } else if arg == "--concurrent" {
64            if i + 1 >= args.len() {
65                eprintln!("Error: {} requires a value", arg);
66                print_help(program_name);
67                return;
68            }
69            i += 1;
70            match args[i].parse::<usize>() {
71                Ok(n) if n > 0 => concurrent_count = Some(n),
72                _ => {
73                    eprintln!("Error: concurrent count must be a positive integer, got '{}'", args[i]);
74                    return;
75                }
76            }
77        } else if arg == "-i" || arg == "--iterations" {
78            if i + 1 >= args.len() {
79                eprintln!("Error: {} requires a value", arg);
80                print_help(program_name);
81                return;
82            }
83            i += 1;
84            match args[i].parse::<usize>() {
85                Ok(n) if n > 0 => iterations = n,
86                _ => {
87                    eprintln!("Error: iterations must be a positive integer, got '{}'", args[i]);
88                    return;
89                }
90            }
91        } else if !arg.starts_with('-') {
92            scenario_filter = Some(arg.clone());
93        } else {
94            eprintln!("Error: unknown option '{}'", arg);
95            print_help(program_name);
96            return;
97        }
98        
99        i += 1;
100    }
101    
102    println!("\n🚀 JSON Evaluation - Benchmark\n");
103    
104    // Show CPU info if requested or if running benchmarks
105    if show_cpu_info || iterations > 1 || concurrent_count.is_some() {
106        common::print_cpu_info();
107    }
108    
109    if use_parsed_schema {
110        println!("📦 Mode: ParsedSchema (parse once, reuse for all iterations)\n");
111    }
112    
113    if use_cache {
114        println!("♻️  Mode: Cache (reuse JSONEval instance across iterations)\n");
115    }
116    
117    if let Some(count) = concurrent_count {
118        println!("🔀 Concurrent evaluations: {} threads\n", count);
119    } else if iterations > 1 {
120        println!("🔄 Iterations per scenario: {}\n", iterations);
121    }
122    
123    if enable_comparison {
124        println!("🔍 Comparison: enabled");
125    }
126    if show_timing {
127        println!("⏱️  Internal timing: enabled");
128    }
129    if enable_comparison || show_timing {
130        println!();
131    }
132
133    let samples_dir = Path::new("samples");
134    let mut scenarios = common::discover_scenarios(samples_dir);
135    
136    // Filter scenarios if a filter is provided
137    if let Some(ref filter) = scenario_filter {
138        scenarios.retain(|s| s.name.contains(filter));
139        println!("📋 Filtering scenarios matching: '{}'\n", filter);
140    }
141
142    if scenarios.is_empty() {
143        if let Some(filter) = scenario_filter {
144            println!(
145                "ℹ️  No scenarios found matching '{}' in `{}`.",
146                filter,
147                samples_dir.display()
148            );
149        } else {
150            println!(
151                "ℹ️  No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
152                samples_dir.display()
153            );
154        }
155        return;
156    }
157    
158    println!("📊 Found {} scenario(s)\n", scenarios.len());
159
160    let mut total_parse_time = std::time::Duration::ZERO;
161    let mut total_eval_time = std::time::Duration::ZERO;
162    let mut successful_scenarios = 0;
163    let mut comparison_failures = 0;
164
165    for scenario in &scenarios {
166        println!("==============================");
167        println!("Scenario: {}", scenario.name);
168        println!("Schema: {} ({})", 
169            scenario.schema_path.display(),
170            if scenario.is_msgpack { "MessagePack" } else { "JSON" }
171        );
172        println!("Data: {}\n", scenario.data_path.display());
173
174        // Clear timing data from previous scenarios
175        if show_timing {
176            json_eval_rs::enable_timing();
177            json_eval_rs::clear_timing_data();
178        }
179
180        let data_str = fs::read_to_string(&scenario.data_path)
181            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
182
183        println!("Running evaluation...\n");
184
185        let (parse_time, eval_time, evaluated_schema, eval, iteration_times) = if use_parsed_schema {
186            // ParsedSchema mode: parse once, reuse for all iterations/threads
187            let start_time = Instant::now();
188            
189            let parsed_schema = if scenario.is_msgpack {
190                let schema_msgpack = fs::read(&scenario.schema_path)
191                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
192                println!("  📦 MessagePack schema size: {} bytes", schema_msgpack.len());
193                Arc::new(ParsedSchema::parse_msgpack(&schema_msgpack)
194                    .unwrap_or_else(|e| panic!("failed to parse MessagePack schema: {}", e)))
195            } else {
196                let schema_str = fs::read_to_string(&scenario.schema_path)
197                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
198                Arc::new(ParsedSchema::parse(&schema_str)
199                    .unwrap_or_else(|e| panic!("failed to parse schema: {}", e)))
200            };
201            
202            let parse_time = start_time.elapsed();
203            println!("  Schema parsing & compilation: {:?}", parse_time);
204            
205            // Concurrent mode with ParsedSchema
206            if let Some(thread_count) = concurrent_count {
207                use std::thread;
208                
209                let eval_start = Instant::now();
210                let mut handles = vec![];
211                
212                for thread_id in 0..thread_count {
213                    let parsed_clone = parsed_schema.clone();
214                    let data_str_clone = data_str.clone();
215                    let iter_count = iterations;
216                    let thread_use_cache = use_cache;
217                    
218                    let handle = thread::spawn(move || {
219                        let mut thread_times = Vec::with_capacity(iter_count);
220                        let mut last_schema = Value::Null;
221                        
222                        let mut eval_instance = JSONEval::with_parsed_schema(
223                            parsed_clone.clone(),
224                            Some("{}"),
225                            Some(&data_str_clone)
226                        ).unwrap();
227                        
228                        for iter in 0..iter_count {
229                            let iter_start = Instant::now();
230                            
231                            if !thread_use_cache && iter > 0 {
232                                eval_instance = JSONEval::with_parsed_schema(
233                                    parsed_clone.clone(),
234                                    Some("{}"),
235                                    Some(&data_str_clone)
236                                ).unwrap();
237                            }
238                            
239                            eval_instance.evaluate(&data_str_clone, Some("{}"), None, None).unwrap();
240                            last_schema = eval_instance.get_evaluated_schema(false);
241                            thread_times.push(iter_start.elapsed());
242                        }
243                        
244                        (thread_times, last_schema, thread_id)
245                    });
246                    handles.push(handle);
247                }
248                
249                let mut all_iteration_times = Vec::new();
250                let mut evaluated_schema = Value::Null;
251                
252                for handle in handles {
253                    let (thread_times, thread_schema, thread_id) = handle.join().unwrap();
254                    println!("  Thread {} completed {} iterations", thread_id, thread_times.len());
255                    all_iteration_times.extend(thread_times);
256                    evaluated_schema = thread_schema; // Use last thread's result
257                }
258                
259                let eval_time = eval_start.elapsed();
260                
261                // Create a temp eval for metadata export
262                let temp_eval = JSONEval::with_parsed_schema(
263                    parsed_schema.clone(),
264                    Some("{}"),
265                    Some(&data_str)
266                ).unwrap();
267                
268                (parse_time, eval_time, evaluated_schema, temp_eval, all_iteration_times)
269            } else {
270                // Sequential iterations with ParsedSchema
271                let eval_start = Instant::now();
272                let mut evaluated_schema = Value::Null;
273                let mut iteration_times = Vec::with_capacity(iterations);
274                let mut eval_instance = JSONEval::with_parsed_schema(
275                    parsed_schema.clone(),
276                    Some("{}"),
277                    Some(&data_str)
278                ).unwrap();
279                
280                for iter in 0..iterations {
281                    let iter_start = Instant::now();
282                    
283                    if !use_cache && iter > 0 {
284                        eval_instance = JSONEval::with_parsed_schema(
285                            parsed_schema.clone(),
286                            Some("{}"),
287                            Some(&data_str)
288                        ).unwrap();
289                    }
290                    
291                    eval_instance.evaluate(&data_str, Some("{}"), None, None)
292                        .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
293                    evaluated_schema = eval_instance.get_evaluated_schema(false);
294                    iteration_times.push(iter_start.elapsed());
295                    
296                    if iterations > 1 && (iter + 1) % 10 == 0 {
297                        print!(".");
298                        if (iter + 1) % 50 == 0 {
299                            println!(" {}/{}", iter + 1, iterations);
300                        }
301                    }
302                }
303                
304                if iterations > 1 && iterations % 50 != 0 {
305                    println!(" {}/{}", iterations, iterations);
306                }
307                
308                let eval_time = eval_start.elapsed();
309                (parse_time, eval_time, evaluated_schema, eval_instance, iteration_times)
310            }
311        } else {
312            // Traditional mode: parse and create JSONEval each time
313            let schema_msgpack = if scenario.is_msgpack {
314                let bytes = fs::read(&scenario.schema_path)
315                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
316                println!("  📦 MessagePack schema size: {} bytes", bytes.len());
317                Some(bytes)
318            } else {
319                None
320            };
321            
322            let schema_str = if !scenario.is_msgpack {
323                Some(fs::read_to_string(&scenario.schema_path)
324                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e)))
325            } else {
326                None
327            };
328
329            let start_time = Instant::now();
330            let mut eval = if scenario.is_msgpack {
331                JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
332                    .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
333            } else {
334                JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
335                    .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
336            };
337            let parse_time = start_time.elapsed();
338            println!("  Schema parsing & compilation: {:?}", parse_time);
339            
340            let eval_start = Instant::now();
341            let mut evaluated_schema = Value::Null;
342            let mut iteration_times = Vec::with_capacity(iterations);
343            
344            for iter in 0..iterations {
345                let iter_start = Instant::now();
346                
347                if !use_cache && iter > 0 {
348                    eval = if scenario.is_msgpack {
349                        JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
350                            .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
351                    } else {
352                        JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
353                            .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
354                    };
355                }
356                
357                eval.evaluate(&data_str, Some("{}"), None, None)
358                    .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
359                evaluated_schema = eval.get_evaluated_schema(false);
360                iteration_times.push(iter_start.elapsed());
361                
362                if iterations > 1 && (iter + 1) % 10 == 0 {
363                    print!(".");
364                    if (iter + 1) % 50 == 0 {
365                        println!(" {}/{}", iter + 1, iterations);
366                    }
367                }
368            }
369            
370            if iterations > 1 && iterations % 50 != 0 {
371                println!(" {}/{}", iterations, iterations);
372            }
373            
374            let eval_time = eval_start.elapsed();
375            (parse_time, eval_time, evaluated_schema, eval, iteration_times)
376        };
377        
378        // Calculate statistics
379        let total_iterations = iteration_times.len();
380        if total_iterations == 1 {
381            println!("  Evaluation: {:?}", eval_time);
382        } else {
383            let avg_time = eval_time / total_iterations as u32;
384            let min_time = iteration_times.iter().min().unwrap();
385            let max_time = iteration_times.iter().max().unwrap();
386            
387            println!("  Total evaluation time: {:?}", eval_time);
388            println!("  Total iterations: {}", total_iterations);
389            println!("  Average per iteration: {:?}", avg_time);
390            println!("  Min: {:?} | Max: {:?}", min_time, max_time);
391            
392            // Show cache statistics
393            let cache_stats = eval.cache_stats();
394            println!("  Cache: {} entries, {} hits, {} misses ({:.1}% hit rate)",
395                cache_stats.entries,
396                cache_stats.hits,
397                cache_stats.misses,
398                cache_stats.hit_rate * 100.0
399            );
400        }
401
402        let total_time = parse_time + eval_time;
403        println!("⏱️  Execution time: {:?}\n", total_time);
404        
405        // Print detailed timing breakdown if --timing flag is set
406        if show_timing {
407            json_eval_rs::print_timing_summary();
408        }
409        
410        // Track statistics
411        total_parse_time += parse_time;
412        total_eval_time += eval_time;
413        successful_scenarios += 1;
414
415        let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
416        let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
417
418        fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
419            .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
420
421        let mut metadata_obj = Map::new();
422        metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
423        metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
424
425        fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
426            .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
427
428        println!("✅ Results saved:");
429        println!("  - {}", evaluated_path.display());
430        println!("  - {}\n", parsed_path.display());
431
432        // Optional comparison
433        if enable_comparison {
434            if let Some(comp_path) = &scenario.comparison_path {
435                if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
436                    comparison_failures += 1;
437                }
438                println!();
439            }
440        }
441    }
442    
443    // Print summary statistics
444    if successful_scenarios > 0 {
445        println!("\n{}", "=".repeat(50));
446        println!("📊 Summary Statistics");
447        println!("{}", "=".repeat(50));
448        println!("Total scenarios run: {}", successful_scenarios);
449        println!("Total parsing time: {:?}", total_parse_time);
450        println!("Total evaluation time: {:?}", total_eval_time);
451        println!("Total time: {:?}", total_parse_time + total_eval_time);
452        
453        if successful_scenarios > 1 {
454            println!("\nAverage per scenario:");
455            println!("  Parsing: {:?}", total_parse_time / successful_scenarios as u32);
456            println!("  Evaluation: {:?}", total_eval_time / successful_scenarios as u32);
457        }
458        
459        if enable_comparison {
460            println!("\nComparison failures: {}", comparison_failures);
461        }
462        
463        println!("\n✅ All scenarios completed successfully!\n");
464    }
465}
Source

pub fn clear_cache(&self)

Clear the cache manually

Source

pub fn enable_cache(&mut self)

Enable caching

Examples found in repository?
examples/cache_disable.rs (line 86)
4fn main() {
5    let schema = json!({
6        "type": "object",
7        "properties": {
8            "price": {
9                "type": "number"
10            },
11            "tax": {
12                "type": "number",
13                "value": {
14                    "$evaluation": {
15                        "*": [
16                            { "$ref": "#/properties/price" },
17                            0.1
18                        ]
19                    }
20                }
21            },
22            "total": {
23                "type": "number",
24                "value": {
25                    "$evaluation": {
26                        "+": [
27                            { "$ref": "#/properties/price" },
28                            { "$ref": "#/properties/tax" }
29                        ]
30                    }
31                }
32            }
33        }
34    });
35
36    let schema_str = serde_json::to_string(&schema).unwrap();
37    
38    println!("=== Example 1: With Caching (Default) ===");
39    {
40        let data = json!({ "price": 100 });
41        let data_str = serde_json::to_string(&data).unwrap();
42        
43        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
44        
45        println!("Cache enabled: {}", eval.is_cache_enabled());
46        println!("Initial cache size: {}", eval.cache_len());
47        
48        eval.evaluate(&data_str, None, None, None).unwrap();
49        
50        println!("After evaluation cache size: {}", eval.cache_len());
51        let stats = eval.cache_stats();
52        println!("Cache stats: {}", stats);
53    }
54    
55    println!("\n=== Example 2: Without Caching (Web API Mode) ===");
56    {
57        let data = json!({ "price": 200 });
58        let data_str = serde_json::to_string(&data).unwrap();
59        
60        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
61        
62        // Disable caching for single-use web API scenario
63        eval.disable_cache();
64        
65        println!("Cache enabled: {}", eval.is_cache_enabled());
66        println!("Initial cache size: {}", eval.cache_len());
67        
68        eval.evaluate(&data_str, None, None, None).unwrap();
69        
70        println!("After evaluation cache size: {}", eval.cache_len());
71        let stats = eval.cache_stats();
72        println!("Cache stats: {}", stats);
73        
74        println!("\n✅ No cache overhead - perfect for web APIs!");
75    }
76    
77    println!("\n=== Example 3: Re-enabling Cache ===");
78    {
79        let data = json!({ "price": 300 });
80        let data_str = serde_json::to_string(&data).unwrap();
81        
82        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
83        
84        // Disable then re-enable
85        eval.disable_cache();
86        eval.enable_cache();
87        
88        println!("Cache enabled: {}", eval.is_cache_enabled());
89        eval.evaluate(&data_str, None, None, None).unwrap();
90        
91        println!("Cache size after evaluation: {}", eval.cache_len());
92        println!("\n✅ Cache can be toggled as needed!");
93    }
94}
Source

pub fn disable_cache(&mut self)

Disable caching

Examples found in repository?
examples/cache_disable.rs (line 63)
4fn main() {
5    let schema = json!({
6        "type": "object",
7        "properties": {
8            "price": {
9                "type": "number"
10            },
11            "tax": {
12                "type": "number",
13                "value": {
14                    "$evaluation": {
15                        "*": [
16                            { "$ref": "#/properties/price" },
17                            0.1
18                        ]
19                    }
20                }
21            },
22            "total": {
23                "type": "number",
24                "value": {
25                    "$evaluation": {
26                        "+": [
27                            { "$ref": "#/properties/price" },
28                            { "$ref": "#/properties/tax" }
29                        ]
30                    }
31                }
32            }
33        }
34    });
35
36    let schema_str = serde_json::to_string(&schema).unwrap();
37    
38    println!("=== Example 1: With Caching (Default) ===");
39    {
40        let data = json!({ "price": 100 });
41        let data_str = serde_json::to_string(&data).unwrap();
42        
43        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
44        
45        println!("Cache enabled: {}", eval.is_cache_enabled());
46        println!("Initial cache size: {}", eval.cache_len());
47        
48        eval.evaluate(&data_str, None, None, None).unwrap();
49        
50        println!("After evaluation cache size: {}", eval.cache_len());
51        let stats = eval.cache_stats();
52        println!("Cache stats: {}", stats);
53    }
54    
55    println!("\n=== Example 2: Without Caching (Web API Mode) ===");
56    {
57        let data = json!({ "price": 200 });
58        let data_str = serde_json::to_string(&data).unwrap();
59        
60        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
61        
62        // Disable caching for single-use web API scenario
63        eval.disable_cache();
64        
65        println!("Cache enabled: {}", eval.is_cache_enabled());
66        println!("Initial cache size: {}", eval.cache_len());
67        
68        eval.evaluate(&data_str, None, None, None).unwrap();
69        
70        println!("After evaluation cache size: {}", eval.cache_len());
71        let stats = eval.cache_stats();
72        println!("Cache stats: {}", stats);
73        
74        println!("\n✅ No cache overhead - perfect for web APIs!");
75    }
76    
77    println!("\n=== Example 3: Re-enabling Cache ===");
78    {
79        let data = json!({ "price": 300 });
80        let data_str = serde_json::to_string(&data).unwrap();
81        
82        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
83        
84        // Disable then re-enable
85        eval.disable_cache();
86        eval.enable_cache();
87        
88        println!("Cache enabled: {}", eval.is_cache_enabled());
89        eval.evaluate(&data_str, None, None, None).unwrap();
90        
91        println!("Cache size after evaluation: {}", eval.cache_len());
92        println!("\n✅ Cache can be toggled as needed!");
93    }
94}
Source

pub fn is_cache_enabled(&self) -> bool

Check if cache is enabled

Examples found in repository?
examples/cache_disable.rs (line 45)
4fn main() {
5    let schema = json!({
6        "type": "object",
7        "properties": {
8            "price": {
9                "type": "number"
10            },
11            "tax": {
12                "type": "number",
13                "value": {
14                    "$evaluation": {
15                        "*": [
16                            { "$ref": "#/properties/price" },
17                            0.1
18                        ]
19                    }
20                }
21            },
22            "total": {
23                "type": "number",
24                "value": {
25                    "$evaluation": {
26                        "+": [
27                            { "$ref": "#/properties/price" },
28                            { "$ref": "#/properties/tax" }
29                        ]
30                    }
31                }
32            }
33        }
34    });
35
36    let schema_str = serde_json::to_string(&schema).unwrap();
37    
38    println!("=== Example 1: With Caching (Default) ===");
39    {
40        let data = json!({ "price": 100 });
41        let data_str = serde_json::to_string(&data).unwrap();
42        
43        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
44        
45        println!("Cache enabled: {}", eval.is_cache_enabled());
46        println!("Initial cache size: {}", eval.cache_len());
47        
48        eval.evaluate(&data_str, None, None, None).unwrap();
49        
50        println!("After evaluation cache size: {}", eval.cache_len());
51        let stats = eval.cache_stats();
52        println!("Cache stats: {}", stats);
53    }
54    
55    println!("\n=== Example 2: Without Caching (Web API Mode) ===");
56    {
57        let data = json!({ "price": 200 });
58        let data_str = serde_json::to_string(&data).unwrap();
59        
60        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
61        
62        // Disable caching for single-use web API scenario
63        eval.disable_cache();
64        
65        println!("Cache enabled: {}", eval.is_cache_enabled());
66        println!("Initial cache size: {}", eval.cache_len());
67        
68        eval.evaluate(&data_str, None, None, None).unwrap();
69        
70        println!("After evaluation cache size: {}", eval.cache_len());
71        let stats = eval.cache_stats();
72        println!("Cache stats: {}", stats);
73        
74        println!("\n✅ No cache overhead - perfect for web APIs!");
75    }
76    
77    println!("\n=== Example 3: Re-enabling Cache ===");
78    {
79        let data = json!({ "price": 300 });
80        let data_str = serde_json::to_string(&data).unwrap();
81        
82        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
83        
84        // Disable then re-enable
85        eval.disable_cache();
86        eval.enable_cache();
87        
88        println!("Cache enabled: {}", eval.is_cache_enabled());
89        eval.evaluate(&data_str, None, None, None).unwrap();
90        
91        println!("Cache size after evaluation: {}", eval.cache_len());
92        println!("\n✅ Cache can be toggled as needed!");
93    }
94}
Source

pub fn cache_len(&self) -> usize

Get cache size

Examples found in repository?
examples/cache_disable.rs (line 46)
4fn main() {
5    let schema = json!({
6        "type": "object",
7        "properties": {
8            "price": {
9                "type": "number"
10            },
11            "tax": {
12                "type": "number",
13                "value": {
14                    "$evaluation": {
15                        "*": [
16                            { "$ref": "#/properties/price" },
17                            0.1
18                        ]
19                    }
20                }
21            },
22            "total": {
23                "type": "number",
24                "value": {
25                    "$evaluation": {
26                        "+": [
27                            { "$ref": "#/properties/price" },
28                            { "$ref": "#/properties/tax" }
29                        ]
30                    }
31                }
32            }
33        }
34    });
35
36    let schema_str = serde_json::to_string(&schema).unwrap();
37    
38    println!("=== Example 1: With Caching (Default) ===");
39    {
40        let data = json!({ "price": 100 });
41        let data_str = serde_json::to_string(&data).unwrap();
42        
43        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
44        
45        println!("Cache enabled: {}", eval.is_cache_enabled());
46        println!("Initial cache size: {}", eval.cache_len());
47        
48        eval.evaluate(&data_str, None, None, None).unwrap();
49        
50        println!("After evaluation cache size: {}", eval.cache_len());
51        let stats = eval.cache_stats();
52        println!("Cache stats: {}", stats);
53    }
54    
55    println!("\n=== Example 2: Without Caching (Web API Mode) ===");
56    {
57        let data = json!({ "price": 200 });
58        let data_str = serde_json::to_string(&data).unwrap();
59        
60        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
61        
62        // Disable caching for single-use web API scenario
63        eval.disable_cache();
64        
65        println!("Cache enabled: {}", eval.is_cache_enabled());
66        println!("Initial cache size: {}", eval.cache_len());
67        
68        eval.evaluate(&data_str, None, None, None).unwrap();
69        
70        println!("After evaluation cache size: {}", eval.cache_len());
71        let stats = eval.cache_stats();
72        println!("Cache stats: {}", stats);
73        
74        println!("\n✅ No cache overhead - perfect for web APIs!");
75    }
76    
77    println!("\n=== Example 3: Re-enabling Cache ===");
78    {
79        let data = json!({ "price": 300 });
80        let data_str = serde_json::to_string(&data).unwrap();
81        
82        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
83        
84        // Disable then re-enable
85        eval.disable_cache();
86        eval.enable_cache();
87        
88        println!("Cache enabled: {}", eval.is_cache_enabled());
89        eval.evaluate(&data_str, None, None, None).unwrap();
90        
91        println!("Cache size after evaluation: {}", eval.cache_len());
92        println!("\n✅ Cache can be toggled as needed!");
93    }
94}
Source§

impl JSONEval

Source

pub fn evaluate_dependents( &mut self, changed_paths: &[String], data: Option<&str>, context: Option<&str>, re_evaluate: bool, token: Option<&CancellationToken>, canceled_paths: Option<&mut Vec<String>>, ) -> Result<Value, String>

Evaluate fields that depend on a changed path This processes all dependent fields transitively when a source field changes

Source§

impl JSONEval

Source

pub fn evaluate( &mut self, data: &str, context: Option<&str>, paths: Option<&[String]>, token: Option<&CancellationToken>, ) -> Result<(), String>

Evaluate the schema with the given data and context.

§Arguments
  • data - The data to evaluate.
  • context - The context to evaluate.
§Returns

A Result indicating success or an error message.

Examples found in repository?
examples/cache_demo.rs (line 74)
38fn demo_local_cache() -> Result<(), Box<dyn std::error::Error>> {
39    println!("📦 Example 1: Local Cache Instance");
40    println!("Creating a dedicated cache for this application...\n");
41    
42    let cache = ParsedSchemaCache::new();
43    
44    // Simple schema
45    let schema_json = r#"{
46        "$params": {
47            "rate": { "type": "number" }
48        },
49        "result": {
50            "type": "number",
51            "title": "Calculated Result",
52            "$evaluation": {
53                "logic": { "*": [{"var": "$rate"}, 100] }
54            }
55        }
56    }"#;
57    
58    // Parse and cache with a custom key
59    println!("📝 Parsing schema and caching with key 'calculation-v1'...");
60    let parsed = ParsedSchema::parse(schema_json)?;
61    cache.insert("calculation-v1".to_string(), Arc::new(parsed));
62    
63    println!("✅ Schema cached successfully");
64    println!("   Cache size: {} entries", cache.len());
65    println!("   Keys: {:?}\n", cache.keys());
66    
67    // Retrieve and use cached schema
68    println!("🔍 Retrieving cached schema...");
69    if let Some(cached_schema) = cache.get("calculation-v1") {
70        println!("✅ Retrieved from cache");
71        
72        // Create JSONEval from cached ParsedSchema
73        let mut eval = JSONEval::with_parsed_schema(cached_schema, Some(r#"{"rate": 1.5}"#), None)?;
74        eval.evaluate("{}", None, None, None)?;
75        
76        let evaluated = eval.get_evaluated_schema(false);
77        let result = evaluated.pointer("/result")
78            .and_then(|v| v.as_f64())
79            .unwrap_or(0.0);
80        println!("   Evaluation result: {}\n", result);
81    }
82    
83    // Check cache stats
84    let stats = cache.stats();
85    println!("📊 Cache Statistics: {}", stats);
86    
87    // Remove entry
88    println!("\n🗑️  Removing 'calculation-v1' from cache...");
89    cache.remove("calculation-v1");
90    println!("   Cache size after removal: {}", cache.len());
91    
92    Ok(())
93}
94
95fn demo_global_cache() -> Result<(), Box<dyn std::error::Error>> {
96    println!("🌍 Example 2: Global Cache Instance");
97    println!("Using the built-in PARSED_SCHEMA_CACHE...\n");
98    
99    let schema_json = r#"{
100        "$params": {
101            "x": { "type": "number" },
102            "y": { "type": "number" }
103        },
104        "sum": {
105            "type": "number",
106            "$evaluation": { "+": [{"var": "$x"}, {"var": "$y"}] }
107        }
108    }"#;
109    
110    // Use global cache
111    println!("📝 Caching schema globally with key 'math-operations'...");
112    let parsed = ParsedSchema::parse(schema_json)?;
113    PARSED_SCHEMA_CACHE.insert("math-operations".to_string(), Arc::new(parsed));
114    
115    println!("✅ Schema cached globally");
116    println!("   Global cache size: {}\n", PARSED_SCHEMA_CACHE.len());
117    
118    // Access from anywhere in the application
119    simulate_another_function()?;
120    
121    // Clean up
122    println!("\n🧹 Clearing global cache...");
123    PARSED_SCHEMA_CACHE.clear();
124    println!("   Global cache size: {}", PARSED_SCHEMA_CACHE.len());
125    
126    Ok(())
127}
128
129fn simulate_another_function() -> Result<(), Box<dyn std::error::Error>> {
130    println!("🔄 In another function, accessing global cache...");
131    
132    if let Some(cached) = PARSED_SCHEMA_CACHE.get("math-operations") {
133        println!("✅ Retrieved schema from global cache");
134        
135        let mut eval = JSONEval::with_parsed_schema(cached, Some(r#"{"x": 10, "y": 20}"#), None)?;
136        eval.evaluate("{}", None, None, None)?;
137        
138        let evaluated = eval.get_evaluated_schema(false);
139        let sum = evaluated.pointer("/sum")
140            .and_then(|v| v.as_f64())
141            .unwrap_or(0.0);
142        println!("   Result: {}", sum);
143    }
144    
145    Ok(())
146}
147
148fn demo_performance_comparison() -> Result<(), Box<dyn std::error::Error>> {
149    println!("⚡ Example 3: Performance Comparison");
150    println!("Comparing cached vs non-cached schema usage...\n");
151    
152    let schema_json = r#"{
153        "$params": {
154            "value": { "type": "number" }
155        },
156        "doubled": {
157            "type": "number",
158            "$evaluation": { "*": [{"var": "$value"}, 2] }
159        },
160        "tripled": {
161            "type": "number",
162            "$evaluation": { "*": [{"var": "$value"}, 3] }
163        }
164    }"#;
165    
166    let iterations = 100;
167    
168    // WITHOUT CACHE: Parse schema every time
169    println!("🐌 Without cache (parse + evaluate each time):");
170    let start = Instant::now();
171    for i in 0..iterations {
172        let context = format!(r#"{{"value": {}}}"#, i);
173        let mut eval = JSONEval::new(schema_json, Some(&context), None)?;
174        eval.evaluate("{}", None, None, None)?;
175    }
176    let without_cache = start.elapsed();
177    println!("   Time: {:?}", without_cache);
178    println!("   Avg per iteration: {:?}\n", without_cache / iterations);
179    
180    // WITH CACHE: Parse once, evaluate many times
181    println!("🚀 With cache (parse once, reuse for all evaluations):");
182    let cache = ParsedSchemaCache::new();
183    
184    // Parse once
185    let parse_start = Instant::now();
186    let parsed = ParsedSchema::parse(schema_json)?;
187    cache.insert("perf-test".to_string(), Arc::new(parsed));
188    let parse_time = parse_start.elapsed();
189    
190    // Evaluate many times
191    let eval_start = Instant::now();
192    for i in 0..iterations {
193        if let Some(cached) = cache.get("perf-test") {
194            let context = format!(r#"{{"value": {}}}"#, i);
195            let mut eval = JSONEval::with_parsed_schema(cached.clone(), Some(&context), None)?;
196            eval.evaluate("{}", None, None, None)?;
197        }
198    }
199    let eval_time = eval_start.elapsed();
200    let with_cache = parse_time + eval_time;
201    
202    println!("   Parse time: {:?}", parse_time);
203    println!("   Eval time: {:?}", eval_time);
204    println!("   Total time: {:?}", with_cache);
205    println!("   Avg per iteration: {:?}\n", eval_time / iterations);
206    
207    let speedup = without_cache.as_secs_f64() / with_cache.as_secs_f64();
208    println!("📈 Speedup: {:.2}x faster", speedup);
209    
210    Ok(())
211}
More examples
Hide additional examples
examples/cache_disable.rs (line 48)
4fn main() {
5    let schema = json!({
6        "type": "object",
7        "properties": {
8            "price": {
9                "type": "number"
10            },
11            "tax": {
12                "type": "number",
13                "value": {
14                    "$evaluation": {
15                        "*": [
16                            { "$ref": "#/properties/price" },
17                            0.1
18                        ]
19                    }
20                }
21            },
22            "total": {
23                "type": "number",
24                "value": {
25                    "$evaluation": {
26                        "+": [
27                            { "$ref": "#/properties/price" },
28                            { "$ref": "#/properties/tax" }
29                        ]
30                    }
31                }
32            }
33        }
34    });
35
36    let schema_str = serde_json::to_string(&schema).unwrap();
37    
38    println!("=== Example 1: With Caching (Default) ===");
39    {
40        let data = json!({ "price": 100 });
41        let data_str = serde_json::to_string(&data).unwrap();
42        
43        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
44        
45        println!("Cache enabled: {}", eval.is_cache_enabled());
46        println!("Initial cache size: {}", eval.cache_len());
47        
48        eval.evaluate(&data_str, None, None, None).unwrap();
49        
50        println!("After evaluation cache size: {}", eval.cache_len());
51        let stats = eval.cache_stats();
52        println!("Cache stats: {}", stats);
53    }
54    
55    println!("\n=== Example 2: Without Caching (Web API Mode) ===");
56    {
57        let data = json!({ "price": 200 });
58        let data_str = serde_json::to_string(&data).unwrap();
59        
60        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
61        
62        // Disable caching for single-use web API scenario
63        eval.disable_cache();
64        
65        println!("Cache enabled: {}", eval.is_cache_enabled());
66        println!("Initial cache size: {}", eval.cache_len());
67        
68        eval.evaluate(&data_str, None, None, None).unwrap();
69        
70        println!("After evaluation cache size: {}", eval.cache_len());
71        let stats = eval.cache_stats();
72        println!("Cache stats: {}", stats);
73        
74        println!("\n✅ No cache overhead - perfect for web APIs!");
75    }
76    
77    println!("\n=== Example 3: Re-enabling Cache ===");
78    {
79        let data = json!({ "price": 300 });
80        let data_str = serde_json::to_string(&data).unwrap();
81        
82        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
83        
84        // Disable then re-enable
85        eval.disable_cache();
86        eval.enable_cache();
87        
88        println!("Cache enabled: {}", eval.is_cache_enabled());
89        eval.evaluate(&data_str, None, None, None).unwrap();
90        
91        println!("Cache size after evaluation: {}", eval.cache_len());
92        println!("\n✅ Cache can be toggled as needed!");
93    }
94}
examples/spaj_toggle.rs (line 49)
6fn main() {
7    println!("\n🚀 JSON Evaluation - SPAJ Toggle Example\n");
8
9    let schema_path = Path::new("samples/spaj.json");
10    let schema_str = fs::read_to_string(schema_path).expect("Failed to read schema");
11
12    // Initial data with minimal context required
13    let context_str = json!({
14        "agentProfile": { "sob": "AG" } 
15    }).to_string();
16
17    let initial_data = json!({
18        "illustration": {
19            "basicinformation": {
20                "print_polflag": false
21            }
22        }
23    }).to_string();
24
25    // Initialize logic
26    let mut eval = JSONEval::new(&schema_str, Some(&context_str), Some(&initial_data))
27        .expect("Failed to create JSONEval");
28
29    // Helper to check visibility
30    let check_visibility = |eval: &mut JSONEval, expected_hidden: bool, step: &str| {
31        let result = eval.get_evaluated_schema(false);
32        let hidden = result.pointer("/illustration/properties/basicinformation/properties/print_poladdress/condition/hidden")
33            .and_then(|v| v.as_bool());
34        
35        match hidden {
36            Some(val) => {
37                if val == expected_hidden {
38                    println!("✅ {}: Hidden = {} (Expected: {})", step, val, expected_hidden);
39                } else {
40                    println!("❌ {}: Hidden = {} (Expected: {})", step, val, expected_hidden);
41                }
42            },
43            None => println!("❌ {}: 'hidden' property not found", step),
44        }
45    };
46
47    // Step 1: Initial state (false)
48    println!("Step 1: Initial State (print_polflag: false)");
49    eval.evaluate(&initial_data, Some(&context_str), None, None).expect("Evaluation failed");
50    check_visibility(&mut eval, true, "Initial check");
51
52    // Step 2: Toggle to true
53    println!("\nStep 2: Toggle True (print_polflag: true)");
54    let data_true = json!({
55        "illustration": {
56            "basicinformation": {
57                "print_polflag": true
58            }
59        }
60    }).to_string();
61    eval.evaluate(&data_true, Some(&context_str), None, None).expect("Evaluation failed");
62    check_visibility(&mut eval, false, "Toggle ON check");
63
64    // Step 3: Toggle back to false
65    println!("\nStep 3: Toggle False (print_polflag: false)");
66    let data_false = json!({
67        "illustration": {
68            "basicinformation": {
69                "print_polflag": false
70            }
71        }
72    }).to_string();
73    eval.evaluate(&data_false, Some(&context_str), None, None).expect("Evaluation failed");
74    
75    let hidden_path = "#/illustration/properties/basicinformation/properties/print_poladdress/condition/hidden";
76    if let Some(deps) = eval.dependencies.get(hidden_path) {
77        println!("Debug: Dependencies for hidden: {:?}", deps);
78    } else {
79        println!("Debug: No dependencies found for hidden path");
80    }
81
82    // Debug: Print current flag value
83    if let Some(val) = eval.get_evaluated_schema(false).pointer("/illustration/properties/basicinformation/properties/print_polflag/value") {
84         println!("Debug: print_polflag value is: {}", val);
85    }
86
87    check_visibility(&mut eval, true, "Toggle OFF check");
88}
examples/basic_msgpack.rs (line 138)
28fn main() {
29    let args: Vec<String> = std::env::args().collect();
30    let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic_msgpack");
31    
32    let mut scenario_filter: Option<String> = None;
33    let mut enable_comparison = false;
34    let mut show_timing = false;
35    let mut i = 1;
36    
37    // Parse arguments
38    while i < args.len() {
39        let arg = &args[i];
40        
41        if arg == "-h" || arg == "--help" {
42            print_help(program_name);
43            return;
44        } else if arg == "--compare" {
45            enable_comparison = true;
46        } else if arg == "--timing" {
47            show_timing = true;
48        } else if !arg.starts_with('-') {
49            scenario_filter = Some(arg.clone());
50        } else {
51            eprintln!("Error: unknown option '{}'", arg);
52            print_help(program_name);
53            return;
54        }
55        
56        i += 1;
57    }
58    
59    println!("\n🚀 JSON Evaluation - Basic Example (MessagePack Schema)\n");
60    
61    if enable_comparison {
62        println!("🔍 Comparison: enabled");
63    }
64    if show_timing {
65        println!("⏱️  Internal timing: enabled");
66    }
67    if enable_comparison || show_timing {
68        println!();
69    }
70    
71    let samples_dir = Path::new("samples");
72    let mut scenarios = common::discover_scenarios(samples_dir);
73    
74    // Filter to only MessagePack scenarios
75    scenarios.retain(|s| s.is_msgpack);
76    
77    // Filter scenarios if a filter is provided
78    if let Some(ref filter) = scenario_filter {
79        scenarios.retain(|s| s.name.contains(filter));
80        println!("📋 Filtering scenarios matching: '{}'\n", filter);
81    }
82
83    if scenarios.is_empty() {
84        if let Some(filter) = scenario_filter {
85            println!(
86                "ℹ️  No MessagePack scenarios found matching '{}' in `{}`.",
87                filter,
88                samples_dir.display()
89            );
90        } else {
91            println!(
92                "ℹ️  No MessagePack scenarios discovered in `{}`. Add files like `name.bform` and `name-data.json`.",
93                samples_dir.display()
94            );
95        }
96        return;
97    }
98    
99    println!("📊 Found {} MessagePack scenario(s)\n", scenarios.len());
100
101    let mut total_parse_time = std::time::Duration::ZERO;
102    let mut total_eval_time = std::time::Duration::ZERO;
103    let mut successful_scenarios = 0;
104    let mut comparison_failures = 0;
105
106    for scenario in &scenarios {
107        println!("==============================");
108        println!("Scenario: {}", scenario.name);
109        println!("Schema: {} (MessagePack)", scenario.schema_path.display());
110        println!("Data: {}\n", scenario.data_path.display());
111
112        // Clear timing data from previous scenarios
113        if show_timing {
114            json_eval_rs::enable_timing();
115            json_eval_rs::clear_timing_data();
116        }
117
118        let data_str = fs::read_to_string(&scenario.data_path)
119            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
120
121        // Step 1: Parse schema (new_from_msgpack)
122        let parse_start = Instant::now();
123        
124        let schema_msgpack = fs::read(&scenario.schema_path)
125            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
126        
127        println!("  📦 MessagePack schema size: {} bytes", schema_msgpack.len());
128        
129        let mut eval = JSONEval::new_from_msgpack(&schema_msgpack, None, Some(&data_str))
130            .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e));
131        
132        let parse_time = parse_start.elapsed();
133        println!("  📝 Parse (msgpack): {:?}", parse_time);
134        
135        // Step 2: Evaluate
136        let eval_start = Instant::now();
137        
138        eval.evaluate(&data_str, Some("{}"), None, None)
139            .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
140        
141        let evaluated_schema = eval.get_evaluated_schema(false);
142        let eval_time = eval_start.elapsed();
143        
144        println!("  ⚡ Eval: {:?}", eval_time);
145        println!("  ⏱️  Total: {:?}\n", parse_time + eval_time);
146        
147        // Print detailed timing breakdown if --timing flag is set
148        if show_timing {
149            json_eval_rs::print_timing_summary();
150        }
151        
152        total_parse_time += parse_time;
153        total_eval_time += eval_time;
154        successful_scenarios += 1;
155
156        // Save results
157        let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
158        let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
159
160        fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
161            .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
162
163        let mut metadata_obj = Map::new();
164        metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
165        metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
166        metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
167
168        fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
169            .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
170
171        println!("✅ Results saved:");
172        println!("  - {}", evaluated_path.display());
173        println!("  - {}\n", parsed_path.display());
174
175        // Optional comparison
176        if enable_comparison {
177            if let Some(comp_path) = &scenario.comparison_path {
178                if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
179                    comparison_failures += 1;
180                }
181                println!();
182            }
183        }
184    }
185    
186    // Print summary
187    println!("{}", "=".repeat(50));
188    println!("📊 Summary");
189    println!("{}", "=".repeat(50));
190    println!("Total scenarios run: {}", successful_scenarios);
191    println!("Total parse time: {:?}", total_parse_time);
192    println!("Total eval time: {:?}", total_eval_time);
193    println!("Total time: {:?}", total_parse_time + total_eval_time);
194    
195    if successful_scenarios > 1 {
196        println!("\nAverage per scenario:");
197        println!("  Parse: {:?}", total_parse_time / successful_scenarios as u32);
198        println!("  Eval: {:?}", total_eval_time / successful_scenarios as u32);
199    }
200    
201    if enable_comparison {
202        println!("Comparison failures: {}", comparison_failures);
203    }
204    
205    println!("\n✅ All scenarios completed!\n");
206}
examples/basic_parsed.rs (line 149)
30fn main() {
31    let args: Vec<String> = std::env::args().collect();
32    let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic_parsed");
33    
34    let mut scenario_filter: Option<String> = None;
35    let mut enable_comparison = false;
36    let mut show_timing = false;
37    let mut i = 1;
38    
39    // Parse arguments
40    while i < args.len() {
41        let arg = &args[i];
42        
43        if arg == "-h" || arg == "--help" {
44            print_help(program_name);
45            return;
46        } else if arg == "--compare" {
47            enable_comparison = true;
48        } else if arg == "--timing" {
49            show_timing = true;
50        } else if !arg.starts_with('-') {
51            scenario_filter = Some(arg.clone());
52        } else {
53            eprintln!("Error: unknown option '{}'", arg);
54            print_help(program_name);
55            return;
56        }
57        
58        i += 1;
59    }
60    
61    println!("\n🚀 JSON Evaluation - Basic Example (ParsedSchema)\n");
62    println!("📦 Using Arc<ParsedSchema> for efficient caching\n");
63    
64    if enable_comparison {
65        println!("🔍 Comparison: enabled");
66    }
67    if show_timing {
68        println!("⏱️  Internal timing: enabled");
69    }
70    if enable_comparison || show_timing {
71        println!();
72    }
73    
74    let samples_dir = Path::new("samples");
75    let mut scenarios = common::discover_scenarios(samples_dir);
76    
77    // Filter scenarios if a filter is provided
78    if let Some(ref filter) = scenario_filter {
79        scenarios.retain(|s| s.name.contains(filter));
80        println!("📋 Filtering scenarios matching: '{}'\n", filter);
81    }
82
83    if scenarios.is_empty() {
84        if let Some(filter) = scenario_filter {
85            println!(
86                "ℹ️  No scenarios found matching '{}' in `{}`.",
87                filter,
88                samples_dir.display()
89            );
90        } else {
91            println!(
92                "ℹ️  No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
93                samples_dir.display()
94            );
95        }
96        return;
97    }
98    
99    println!("📊 Found {} scenario(s)\n", scenarios.len());
100
101    let mut total_parse_time = std::time::Duration::ZERO;
102    let mut total_eval_time = std::time::Duration::ZERO;
103    let mut successful_scenarios = 0;
104    let mut comparison_failures = 0;
105
106    for scenario in &scenarios {
107        println!("==============================");
108        println!("Scenario: {}", scenario.name);
109        println!("Schema: {} ({})", 
110            scenario.schema_path.display(),
111            if scenario.is_msgpack { "MessagePack" } else { "JSON" }
112        );
113        println!("Data: {}\n", scenario.data_path.display());
114
115        // Clear timing data from previous scenarios
116        if show_timing {
117            json_eval_rs::enable_timing();
118            json_eval_rs::clear_timing_data();
119        }
120
121        let data_str = fs::read_to_string(&scenario.data_path)
122            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
123
124        // Step 1: Parse schema once
125        let parse_start = Instant::now();
126        let parsed_schema = if scenario.is_msgpack {
127            let schema_msgpack = fs::read(&scenario.schema_path)
128                .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
129            println!("  📦 MessagePack schema size: {} bytes", schema_msgpack.len());
130            Arc::new(ParsedSchema::parse_msgpack(&schema_msgpack)
131                .unwrap_or_else(|e| panic!("failed to parse MessagePack schema: {}", e)))
132        } else {
133            let schema_str = fs::read_to_string(&scenario.schema_path)
134                .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
135            Arc::new(ParsedSchema::parse(&schema_str)
136                .unwrap_or_else(|e| panic!("failed to parse schema: {}", e)))
137        };
138        let parse_time = parse_start.elapsed();
139        println!("  📝 Schema parsing: {:?}", parse_time);
140        
141        // Step 2: Create JSONEval from ParsedSchema (reuses compiled logic)
142        let eval_start = Instant::now();
143        let mut eval = JSONEval::with_parsed_schema(
144            parsed_schema.clone(),  // Arc::clone is cheap!
145            Some("{}"),
146            Some(&data_str)
147        ).unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e));
148
149        eval.evaluate(&data_str, Some("{}"), None, None)
150            .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
151        
152        let evaluated_schema = eval.get_evaluated_schema(false);
153        let eval_time = eval_start.elapsed();
154        
155        println!("  ⚡ Eval: {:?}", eval_time);
156        println!("  ⏱️  Total: {:?}\n", parse_time + eval_time);
157        
158        // Print detailed timing breakdown if --timing flag is set
159        if show_timing {
160            json_eval_rs::print_timing_summary();
161        }
162        
163        total_parse_time += parse_time;
164        total_eval_time += eval_time;
165        successful_scenarios += 1;
166
167        // Save results
168        let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
169        let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
170
171        fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
172            .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
173
174        let mut metadata_obj = Map::new();
175        metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
176        metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
177        metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
178
179        fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
180            .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
181
182        println!("✅ Results saved:");
183        println!("  - {}", evaluated_path.display());
184        println!("  - {}\n", parsed_path.display());
185
186        // Optional comparison
187        if enable_comparison {
188            if let Some(comp_path) = &scenario.comparison_path {
189                if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
190                    comparison_failures += 1;
191                }
192                println!();
193            }
194        }
195    }
196    
197    // Print summary
198    println!("{}", "=".repeat(50));
199    println!("📊 Summary");
200    println!("{}", "=".repeat(50));
201    println!("Total scenarios run: {}", successful_scenarios);
202    println!("Total parsing time: {:?}", total_parse_time);
203    println!("Total evaluation time: {:?}", total_eval_time);
204    println!("Total time: {:?}", total_parse_time + total_eval_time);
205    
206    if successful_scenarios > 1 {
207        println!("\nAverage per scenario:");
208        println!("  Parsing: {:?}", total_parse_time / successful_scenarios as u32);
209        println!("  Evaluation: {:?}", total_eval_time / successful_scenarios as u32);
210    }
211    
212    if enable_comparison {
213        println!("\nComparison failures: {}", comparison_failures);
214    }
215    
216    println!("\n✅ All scenarios completed!\n");
217}
examples/basic.rs (line 139)
28fn main() {
29    let args: Vec<String> = std::env::args().collect();
30    let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic");
31    
32    let mut scenario_filter: Option<String> = None;
33    let mut enable_comparison = false;
34    let mut show_timing = false;
35    let mut i = 1;
36    
37    // Parse arguments
38    while i < args.len() {
39        let arg = &args[i];
40        
41        if arg == "-h" || arg == "--help" {
42            print_help(program_name);
43            return;
44        } else if arg == "--compare" {
45            enable_comparison = true;
46        } else if arg == "--timing" {
47            show_timing = true;
48        } else if !arg.starts_with('-') {
49            scenario_filter = Some(arg.clone());
50        } else {
51            eprintln!("Error: unknown option '{}'", arg);
52            print_help(program_name);
53            return;
54        }
55        
56        i += 1;
57    }
58    
59    println!("\n🚀 JSON Evaluation - Basic Example (JSON Schema)\n");
60    
61    if enable_comparison {
62        println!("🔍 Comparison: enabled");
63    }
64    if show_timing {
65        println!("⏱️  Internal timing: enabled");
66    }
67    if enable_comparison || show_timing {
68        println!();
69    }
70    
71    let samples_dir = Path::new("samples");
72    let mut scenarios = common::discover_scenarios(samples_dir);
73    
74    // Filter out MessagePack scenarios - only use JSON
75    scenarios.retain(|s| !s.is_msgpack);
76    
77    // Filter scenarios if a filter is provided
78    if let Some(ref filter) = scenario_filter {
79        scenarios.retain(|s| s.name.contains(filter));
80        println!("📋 Filtering scenarios matching: '{}'\n", filter);
81    }
82
83    if scenarios.is_empty() {
84        if let Some(filter) = scenario_filter {
85            println!(
86                "ℹ️  No scenarios found matching '{}' in `{}`.",
87                filter,
88                samples_dir.display()
89            );
90        } else {
91            println!(
92                "ℹ️  No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
93                samples_dir.display()
94            );
95        }
96        return;
97    }
98    
99    println!("📊 Found {} scenario(s)\n", scenarios.len());
100
101    let mut total_parse_time = std::time::Duration::ZERO;
102    let mut total_eval_time = std::time::Duration::ZERO;
103    let mut successful_scenarios = 0;
104    let mut comparison_failures = 0;
105
106    for scenario in &scenarios {
107        println!("==============================");
108        println!("Scenario: {}", scenario.name);
109        println!("Schema: {} ({})", 
110            scenario.schema_path.display(),
111            if scenario.is_msgpack { "MessagePack" } else { "JSON" }
112        );
113        println!("Data: {}\n", scenario.data_path.display());
114
115        // Clear timing data from previous scenarios
116        if show_timing {
117            json_eval_rs::enable_timing();
118            json_eval_rs::clear_timing_data();
119        }
120
121        let data_str = fs::read_to_string(&scenario.data_path)
122            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
123
124        // Step 1: Parse schema (JSONEval::new)
125        let parse_start = Instant::now();
126        
127        let schema_str = fs::read_to_string(&scenario.schema_path)
128            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
129        
130        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str))
131            .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e));
132        
133        let parse_time = parse_start.elapsed();
134        println!("  📝 Parse (new): {:?}", parse_time);
135        
136        // Step 2: Evaluate
137        let eval_start = Instant::now();
138        
139        eval.evaluate(&data_str, Some("{}"), None, None)
140            .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
141
142        // Step 3: Validate
143        let validation_start = Instant::now();
144        let validation_result = eval.validate(&data_str, None, None, None)
145            .unwrap_or_else(|e| panic!("validation failed: {}", e));
146        let validation_time = validation_start.elapsed();
147        println!("  🛡️ Validate: {:?}", validation_time);
148        
149        // Legacy behavior: get_evaluated_schema takes skip_layout: bool
150        // We pass false to ensure layout IS resolved
151        let evaluated_schema = eval.get_evaluated_schema(false);
152        let schema_value = eval.get_schema_value();
153        let eval_time = eval_start.elapsed();
154        
155        println!("  ⚡ Eval: {:?}", eval_time);
156        println!("  ⏱️  Total: {:?}\n", parse_time + eval_time);
157        
158        // Print detailed timing breakdown if --timing flag is set
159        if show_timing {
160            json_eval_rs::print_timing_summary();
161        }
162        
163        total_parse_time += parse_time;
164        total_eval_time += eval_time;
165        successful_scenarios += 1;
166
167        // Save results
168        let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
169        let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
170        let value_path = samples_dir.join(format!("{}-schema-value.json", scenario.name));
171        let validation_path = samples_dir.join(format!("{}-validation.json", scenario.name));
172
173        fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
174            .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
175
176        let mut metadata_obj = Map::new();
177        metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
178        metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
179        metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
180
181        fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
182            .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
183
184        fs::write(&value_path, common::pretty_json(&schema_value))
185            .unwrap_or_else(|e| panic!("failed to write {}: {}", value_path.display(), e));
186
187        let validation_value = serde_json::to_value(&validation_result)
188            .unwrap_or_else(|e| panic!("failed to serialize validation result: {}", e));
189        fs::write(&validation_path, common::pretty_json(&validation_value))
190            .unwrap_or_else(|e| panic!("failed to write {}: {}", validation_path.display(), e));
191
192        println!("✅ Results saved:");
193        println!("  - {}", evaluated_path.display());
194        println!("  - {}", parsed_path.display());
195        println!("  - {}", value_path.display());
196        println!("  - {}\n", validation_path.display());
197
198        // Optional comparison
199        if enable_comparison {
200            if let Some(comp_path) = &scenario.comparison_path {
201                if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
202                    comparison_failures += 1;
203                }
204                println!();
205            }
206        }
207    }
208    
209    // Print summary
210    println!("{}", "=".repeat(50));
211    println!("📊 Summary");
212    println!("{}", "=".repeat(50));
213    println!("Total scenarios run: {}", successful_scenarios);
214    println!("Total parse time: {:?}", total_parse_time);
215    println!("Total eval time: {:?}", total_eval_time);
216    println!("Total time: {:?}", total_parse_time + total_eval_time);
217    
218    if successful_scenarios > 1 {
219        println!("\nAverage per scenario:");
220        println!("  Parse: {:?}", total_parse_time / successful_scenarios as u32);
221        println!("  Eval: {:?}", total_eval_time / successful_scenarios as u32);
222    }
223    
224    if enable_comparison {
225        println!("Comparison failures: {}", comparison_failures);
226    }
227    
228    println!("\n✅ All scenarios completed!\n");
229}
Source§

impl JSONEval

Source

pub fn get_evaluated_schema(&mut self, skip_layout: bool) -> Value

Get the evaluated schema with optional layout resolution.

§Arguments
  • skip_layout - Whether to skip layout resolution.
§Returns

The evaluated schema as a JSON value.

Examples found in repository?
examples/cache_demo.rs (line 76)
38fn demo_local_cache() -> Result<(), Box<dyn std::error::Error>> {
39    println!("📦 Example 1: Local Cache Instance");
40    println!("Creating a dedicated cache for this application...\n");
41    
42    let cache = ParsedSchemaCache::new();
43    
44    // Simple schema
45    let schema_json = r#"{
46        "$params": {
47            "rate": { "type": "number" }
48        },
49        "result": {
50            "type": "number",
51            "title": "Calculated Result",
52            "$evaluation": {
53                "logic": { "*": [{"var": "$rate"}, 100] }
54            }
55        }
56    }"#;
57    
58    // Parse and cache with a custom key
59    println!("📝 Parsing schema and caching with key 'calculation-v1'...");
60    let parsed = ParsedSchema::parse(schema_json)?;
61    cache.insert("calculation-v1".to_string(), Arc::new(parsed));
62    
63    println!("✅ Schema cached successfully");
64    println!("   Cache size: {} entries", cache.len());
65    println!("   Keys: {:?}\n", cache.keys());
66    
67    // Retrieve and use cached schema
68    println!("🔍 Retrieving cached schema...");
69    if let Some(cached_schema) = cache.get("calculation-v1") {
70        println!("✅ Retrieved from cache");
71        
72        // Create JSONEval from cached ParsedSchema
73        let mut eval = JSONEval::with_parsed_schema(cached_schema, Some(r#"{"rate": 1.5}"#), None)?;
74        eval.evaluate("{}", None, None, None)?;
75        
76        let evaluated = eval.get_evaluated_schema(false);
77        let result = evaluated.pointer("/result")
78            .and_then(|v| v.as_f64())
79            .unwrap_or(0.0);
80        println!("   Evaluation result: {}\n", result);
81    }
82    
83    // Check cache stats
84    let stats = cache.stats();
85    println!("📊 Cache Statistics: {}", stats);
86    
87    // Remove entry
88    println!("\n🗑️  Removing 'calculation-v1' from cache...");
89    cache.remove("calculation-v1");
90    println!("   Cache size after removal: {}", cache.len());
91    
92    Ok(())
93}
94
95fn demo_global_cache() -> Result<(), Box<dyn std::error::Error>> {
96    println!("🌍 Example 2: Global Cache Instance");
97    println!("Using the built-in PARSED_SCHEMA_CACHE...\n");
98    
99    let schema_json = r#"{
100        "$params": {
101            "x": { "type": "number" },
102            "y": { "type": "number" }
103        },
104        "sum": {
105            "type": "number",
106            "$evaluation": { "+": [{"var": "$x"}, {"var": "$y"}] }
107        }
108    }"#;
109    
110    // Use global cache
111    println!("📝 Caching schema globally with key 'math-operations'...");
112    let parsed = ParsedSchema::parse(schema_json)?;
113    PARSED_SCHEMA_CACHE.insert("math-operations".to_string(), Arc::new(parsed));
114    
115    println!("✅ Schema cached globally");
116    println!("   Global cache size: {}\n", PARSED_SCHEMA_CACHE.len());
117    
118    // Access from anywhere in the application
119    simulate_another_function()?;
120    
121    // Clean up
122    println!("\n🧹 Clearing global cache...");
123    PARSED_SCHEMA_CACHE.clear();
124    println!("   Global cache size: {}", PARSED_SCHEMA_CACHE.len());
125    
126    Ok(())
127}
128
129fn simulate_another_function() -> Result<(), Box<dyn std::error::Error>> {
130    println!("🔄 In another function, accessing global cache...");
131    
132    if let Some(cached) = PARSED_SCHEMA_CACHE.get("math-operations") {
133        println!("✅ Retrieved schema from global cache");
134        
135        let mut eval = JSONEval::with_parsed_schema(cached, Some(r#"{"x": 10, "y": 20}"#), None)?;
136        eval.evaluate("{}", None, None, None)?;
137        
138        let evaluated = eval.get_evaluated_schema(false);
139        let sum = evaluated.pointer("/sum")
140            .and_then(|v| v.as_f64())
141            .unwrap_or(0.0);
142        println!("   Result: {}", sum);
143    }
144    
145    Ok(())
146}
More examples
Hide additional examples
examples/spaj_toggle.rs (line 31)
6fn main() {
7    println!("\n🚀 JSON Evaluation - SPAJ Toggle Example\n");
8
9    let schema_path = Path::new("samples/spaj.json");
10    let schema_str = fs::read_to_string(schema_path).expect("Failed to read schema");
11
12    // Initial data with minimal context required
13    let context_str = json!({
14        "agentProfile": { "sob": "AG" } 
15    }).to_string();
16
17    let initial_data = json!({
18        "illustration": {
19            "basicinformation": {
20                "print_polflag": false
21            }
22        }
23    }).to_string();
24
25    // Initialize logic
26    let mut eval = JSONEval::new(&schema_str, Some(&context_str), Some(&initial_data))
27        .expect("Failed to create JSONEval");
28
29    // Helper to check visibility
30    let check_visibility = |eval: &mut JSONEval, expected_hidden: bool, step: &str| {
31        let result = eval.get_evaluated_schema(false);
32        let hidden = result.pointer("/illustration/properties/basicinformation/properties/print_poladdress/condition/hidden")
33            .and_then(|v| v.as_bool());
34        
35        match hidden {
36            Some(val) => {
37                if val == expected_hidden {
38                    println!("✅ {}: Hidden = {} (Expected: {})", step, val, expected_hidden);
39                } else {
40                    println!("❌ {}: Hidden = {} (Expected: {})", step, val, expected_hidden);
41                }
42            },
43            None => println!("❌ {}: 'hidden' property not found", step),
44        }
45    };
46
47    // Step 1: Initial state (false)
48    println!("Step 1: Initial State (print_polflag: false)");
49    eval.evaluate(&initial_data, Some(&context_str), None, None).expect("Evaluation failed");
50    check_visibility(&mut eval, true, "Initial check");
51
52    // Step 2: Toggle to true
53    println!("\nStep 2: Toggle True (print_polflag: true)");
54    let data_true = json!({
55        "illustration": {
56            "basicinformation": {
57                "print_polflag": true
58            }
59        }
60    }).to_string();
61    eval.evaluate(&data_true, Some(&context_str), None, None).expect("Evaluation failed");
62    check_visibility(&mut eval, false, "Toggle ON check");
63
64    // Step 3: Toggle back to false
65    println!("\nStep 3: Toggle False (print_polflag: false)");
66    let data_false = json!({
67        "illustration": {
68            "basicinformation": {
69                "print_polflag": false
70            }
71        }
72    }).to_string();
73    eval.evaluate(&data_false, Some(&context_str), None, None).expect("Evaluation failed");
74    
75    let hidden_path = "#/illustration/properties/basicinformation/properties/print_poladdress/condition/hidden";
76    if let Some(deps) = eval.dependencies.get(hidden_path) {
77        println!("Debug: Dependencies for hidden: {:?}", deps);
78    } else {
79        println!("Debug: No dependencies found for hidden path");
80    }
81
82    // Debug: Print current flag value
83    if let Some(val) = eval.get_evaluated_schema(false).pointer("/illustration/properties/basicinformation/properties/print_polflag/value") {
84         println!("Debug: print_polflag value is: {}", val);
85    }
86
87    check_visibility(&mut eval, true, "Toggle OFF check");
88}
examples/basic_msgpack.rs (line 141)
28fn main() {
29    let args: Vec<String> = std::env::args().collect();
30    let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic_msgpack");
31    
32    let mut scenario_filter: Option<String> = None;
33    let mut enable_comparison = false;
34    let mut show_timing = false;
35    let mut i = 1;
36    
37    // Parse arguments
38    while i < args.len() {
39        let arg = &args[i];
40        
41        if arg == "-h" || arg == "--help" {
42            print_help(program_name);
43            return;
44        } else if arg == "--compare" {
45            enable_comparison = true;
46        } else if arg == "--timing" {
47            show_timing = true;
48        } else if !arg.starts_with('-') {
49            scenario_filter = Some(arg.clone());
50        } else {
51            eprintln!("Error: unknown option '{}'", arg);
52            print_help(program_name);
53            return;
54        }
55        
56        i += 1;
57    }
58    
59    println!("\n🚀 JSON Evaluation - Basic Example (MessagePack Schema)\n");
60    
61    if enable_comparison {
62        println!("🔍 Comparison: enabled");
63    }
64    if show_timing {
65        println!("⏱️  Internal timing: enabled");
66    }
67    if enable_comparison || show_timing {
68        println!();
69    }
70    
71    let samples_dir = Path::new("samples");
72    let mut scenarios = common::discover_scenarios(samples_dir);
73    
74    // Filter to only MessagePack scenarios
75    scenarios.retain(|s| s.is_msgpack);
76    
77    // Filter scenarios if a filter is provided
78    if let Some(ref filter) = scenario_filter {
79        scenarios.retain(|s| s.name.contains(filter));
80        println!("📋 Filtering scenarios matching: '{}'\n", filter);
81    }
82
83    if scenarios.is_empty() {
84        if let Some(filter) = scenario_filter {
85            println!(
86                "ℹ️  No MessagePack scenarios found matching '{}' in `{}`.",
87                filter,
88                samples_dir.display()
89            );
90        } else {
91            println!(
92                "ℹ️  No MessagePack scenarios discovered in `{}`. Add files like `name.bform` and `name-data.json`.",
93                samples_dir.display()
94            );
95        }
96        return;
97    }
98    
99    println!("📊 Found {} MessagePack scenario(s)\n", scenarios.len());
100
101    let mut total_parse_time = std::time::Duration::ZERO;
102    let mut total_eval_time = std::time::Duration::ZERO;
103    let mut successful_scenarios = 0;
104    let mut comparison_failures = 0;
105
106    for scenario in &scenarios {
107        println!("==============================");
108        println!("Scenario: {}", scenario.name);
109        println!("Schema: {} (MessagePack)", scenario.schema_path.display());
110        println!("Data: {}\n", scenario.data_path.display());
111
112        // Clear timing data from previous scenarios
113        if show_timing {
114            json_eval_rs::enable_timing();
115            json_eval_rs::clear_timing_data();
116        }
117
118        let data_str = fs::read_to_string(&scenario.data_path)
119            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
120
121        // Step 1: Parse schema (new_from_msgpack)
122        let parse_start = Instant::now();
123        
124        let schema_msgpack = fs::read(&scenario.schema_path)
125            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
126        
127        println!("  📦 MessagePack schema size: {} bytes", schema_msgpack.len());
128        
129        let mut eval = JSONEval::new_from_msgpack(&schema_msgpack, None, Some(&data_str))
130            .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e));
131        
132        let parse_time = parse_start.elapsed();
133        println!("  📝 Parse (msgpack): {:?}", parse_time);
134        
135        // Step 2: Evaluate
136        let eval_start = Instant::now();
137        
138        eval.evaluate(&data_str, Some("{}"), None, None)
139            .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
140        
141        let evaluated_schema = eval.get_evaluated_schema(false);
142        let eval_time = eval_start.elapsed();
143        
144        println!("  ⚡ Eval: {:?}", eval_time);
145        println!("  ⏱️  Total: {:?}\n", parse_time + eval_time);
146        
147        // Print detailed timing breakdown if --timing flag is set
148        if show_timing {
149            json_eval_rs::print_timing_summary();
150        }
151        
152        total_parse_time += parse_time;
153        total_eval_time += eval_time;
154        successful_scenarios += 1;
155
156        // Save results
157        let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
158        let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
159
160        fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
161            .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
162
163        let mut metadata_obj = Map::new();
164        metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
165        metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
166        metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
167
168        fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
169            .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
170
171        println!("✅ Results saved:");
172        println!("  - {}", evaluated_path.display());
173        println!("  - {}\n", parsed_path.display());
174
175        // Optional comparison
176        if enable_comparison {
177            if let Some(comp_path) = &scenario.comparison_path {
178                if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
179                    comparison_failures += 1;
180                }
181                println!();
182            }
183        }
184    }
185    
186    // Print summary
187    println!("{}", "=".repeat(50));
188    println!("📊 Summary");
189    println!("{}", "=".repeat(50));
190    println!("Total scenarios run: {}", successful_scenarios);
191    println!("Total parse time: {:?}", total_parse_time);
192    println!("Total eval time: {:?}", total_eval_time);
193    println!("Total time: {:?}", total_parse_time + total_eval_time);
194    
195    if successful_scenarios > 1 {
196        println!("\nAverage per scenario:");
197        println!("  Parse: {:?}", total_parse_time / successful_scenarios as u32);
198        println!("  Eval: {:?}", total_eval_time / successful_scenarios as u32);
199    }
200    
201    if enable_comparison {
202        println!("Comparison failures: {}", comparison_failures);
203    }
204    
205    println!("\n✅ All scenarios completed!\n");
206}
examples/basic_parsed.rs (line 152)
30fn main() {
31    let args: Vec<String> = std::env::args().collect();
32    let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic_parsed");
33    
34    let mut scenario_filter: Option<String> = None;
35    let mut enable_comparison = false;
36    let mut show_timing = false;
37    let mut i = 1;
38    
39    // Parse arguments
40    while i < args.len() {
41        let arg = &args[i];
42        
43        if arg == "-h" || arg == "--help" {
44            print_help(program_name);
45            return;
46        } else if arg == "--compare" {
47            enable_comparison = true;
48        } else if arg == "--timing" {
49            show_timing = true;
50        } else if !arg.starts_with('-') {
51            scenario_filter = Some(arg.clone());
52        } else {
53            eprintln!("Error: unknown option '{}'", arg);
54            print_help(program_name);
55            return;
56        }
57        
58        i += 1;
59    }
60    
61    println!("\n🚀 JSON Evaluation - Basic Example (ParsedSchema)\n");
62    println!("📦 Using Arc<ParsedSchema> for efficient caching\n");
63    
64    if enable_comparison {
65        println!("🔍 Comparison: enabled");
66    }
67    if show_timing {
68        println!("⏱️  Internal timing: enabled");
69    }
70    if enable_comparison || show_timing {
71        println!();
72    }
73    
74    let samples_dir = Path::new("samples");
75    let mut scenarios = common::discover_scenarios(samples_dir);
76    
77    // Filter scenarios if a filter is provided
78    if let Some(ref filter) = scenario_filter {
79        scenarios.retain(|s| s.name.contains(filter));
80        println!("📋 Filtering scenarios matching: '{}'\n", filter);
81    }
82
83    if scenarios.is_empty() {
84        if let Some(filter) = scenario_filter {
85            println!(
86                "ℹ️  No scenarios found matching '{}' in `{}`.",
87                filter,
88                samples_dir.display()
89            );
90        } else {
91            println!(
92                "ℹ️  No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
93                samples_dir.display()
94            );
95        }
96        return;
97    }
98    
99    println!("📊 Found {} scenario(s)\n", scenarios.len());
100
101    let mut total_parse_time = std::time::Duration::ZERO;
102    let mut total_eval_time = std::time::Duration::ZERO;
103    let mut successful_scenarios = 0;
104    let mut comparison_failures = 0;
105
106    for scenario in &scenarios {
107        println!("==============================");
108        println!("Scenario: {}", scenario.name);
109        println!("Schema: {} ({})", 
110            scenario.schema_path.display(),
111            if scenario.is_msgpack { "MessagePack" } else { "JSON" }
112        );
113        println!("Data: {}\n", scenario.data_path.display());
114
115        // Clear timing data from previous scenarios
116        if show_timing {
117            json_eval_rs::enable_timing();
118            json_eval_rs::clear_timing_data();
119        }
120
121        let data_str = fs::read_to_string(&scenario.data_path)
122            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
123
124        // Step 1: Parse schema once
125        let parse_start = Instant::now();
126        let parsed_schema = if scenario.is_msgpack {
127            let schema_msgpack = fs::read(&scenario.schema_path)
128                .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
129            println!("  📦 MessagePack schema size: {} bytes", schema_msgpack.len());
130            Arc::new(ParsedSchema::parse_msgpack(&schema_msgpack)
131                .unwrap_or_else(|e| panic!("failed to parse MessagePack schema: {}", e)))
132        } else {
133            let schema_str = fs::read_to_string(&scenario.schema_path)
134                .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
135            Arc::new(ParsedSchema::parse(&schema_str)
136                .unwrap_or_else(|e| panic!("failed to parse schema: {}", e)))
137        };
138        let parse_time = parse_start.elapsed();
139        println!("  📝 Schema parsing: {:?}", parse_time);
140        
141        // Step 2: Create JSONEval from ParsedSchema (reuses compiled logic)
142        let eval_start = Instant::now();
143        let mut eval = JSONEval::with_parsed_schema(
144            parsed_schema.clone(),  // Arc::clone is cheap!
145            Some("{}"),
146            Some(&data_str)
147        ).unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e));
148
149        eval.evaluate(&data_str, Some("{}"), None, None)
150            .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
151        
152        let evaluated_schema = eval.get_evaluated_schema(false);
153        let eval_time = eval_start.elapsed();
154        
155        println!("  ⚡ Eval: {:?}", eval_time);
156        println!("  ⏱️  Total: {:?}\n", parse_time + eval_time);
157        
158        // Print detailed timing breakdown if --timing flag is set
159        if show_timing {
160            json_eval_rs::print_timing_summary();
161        }
162        
163        total_parse_time += parse_time;
164        total_eval_time += eval_time;
165        successful_scenarios += 1;
166
167        // Save results
168        let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
169        let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
170
171        fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
172            .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
173
174        let mut metadata_obj = Map::new();
175        metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
176        metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
177        metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
178
179        fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
180            .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
181
182        println!("✅ Results saved:");
183        println!("  - {}", evaluated_path.display());
184        println!("  - {}\n", parsed_path.display());
185
186        // Optional comparison
187        if enable_comparison {
188            if let Some(comp_path) = &scenario.comparison_path {
189                if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
190                    comparison_failures += 1;
191                }
192                println!();
193            }
194        }
195    }
196    
197    // Print summary
198    println!("{}", "=".repeat(50));
199    println!("📊 Summary");
200    println!("{}", "=".repeat(50));
201    println!("Total scenarios run: {}", successful_scenarios);
202    println!("Total parsing time: {:?}", total_parse_time);
203    println!("Total evaluation time: {:?}", total_eval_time);
204    println!("Total time: {:?}", total_parse_time + total_eval_time);
205    
206    if successful_scenarios > 1 {
207        println!("\nAverage per scenario:");
208        println!("  Parsing: {:?}", total_parse_time / successful_scenarios as u32);
209        println!("  Evaluation: {:?}", total_eval_time / successful_scenarios as u32);
210    }
211    
212    if enable_comparison {
213        println!("\nComparison failures: {}", comparison_failures);
214    }
215    
216    println!("\n✅ All scenarios completed!\n");
217}
examples/basic.rs (line 151)
28fn main() {
29    let args: Vec<String> = std::env::args().collect();
30    let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic");
31    
32    let mut scenario_filter: Option<String> = None;
33    let mut enable_comparison = false;
34    let mut show_timing = false;
35    let mut i = 1;
36    
37    // Parse arguments
38    while i < args.len() {
39        let arg = &args[i];
40        
41        if arg == "-h" || arg == "--help" {
42            print_help(program_name);
43            return;
44        } else if arg == "--compare" {
45            enable_comparison = true;
46        } else if arg == "--timing" {
47            show_timing = true;
48        } else if !arg.starts_with('-') {
49            scenario_filter = Some(arg.clone());
50        } else {
51            eprintln!("Error: unknown option '{}'", arg);
52            print_help(program_name);
53            return;
54        }
55        
56        i += 1;
57    }
58    
59    println!("\n🚀 JSON Evaluation - Basic Example (JSON Schema)\n");
60    
61    if enable_comparison {
62        println!("🔍 Comparison: enabled");
63    }
64    if show_timing {
65        println!("⏱️  Internal timing: enabled");
66    }
67    if enable_comparison || show_timing {
68        println!();
69    }
70    
71    let samples_dir = Path::new("samples");
72    let mut scenarios = common::discover_scenarios(samples_dir);
73    
74    // Filter out MessagePack scenarios - only use JSON
75    scenarios.retain(|s| !s.is_msgpack);
76    
77    // Filter scenarios if a filter is provided
78    if let Some(ref filter) = scenario_filter {
79        scenarios.retain(|s| s.name.contains(filter));
80        println!("📋 Filtering scenarios matching: '{}'\n", filter);
81    }
82
83    if scenarios.is_empty() {
84        if let Some(filter) = scenario_filter {
85            println!(
86                "ℹ️  No scenarios found matching '{}' in `{}`.",
87                filter,
88                samples_dir.display()
89            );
90        } else {
91            println!(
92                "ℹ️  No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
93                samples_dir.display()
94            );
95        }
96        return;
97    }
98    
99    println!("📊 Found {} scenario(s)\n", scenarios.len());
100
101    let mut total_parse_time = std::time::Duration::ZERO;
102    let mut total_eval_time = std::time::Duration::ZERO;
103    let mut successful_scenarios = 0;
104    let mut comparison_failures = 0;
105
106    for scenario in &scenarios {
107        println!("==============================");
108        println!("Scenario: {}", scenario.name);
109        println!("Schema: {} ({})", 
110            scenario.schema_path.display(),
111            if scenario.is_msgpack { "MessagePack" } else { "JSON" }
112        );
113        println!("Data: {}\n", scenario.data_path.display());
114
115        // Clear timing data from previous scenarios
116        if show_timing {
117            json_eval_rs::enable_timing();
118            json_eval_rs::clear_timing_data();
119        }
120
121        let data_str = fs::read_to_string(&scenario.data_path)
122            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
123
124        // Step 1: Parse schema (JSONEval::new)
125        let parse_start = Instant::now();
126        
127        let schema_str = fs::read_to_string(&scenario.schema_path)
128            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
129        
130        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str))
131            .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e));
132        
133        let parse_time = parse_start.elapsed();
134        println!("  📝 Parse (new): {:?}", parse_time);
135        
136        // Step 2: Evaluate
137        let eval_start = Instant::now();
138        
139        eval.evaluate(&data_str, Some("{}"), None, None)
140            .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
141
142        // Step 3: Validate
143        let validation_start = Instant::now();
144        let validation_result = eval.validate(&data_str, None, None, None)
145            .unwrap_or_else(|e| panic!("validation failed: {}", e));
146        let validation_time = validation_start.elapsed();
147        println!("  🛡️ Validate: {:?}", validation_time);
148        
149        // Legacy behavior: get_evaluated_schema takes skip_layout: bool
150        // We pass false to ensure layout IS resolved
151        let evaluated_schema = eval.get_evaluated_schema(false);
152        let schema_value = eval.get_schema_value();
153        let eval_time = eval_start.elapsed();
154        
155        println!("  ⚡ Eval: {:?}", eval_time);
156        println!("  ⏱️  Total: {:?}\n", parse_time + eval_time);
157        
158        // Print detailed timing breakdown if --timing flag is set
159        if show_timing {
160            json_eval_rs::print_timing_summary();
161        }
162        
163        total_parse_time += parse_time;
164        total_eval_time += eval_time;
165        successful_scenarios += 1;
166
167        // Save results
168        let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
169        let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
170        let value_path = samples_dir.join(format!("{}-schema-value.json", scenario.name));
171        let validation_path = samples_dir.join(format!("{}-validation.json", scenario.name));
172
173        fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
174            .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
175
176        let mut metadata_obj = Map::new();
177        metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
178        metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
179        metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
180
181        fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
182            .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
183
184        fs::write(&value_path, common::pretty_json(&schema_value))
185            .unwrap_or_else(|e| panic!("failed to write {}: {}", value_path.display(), e));
186
187        let validation_value = serde_json::to_value(&validation_result)
188            .unwrap_or_else(|e| panic!("failed to serialize validation result: {}", e));
189        fs::write(&validation_path, common::pretty_json(&validation_value))
190            .unwrap_or_else(|e| panic!("failed to write {}: {}", validation_path.display(), e));
191
192        println!("✅ Results saved:");
193        println!("  - {}", evaluated_path.display());
194        println!("  - {}", parsed_path.display());
195        println!("  - {}", value_path.display());
196        println!("  - {}\n", validation_path.display());
197
198        // Optional comparison
199        if enable_comparison {
200            if let Some(comp_path) = &scenario.comparison_path {
201                if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
202                    comparison_failures += 1;
203                }
204                println!();
205            }
206        }
207    }
208    
209    // Print summary
210    println!("{}", "=".repeat(50));
211    println!("📊 Summary");
212    println!("{}", "=".repeat(50));
213    println!("Total scenarios run: {}", successful_scenarios);
214    println!("Total parse time: {:?}", total_parse_time);
215    println!("Total eval time: {:?}", total_eval_time);
216    println!("Total time: {:?}", total_parse_time + total_eval_time);
217    
218    if successful_scenarios > 1 {
219        println!("\nAverage per scenario:");
220        println!("  Parse: {:?}", total_parse_time / successful_scenarios as u32);
221        println!("  Eval: {:?}", total_eval_time / successful_scenarios as u32);
222    }
223    
224    if enable_comparison {
225        println!("Comparison failures: {}", comparison_failures);
226    }
227    
228    println!("\n✅ All scenarios completed!\n");
229}
examples/benchmark.rs (line 240)
32fn main() {
33    let args: Vec<String> = std::env::args().collect();
34    let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("benchmark");
35    
36    let mut iterations = 1usize;
37    let mut scenario_filter: Option<String> = None;
38    let mut show_cpu_info = false;
39    let mut use_parsed_schema = false;
40    let mut use_cache = false;
41    let mut concurrent_count: Option<usize> = None;
42    let mut enable_comparison = false;
43    let mut show_timing = false;
44    let mut i = 1;
45    
46    // Parse arguments
47    while i < args.len() {
48        let arg = &args[i];
49        
50        if arg == "-h" || arg == "--help" {
51            print_help(program_name);
52            return;
53        } else if arg == "--cpu-info" {
54            show_cpu_info = true;
55        } else if arg == "--parsed" {
56            use_parsed_schema = true;
57        } else if arg == "--cache" {
58            use_cache = true;
59        } else if arg == "--compare" {
60            enable_comparison = true;
61        } else if arg == "--timing" {
62            show_timing = true;
63        } else if arg == "--concurrent" {
64            if i + 1 >= args.len() {
65                eprintln!("Error: {} requires a value", arg);
66                print_help(program_name);
67                return;
68            }
69            i += 1;
70            match args[i].parse::<usize>() {
71                Ok(n) if n > 0 => concurrent_count = Some(n),
72                _ => {
73                    eprintln!("Error: concurrent count must be a positive integer, got '{}'", args[i]);
74                    return;
75                }
76            }
77        } else if arg == "-i" || arg == "--iterations" {
78            if i + 1 >= args.len() {
79                eprintln!("Error: {} requires a value", arg);
80                print_help(program_name);
81                return;
82            }
83            i += 1;
84            match args[i].parse::<usize>() {
85                Ok(n) if n > 0 => iterations = n,
86                _ => {
87                    eprintln!("Error: iterations must be a positive integer, got '{}'", args[i]);
88                    return;
89                }
90            }
91        } else if !arg.starts_with('-') {
92            scenario_filter = Some(arg.clone());
93        } else {
94            eprintln!("Error: unknown option '{}'", arg);
95            print_help(program_name);
96            return;
97        }
98        
99        i += 1;
100    }
101    
102    println!("\n🚀 JSON Evaluation - Benchmark\n");
103    
104    // Show CPU info if requested or if running benchmarks
105    if show_cpu_info || iterations > 1 || concurrent_count.is_some() {
106        common::print_cpu_info();
107    }
108    
109    if use_parsed_schema {
110        println!("📦 Mode: ParsedSchema (parse once, reuse for all iterations)\n");
111    }
112    
113    if use_cache {
114        println!("♻️  Mode: Cache (reuse JSONEval instance across iterations)\n");
115    }
116    
117    if let Some(count) = concurrent_count {
118        println!("🔀 Concurrent evaluations: {} threads\n", count);
119    } else if iterations > 1 {
120        println!("🔄 Iterations per scenario: {}\n", iterations);
121    }
122    
123    if enable_comparison {
124        println!("🔍 Comparison: enabled");
125    }
126    if show_timing {
127        println!("⏱️  Internal timing: enabled");
128    }
129    if enable_comparison || show_timing {
130        println!();
131    }
132
133    let samples_dir = Path::new("samples");
134    let mut scenarios = common::discover_scenarios(samples_dir);
135    
136    // Filter scenarios if a filter is provided
137    if let Some(ref filter) = scenario_filter {
138        scenarios.retain(|s| s.name.contains(filter));
139        println!("📋 Filtering scenarios matching: '{}'\n", filter);
140    }
141
142    if scenarios.is_empty() {
143        if let Some(filter) = scenario_filter {
144            println!(
145                "ℹ️  No scenarios found matching '{}' in `{}`.",
146                filter,
147                samples_dir.display()
148            );
149        } else {
150            println!(
151                "ℹ️  No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
152                samples_dir.display()
153            );
154        }
155        return;
156    }
157    
158    println!("📊 Found {} scenario(s)\n", scenarios.len());
159
160    let mut total_parse_time = std::time::Duration::ZERO;
161    let mut total_eval_time = std::time::Duration::ZERO;
162    let mut successful_scenarios = 0;
163    let mut comparison_failures = 0;
164
165    for scenario in &scenarios {
166        println!("==============================");
167        println!("Scenario: {}", scenario.name);
168        println!("Schema: {} ({})", 
169            scenario.schema_path.display(),
170            if scenario.is_msgpack { "MessagePack" } else { "JSON" }
171        );
172        println!("Data: {}\n", scenario.data_path.display());
173
174        // Clear timing data from previous scenarios
175        if show_timing {
176            json_eval_rs::enable_timing();
177            json_eval_rs::clear_timing_data();
178        }
179
180        let data_str = fs::read_to_string(&scenario.data_path)
181            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
182
183        println!("Running evaluation...\n");
184
185        let (parse_time, eval_time, evaluated_schema, eval, iteration_times) = if use_parsed_schema {
186            // ParsedSchema mode: parse once, reuse for all iterations/threads
187            let start_time = Instant::now();
188            
189            let parsed_schema = if scenario.is_msgpack {
190                let schema_msgpack = fs::read(&scenario.schema_path)
191                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
192                println!("  📦 MessagePack schema size: {} bytes", schema_msgpack.len());
193                Arc::new(ParsedSchema::parse_msgpack(&schema_msgpack)
194                    .unwrap_or_else(|e| panic!("failed to parse MessagePack schema: {}", e)))
195            } else {
196                let schema_str = fs::read_to_string(&scenario.schema_path)
197                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
198                Arc::new(ParsedSchema::parse(&schema_str)
199                    .unwrap_or_else(|e| panic!("failed to parse schema: {}", e)))
200            };
201            
202            let parse_time = start_time.elapsed();
203            println!("  Schema parsing & compilation: {:?}", parse_time);
204            
205            // Concurrent mode with ParsedSchema
206            if let Some(thread_count) = concurrent_count {
207                use std::thread;
208                
209                let eval_start = Instant::now();
210                let mut handles = vec![];
211                
212                for thread_id in 0..thread_count {
213                    let parsed_clone = parsed_schema.clone();
214                    let data_str_clone = data_str.clone();
215                    let iter_count = iterations;
216                    let thread_use_cache = use_cache;
217                    
218                    let handle = thread::spawn(move || {
219                        let mut thread_times = Vec::with_capacity(iter_count);
220                        let mut last_schema = Value::Null;
221                        
222                        let mut eval_instance = JSONEval::with_parsed_schema(
223                            parsed_clone.clone(),
224                            Some("{}"),
225                            Some(&data_str_clone)
226                        ).unwrap();
227                        
228                        for iter in 0..iter_count {
229                            let iter_start = Instant::now();
230                            
231                            if !thread_use_cache && iter > 0 {
232                                eval_instance = JSONEval::with_parsed_schema(
233                                    parsed_clone.clone(),
234                                    Some("{}"),
235                                    Some(&data_str_clone)
236                                ).unwrap();
237                            }
238                            
239                            eval_instance.evaluate(&data_str_clone, Some("{}"), None, None).unwrap();
240                            last_schema = eval_instance.get_evaluated_schema(false);
241                            thread_times.push(iter_start.elapsed());
242                        }
243                        
244                        (thread_times, last_schema, thread_id)
245                    });
246                    handles.push(handle);
247                }
248                
249                let mut all_iteration_times = Vec::new();
250                let mut evaluated_schema = Value::Null;
251                
252                for handle in handles {
253                    let (thread_times, thread_schema, thread_id) = handle.join().unwrap();
254                    println!("  Thread {} completed {} iterations", thread_id, thread_times.len());
255                    all_iteration_times.extend(thread_times);
256                    evaluated_schema = thread_schema; // Use last thread's result
257                }
258                
259                let eval_time = eval_start.elapsed();
260                
261                // Create a temp eval for metadata export
262                let temp_eval = JSONEval::with_parsed_schema(
263                    parsed_schema.clone(),
264                    Some("{}"),
265                    Some(&data_str)
266                ).unwrap();
267                
268                (parse_time, eval_time, evaluated_schema, temp_eval, all_iteration_times)
269            } else {
270                // Sequential iterations with ParsedSchema
271                let eval_start = Instant::now();
272                let mut evaluated_schema = Value::Null;
273                let mut iteration_times = Vec::with_capacity(iterations);
274                let mut eval_instance = JSONEval::with_parsed_schema(
275                    parsed_schema.clone(),
276                    Some("{}"),
277                    Some(&data_str)
278                ).unwrap();
279                
280                for iter in 0..iterations {
281                    let iter_start = Instant::now();
282                    
283                    if !use_cache && iter > 0 {
284                        eval_instance = JSONEval::with_parsed_schema(
285                            parsed_schema.clone(),
286                            Some("{}"),
287                            Some(&data_str)
288                        ).unwrap();
289                    }
290                    
291                    eval_instance.evaluate(&data_str, Some("{}"), None, None)
292                        .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
293                    evaluated_schema = eval_instance.get_evaluated_schema(false);
294                    iteration_times.push(iter_start.elapsed());
295                    
296                    if iterations > 1 && (iter + 1) % 10 == 0 {
297                        print!(".");
298                        if (iter + 1) % 50 == 0 {
299                            println!(" {}/{}", iter + 1, iterations);
300                        }
301                    }
302                }
303                
304                if iterations > 1 && iterations % 50 != 0 {
305                    println!(" {}/{}", iterations, iterations);
306                }
307                
308                let eval_time = eval_start.elapsed();
309                (parse_time, eval_time, evaluated_schema, eval_instance, iteration_times)
310            }
311        } else {
312            // Traditional mode: parse and create JSONEval each time
313            let schema_msgpack = if scenario.is_msgpack {
314                let bytes = fs::read(&scenario.schema_path)
315                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
316                println!("  📦 MessagePack schema size: {} bytes", bytes.len());
317                Some(bytes)
318            } else {
319                None
320            };
321            
322            let schema_str = if !scenario.is_msgpack {
323                Some(fs::read_to_string(&scenario.schema_path)
324                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e)))
325            } else {
326                None
327            };
328
329            let start_time = Instant::now();
330            let mut eval = if scenario.is_msgpack {
331                JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
332                    .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
333            } else {
334                JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
335                    .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
336            };
337            let parse_time = start_time.elapsed();
338            println!("  Schema parsing & compilation: {:?}", parse_time);
339            
340            let eval_start = Instant::now();
341            let mut evaluated_schema = Value::Null;
342            let mut iteration_times = Vec::with_capacity(iterations);
343            
344            for iter in 0..iterations {
345                let iter_start = Instant::now();
346                
347                if !use_cache && iter > 0 {
348                    eval = if scenario.is_msgpack {
349                        JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
350                            .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
351                    } else {
352                        JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
353                            .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
354                    };
355                }
356                
357                eval.evaluate(&data_str, Some("{}"), None, None)
358                    .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
359                evaluated_schema = eval.get_evaluated_schema(false);
360                iteration_times.push(iter_start.elapsed());
361                
362                if iterations > 1 && (iter + 1) % 10 == 0 {
363                    print!(".");
364                    if (iter + 1) % 50 == 0 {
365                        println!(" {}/{}", iter + 1, iterations);
366                    }
367                }
368            }
369            
370            if iterations > 1 && iterations % 50 != 0 {
371                println!(" {}/{}", iterations, iterations);
372            }
373            
374            let eval_time = eval_start.elapsed();
375            (parse_time, eval_time, evaluated_schema, eval, iteration_times)
376        };
377        
378        // Calculate statistics
379        let total_iterations = iteration_times.len();
380        if total_iterations == 1 {
381            println!("  Evaluation: {:?}", eval_time);
382        } else {
383            let avg_time = eval_time / total_iterations as u32;
384            let min_time = iteration_times.iter().min().unwrap();
385            let max_time = iteration_times.iter().max().unwrap();
386            
387            println!("  Total evaluation time: {:?}", eval_time);
388            println!("  Total iterations: {}", total_iterations);
389            println!("  Average per iteration: {:?}", avg_time);
390            println!("  Min: {:?} | Max: {:?}", min_time, max_time);
391            
392            // Show cache statistics
393            let cache_stats = eval.cache_stats();
394            println!("  Cache: {} entries, {} hits, {} misses ({:.1}% hit rate)",
395                cache_stats.entries,
396                cache_stats.hits,
397                cache_stats.misses,
398                cache_stats.hit_rate * 100.0
399            );
400        }
401
402        let total_time = parse_time + eval_time;
403        println!("⏱️  Execution time: {:?}\n", total_time);
404        
405        // Print detailed timing breakdown if --timing flag is set
406        if show_timing {
407            json_eval_rs::print_timing_summary();
408        }
409        
410        // Track statistics
411        total_parse_time += parse_time;
412        total_eval_time += eval_time;
413        successful_scenarios += 1;
414
415        let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
416        let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
417
418        fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
419            .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
420
421        let mut metadata_obj = Map::new();
422        metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
423        metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
424
425        fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
426            .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
427
428        println!("✅ Results saved:");
429        println!("  - {}", evaluated_path.display());
430        println!("  - {}\n", parsed_path.display());
431
432        // Optional comparison
433        if enable_comparison {
434            if let Some(comp_path) = &scenario.comparison_path {
435                if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
436                    comparison_failures += 1;
437                }
438                println!();
439            }
440        }
441    }
442    
443    // Print summary statistics
444    if successful_scenarios > 0 {
445        println!("\n{}", "=".repeat(50));
446        println!("📊 Summary Statistics");
447        println!("{}", "=".repeat(50));
448        println!("Total scenarios run: {}", successful_scenarios);
449        println!("Total parsing time: {:?}", total_parse_time);
450        println!("Total evaluation time: {:?}", total_eval_time);
451        println!("Total time: {:?}", total_parse_time + total_eval_time);
452        
453        if successful_scenarios > 1 {
454            println!("\nAverage per scenario:");
455            println!("  Parsing: {:?}", total_parse_time / successful_scenarios as u32);
456            println!("  Evaluation: {:?}", total_eval_time / successful_scenarios as u32);
457        }
458        
459        if enable_comparison {
460            println!("\nComparison failures: {}", comparison_failures);
461        }
462        
463        println!("\n✅ All scenarios completed successfully!\n");
464    }
465}
Source

pub fn get_schema_value_by_path(&self, path: &str) -> Option<Value>

Get specific schema value by path

Source

pub fn get_schema_value(&mut self) -> Value

Get all schema values (data view) Mutates internal data state by overriding with values from value evaluations This corresponds to subform.get_schema_value() usage

Examples found in repository?
examples/basic.rs (line 152)
28fn main() {
29    let args: Vec<String> = std::env::args().collect();
30    let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic");
31    
32    let mut scenario_filter: Option<String> = None;
33    let mut enable_comparison = false;
34    let mut show_timing = false;
35    let mut i = 1;
36    
37    // Parse arguments
38    while i < args.len() {
39        let arg = &args[i];
40        
41        if arg == "-h" || arg == "--help" {
42            print_help(program_name);
43            return;
44        } else if arg == "--compare" {
45            enable_comparison = true;
46        } else if arg == "--timing" {
47            show_timing = true;
48        } else if !arg.starts_with('-') {
49            scenario_filter = Some(arg.clone());
50        } else {
51            eprintln!("Error: unknown option '{}'", arg);
52            print_help(program_name);
53            return;
54        }
55        
56        i += 1;
57    }
58    
59    println!("\n🚀 JSON Evaluation - Basic Example (JSON Schema)\n");
60    
61    if enable_comparison {
62        println!("🔍 Comparison: enabled");
63    }
64    if show_timing {
65        println!("⏱️  Internal timing: enabled");
66    }
67    if enable_comparison || show_timing {
68        println!();
69    }
70    
71    let samples_dir = Path::new("samples");
72    let mut scenarios = common::discover_scenarios(samples_dir);
73    
74    // Filter out MessagePack scenarios - only use JSON
75    scenarios.retain(|s| !s.is_msgpack);
76    
77    // Filter scenarios if a filter is provided
78    if let Some(ref filter) = scenario_filter {
79        scenarios.retain(|s| s.name.contains(filter));
80        println!("📋 Filtering scenarios matching: '{}'\n", filter);
81    }
82
83    if scenarios.is_empty() {
84        if let Some(filter) = scenario_filter {
85            println!(
86                "ℹ️  No scenarios found matching '{}' in `{}`.",
87                filter,
88                samples_dir.display()
89            );
90        } else {
91            println!(
92                "ℹ️  No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
93                samples_dir.display()
94            );
95        }
96        return;
97    }
98    
99    println!("📊 Found {} scenario(s)\n", scenarios.len());
100
101    let mut total_parse_time = std::time::Duration::ZERO;
102    let mut total_eval_time = std::time::Duration::ZERO;
103    let mut successful_scenarios = 0;
104    let mut comparison_failures = 0;
105
106    for scenario in &scenarios {
107        println!("==============================");
108        println!("Scenario: {}", scenario.name);
109        println!("Schema: {} ({})", 
110            scenario.schema_path.display(),
111            if scenario.is_msgpack { "MessagePack" } else { "JSON" }
112        );
113        println!("Data: {}\n", scenario.data_path.display());
114
115        // Clear timing data from previous scenarios
116        if show_timing {
117            json_eval_rs::enable_timing();
118            json_eval_rs::clear_timing_data();
119        }
120
121        let data_str = fs::read_to_string(&scenario.data_path)
122            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
123
124        // Step 1: Parse schema (JSONEval::new)
125        let parse_start = Instant::now();
126        
127        let schema_str = fs::read_to_string(&scenario.schema_path)
128            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
129        
130        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str))
131            .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e));
132        
133        let parse_time = parse_start.elapsed();
134        println!("  📝 Parse (new): {:?}", parse_time);
135        
136        // Step 2: Evaluate
137        let eval_start = Instant::now();
138        
139        eval.evaluate(&data_str, Some("{}"), None, None)
140            .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
141
142        // Step 3: Validate
143        let validation_start = Instant::now();
144        let validation_result = eval.validate(&data_str, None, None, None)
145            .unwrap_or_else(|e| panic!("validation failed: {}", e));
146        let validation_time = validation_start.elapsed();
147        println!("  🛡️ Validate: {:?}", validation_time);
148        
149        // Legacy behavior: get_evaluated_schema takes skip_layout: bool
150        // We pass false to ensure layout IS resolved
151        let evaluated_schema = eval.get_evaluated_schema(false);
152        let schema_value = eval.get_schema_value();
153        let eval_time = eval_start.elapsed();
154        
155        println!("  ⚡ Eval: {:?}", eval_time);
156        println!("  ⏱️  Total: {:?}\n", parse_time + eval_time);
157        
158        // Print detailed timing breakdown if --timing flag is set
159        if show_timing {
160            json_eval_rs::print_timing_summary();
161        }
162        
163        total_parse_time += parse_time;
164        total_eval_time += eval_time;
165        successful_scenarios += 1;
166
167        // Save results
168        let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
169        let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
170        let value_path = samples_dir.join(format!("{}-schema-value.json", scenario.name));
171        let validation_path = samples_dir.join(format!("{}-validation.json", scenario.name));
172
173        fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
174            .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
175
176        let mut metadata_obj = Map::new();
177        metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
178        metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
179        metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
180
181        fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
182            .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
183
184        fs::write(&value_path, common::pretty_json(&schema_value))
185            .unwrap_or_else(|e| panic!("failed to write {}: {}", value_path.display(), e));
186
187        let validation_value = serde_json::to_value(&validation_result)
188            .unwrap_or_else(|e| panic!("failed to serialize validation result: {}", e));
189        fs::write(&validation_path, common::pretty_json(&validation_value))
190            .unwrap_or_else(|e| panic!("failed to write {}: {}", validation_path.display(), e));
191
192        println!("✅ Results saved:");
193        println!("  - {}", evaluated_path.display());
194        println!("  - {}", parsed_path.display());
195        println!("  - {}", value_path.display());
196        println!("  - {}\n", validation_path.display());
197
198        // Optional comparison
199        if enable_comparison {
200            if let Some(comp_path) = &scenario.comparison_path {
201                if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
202                    comparison_failures += 1;
203                }
204                println!();
205            }
206        }
207    }
208    
209    // Print summary
210    println!("{}", "=".repeat(50));
211    println!("📊 Summary");
212    println!("{}", "=".repeat(50));
213    println!("Total scenarios run: {}", successful_scenarios);
214    println!("Total parse time: {:?}", total_parse_time);
215    println!("Total eval time: {:?}", total_eval_time);
216    println!("Total time: {:?}", total_parse_time + total_eval_time);
217    
218    if successful_scenarios > 1 {
219        println!("\nAverage per scenario:");
220        println!("  Parse: {:?}", total_parse_time / successful_scenarios as u32);
221        println!("  Eval: {:?}", total_eval_time / successful_scenarios as u32);
222    }
223    
224    if enable_comparison {
225        println!("Comparison failures: {}", comparison_failures);
226    }
227    
228    println!("\n✅ All scenarios completed!\n");
229}
Source

pub fn get_schema_value_array(&self) -> Value

Get all schema values as array of path-value pairs Returns [{path: “”, value: “”}, …]

§Returns

Array of objects containing path (dotted notation) and value pairs from value evaluations

Source

pub fn get_schema_value_object(&self) -> Value

Get all schema values as object with dotted path keys Returns {path: value, …}

§Returns

Flat object with dotted notation paths as keys and evaluated values

Source

pub fn get_evaluated_schema_without_params( &mut self, skip_layout: bool, ) -> Value

Get evaluated schema without $params

Source

pub fn get_evaluated_schema_msgpack( &mut self, skip_layout: bool, ) -> Result<Vec<u8>, String>

Get evaluated schema as MessagePack bytes

Source

pub fn get_evaluated_schema_by_path( &mut self, path: &str, skip_layout: bool, ) -> Option<Value>

Get value from evaluated schema by path

Source

pub fn get_evaluated_schema_by_paths( &mut self, paths: &[String], skip_layout: bool, format: Option<ReturnFormat>, ) -> Value

Get evaluated schema parts by multiple paths

Source

pub fn get_schema_by_path(&self, path: &str) -> Option<Value>

Get original (unevaluated) schema by path

Source

pub fn get_schema_by_paths( &self, paths: &[String], format: Option<ReturnFormat>, ) -> Value

Get original schema by multiple paths

Source

pub fn flatten_object( prefix: &str, value: &Value, result: &mut Map<String, Value>, )

Flatten a nested object key-value pair to dotted keys

Source

pub fn convert_to_format(value: Value, format: ReturnFormat) -> Value

Source§

impl JSONEval

Source

pub fn new( schema: &str, context: Option<&str>, data: Option<&str>, ) -> Result<Self, Error>

Examples found in repository?
examples/cache_demo.rs (line 173)
148fn demo_performance_comparison() -> Result<(), Box<dyn std::error::Error>> {
149    println!("⚡ Example 3: Performance Comparison");
150    println!("Comparing cached vs non-cached schema usage...\n");
151    
152    let schema_json = r#"{
153        "$params": {
154            "value": { "type": "number" }
155        },
156        "doubled": {
157            "type": "number",
158            "$evaluation": { "*": [{"var": "$value"}, 2] }
159        },
160        "tripled": {
161            "type": "number",
162            "$evaluation": { "*": [{"var": "$value"}, 3] }
163        }
164    }"#;
165    
166    let iterations = 100;
167    
168    // WITHOUT CACHE: Parse schema every time
169    println!("🐌 Without cache (parse + evaluate each time):");
170    let start = Instant::now();
171    for i in 0..iterations {
172        let context = format!(r#"{{"value": {}}}"#, i);
173        let mut eval = JSONEval::new(schema_json, Some(&context), None)?;
174        eval.evaluate("{}", None, None, None)?;
175    }
176    let without_cache = start.elapsed();
177    println!("   Time: {:?}", without_cache);
178    println!("   Avg per iteration: {:?}\n", without_cache / iterations);
179    
180    // WITH CACHE: Parse once, evaluate many times
181    println!("🚀 With cache (parse once, reuse for all evaluations):");
182    let cache = ParsedSchemaCache::new();
183    
184    // Parse once
185    let parse_start = Instant::now();
186    let parsed = ParsedSchema::parse(schema_json)?;
187    cache.insert("perf-test".to_string(), Arc::new(parsed));
188    let parse_time = parse_start.elapsed();
189    
190    // Evaluate many times
191    let eval_start = Instant::now();
192    for i in 0..iterations {
193        if let Some(cached) = cache.get("perf-test") {
194            let context = format!(r#"{{"value": {}}}"#, i);
195            let mut eval = JSONEval::with_parsed_schema(cached.clone(), Some(&context), None)?;
196            eval.evaluate("{}", None, None, None)?;
197        }
198    }
199    let eval_time = eval_start.elapsed();
200    let with_cache = parse_time + eval_time;
201    
202    println!("   Parse time: {:?}", parse_time);
203    println!("   Eval time: {:?}", eval_time);
204    println!("   Total time: {:?}", with_cache);
205    println!("   Avg per iteration: {:?}\n", eval_time / iterations);
206    
207    let speedup = without_cache.as_secs_f64() / with_cache.as_secs_f64();
208    println!("📈 Speedup: {:.2}x faster", speedup);
209    
210    Ok(())
211}
More examples
Hide additional examples
examples/cache_disable.rs (line 43)
4fn main() {
5    let schema = json!({
6        "type": "object",
7        "properties": {
8            "price": {
9                "type": "number"
10            },
11            "tax": {
12                "type": "number",
13                "value": {
14                    "$evaluation": {
15                        "*": [
16                            { "$ref": "#/properties/price" },
17                            0.1
18                        ]
19                    }
20                }
21            },
22            "total": {
23                "type": "number",
24                "value": {
25                    "$evaluation": {
26                        "+": [
27                            { "$ref": "#/properties/price" },
28                            { "$ref": "#/properties/tax" }
29                        ]
30                    }
31                }
32            }
33        }
34    });
35
36    let schema_str = serde_json::to_string(&schema).unwrap();
37    
38    println!("=== Example 1: With Caching (Default) ===");
39    {
40        let data = json!({ "price": 100 });
41        let data_str = serde_json::to_string(&data).unwrap();
42        
43        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
44        
45        println!("Cache enabled: {}", eval.is_cache_enabled());
46        println!("Initial cache size: {}", eval.cache_len());
47        
48        eval.evaluate(&data_str, None, None, None).unwrap();
49        
50        println!("After evaluation cache size: {}", eval.cache_len());
51        let stats = eval.cache_stats();
52        println!("Cache stats: {}", stats);
53    }
54    
55    println!("\n=== Example 2: Without Caching (Web API Mode) ===");
56    {
57        let data = json!({ "price": 200 });
58        let data_str = serde_json::to_string(&data).unwrap();
59        
60        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
61        
62        // Disable caching for single-use web API scenario
63        eval.disable_cache();
64        
65        println!("Cache enabled: {}", eval.is_cache_enabled());
66        println!("Initial cache size: {}", eval.cache_len());
67        
68        eval.evaluate(&data_str, None, None, None).unwrap();
69        
70        println!("After evaluation cache size: {}", eval.cache_len());
71        let stats = eval.cache_stats();
72        println!("Cache stats: {}", stats);
73        
74        println!("\n✅ No cache overhead - perfect for web APIs!");
75    }
76    
77    println!("\n=== Example 3: Re-enabling Cache ===");
78    {
79        let data = json!({ "price": 300 });
80        let data_str = serde_json::to_string(&data).unwrap();
81        
82        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
83        
84        // Disable then re-enable
85        eval.disable_cache();
86        eval.enable_cache();
87        
88        println!("Cache enabled: {}", eval.is_cache_enabled());
89        eval.evaluate(&data_str, None, None, None).unwrap();
90        
91        println!("Cache size after evaluation: {}", eval.cache_len());
92        println!("\n✅ Cache can be toggled as needed!");
93    }
94}
examples/spaj_toggle.rs (line 26)
6fn main() {
7    println!("\n🚀 JSON Evaluation - SPAJ Toggle Example\n");
8
9    let schema_path = Path::new("samples/spaj.json");
10    let schema_str = fs::read_to_string(schema_path).expect("Failed to read schema");
11
12    // Initial data with minimal context required
13    let context_str = json!({
14        "agentProfile": { "sob": "AG" } 
15    }).to_string();
16
17    let initial_data = json!({
18        "illustration": {
19            "basicinformation": {
20                "print_polflag": false
21            }
22        }
23    }).to_string();
24
25    // Initialize logic
26    let mut eval = JSONEval::new(&schema_str, Some(&context_str), Some(&initial_data))
27        .expect("Failed to create JSONEval");
28
29    // Helper to check visibility
30    let check_visibility = |eval: &mut JSONEval, expected_hidden: bool, step: &str| {
31        let result = eval.get_evaluated_schema(false);
32        let hidden = result.pointer("/illustration/properties/basicinformation/properties/print_poladdress/condition/hidden")
33            .and_then(|v| v.as_bool());
34        
35        match hidden {
36            Some(val) => {
37                if val == expected_hidden {
38                    println!("✅ {}: Hidden = {} (Expected: {})", step, val, expected_hidden);
39                } else {
40                    println!("❌ {}: Hidden = {} (Expected: {})", step, val, expected_hidden);
41                }
42            },
43            None => println!("❌ {}: 'hidden' property not found", step),
44        }
45    };
46
47    // Step 1: Initial state (false)
48    println!("Step 1: Initial State (print_polflag: false)");
49    eval.evaluate(&initial_data, Some(&context_str), None, None).expect("Evaluation failed");
50    check_visibility(&mut eval, true, "Initial check");
51
52    // Step 2: Toggle to true
53    println!("\nStep 2: Toggle True (print_polflag: true)");
54    let data_true = json!({
55        "illustration": {
56            "basicinformation": {
57                "print_polflag": true
58            }
59        }
60    }).to_string();
61    eval.evaluate(&data_true, Some(&context_str), None, None).expect("Evaluation failed");
62    check_visibility(&mut eval, false, "Toggle ON check");
63
64    // Step 3: Toggle back to false
65    println!("\nStep 3: Toggle False (print_polflag: false)");
66    let data_false = json!({
67        "illustration": {
68            "basicinformation": {
69                "print_polflag": false
70            }
71        }
72    }).to_string();
73    eval.evaluate(&data_false, Some(&context_str), None, None).expect("Evaluation failed");
74    
75    let hidden_path = "#/illustration/properties/basicinformation/properties/print_poladdress/condition/hidden";
76    if let Some(deps) = eval.dependencies.get(hidden_path) {
77        println!("Debug: Dependencies for hidden: {:?}", deps);
78    } else {
79        println!("Debug: No dependencies found for hidden path");
80    }
81
82    // Debug: Print current flag value
83    if let Some(val) = eval.get_evaluated_schema(false).pointer("/illustration/properties/basicinformation/properties/print_polflag/value") {
84         println!("Debug: print_polflag value is: {}", val);
85    }
86
87    check_visibility(&mut eval, true, "Toggle OFF check");
88}
examples/basic.rs (line 130)
28fn main() {
29    let args: Vec<String> = std::env::args().collect();
30    let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic");
31    
32    let mut scenario_filter: Option<String> = None;
33    let mut enable_comparison = false;
34    let mut show_timing = false;
35    let mut i = 1;
36    
37    // Parse arguments
38    while i < args.len() {
39        let arg = &args[i];
40        
41        if arg == "-h" || arg == "--help" {
42            print_help(program_name);
43            return;
44        } else if arg == "--compare" {
45            enable_comparison = true;
46        } else if arg == "--timing" {
47            show_timing = true;
48        } else if !arg.starts_with('-') {
49            scenario_filter = Some(arg.clone());
50        } else {
51            eprintln!("Error: unknown option '{}'", arg);
52            print_help(program_name);
53            return;
54        }
55        
56        i += 1;
57    }
58    
59    println!("\n🚀 JSON Evaluation - Basic Example (JSON Schema)\n");
60    
61    if enable_comparison {
62        println!("🔍 Comparison: enabled");
63    }
64    if show_timing {
65        println!("⏱️  Internal timing: enabled");
66    }
67    if enable_comparison || show_timing {
68        println!();
69    }
70    
71    let samples_dir = Path::new("samples");
72    let mut scenarios = common::discover_scenarios(samples_dir);
73    
74    // Filter out MessagePack scenarios - only use JSON
75    scenarios.retain(|s| !s.is_msgpack);
76    
77    // Filter scenarios if a filter is provided
78    if let Some(ref filter) = scenario_filter {
79        scenarios.retain(|s| s.name.contains(filter));
80        println!("📋 Filtering scenarios matching: '{}'\n", filter);
81    }
82
83    if scenarios.is_empty() {
84        if let Some(filter) = scenario_filter {
85            println!(
86                "ℹ️  No scenarios found matching '{}' in `{}`.",
87                filter,
88                samples_dir.display()
89            );
90        } else {
91            println!(
92                "ℹ️  No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
93                samples_dir.display()
94            );
95        }
96        return;
97    }
98    
99    println!("📊 Found {} scenario(s)\n", scenarios.len());
100
101    let mut total_parse_time = std::time::Duration::ZERO;
102    let mut total_eval_time = std::time::Duration::ZERO;
103    let mut successful_scenarios = 0;
104    let mut comparison_failures = 0;
105
106    for scenario in &scenarios {
107        println!("==============================");
108        println!("Scenario: {}", scenario.name);
109        println!("Schema: {} ({})", 
110            scenario.schema_path.display(),
111            if scenario.is_msgpack { "MessagePack" } else { "JSON" }
112        );
113        println!("Data: {}\n", scenario.data_path.display());
114
115        // Clear timing data from previous scenarios
116        if show_timing {
117            json_eval_rs::enable_timing();
118            json_eval_rs::clear_timing_data();
119        }
120
121        let data_str = fs::read_to_string(&scenario.data_path)
122            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
123
124        // Step 1: Parse schema (JSONEval::new)
125        let parse_start = Instant::now();
126        
127        let schema_str = fs::read_to_string(&scenario.schema_path)
128            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
129        
130        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str))
131            .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e));
132        
133        let parse_time = parse_start.elapsed();
134        println!("  📝 Parse (new): {:?}", parse_time);
135        
136        // Step 2: Evaluate
137        let eval_start = Instant::now();
138        
139        eval.evaluate(&data_str, Some("{}"), None, None)
140            .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
141
142        // Step 3: Validate
143        let validation_start = Instant::now();
144        let validation_result = eval.validate(&data_str, None, None, None)
145            .unwrap_or_else(|e| panic!("validation failed: {}", e));
146        let validation_time = validation_start.elapsed();
147        println!("  🛡️ Validate: {:?}", validation_time);
148        
149        // Legacy behavior: get_evaluated_schema takes skip_layout: bool
150        // We pass false to ensure layout IS resolved
151        let evaluated_schema = eval.get_evaluated_schema(false);
152        let schema_value = eval.get_schema_value();
153        let eval_time = eval_start.elapsed();
154        
155        println!("  ⚡ Eval: {:?}", eval_time);
156        println!("  ⏱️  Total: {:?}\n", parse_time + eval_time);
157        
158        // Print detailed timing breakdown if --timing flag is set
159        if show_timing {
160            json_eval_rs::print_timing_summary();
161        }
162        
163        total_parse_time += parse_time;
164        total_eval_time += eval_time;
165        successful_scenarios += 1;
166
167        // Save results
168        let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
169        let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
170        let value_path = samples_dir.join(format!("{}-schema-value.json", scenario.name));
171        let validation_path = samples_dir.join(format!("{}-validation.json", scenario.name));
172
173        fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
174            .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
175
176        let mut metadata_obj = Map::new();
177        metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
178        metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
179        metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
180
181        fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
182            .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
183
184        fs::write(&value_path, common::pretty_json(&schema_value))
185            .unwrap_or_else(|e| panic!("failed to write {}: {}", value_path.display(), e));
186
187        let validation_value = serde_json::to_value(&validation_result)
188            .unwrap_or_else(|e| panic!("failed to serialize validation result: {}", e));
189        fs::write(&validation_path, common::pretty_json(&validation_value))
190            .unwrap_or_else(|e| panic!("failed to write {}: {}", validation_path.display(), e));
191
192        println!("✅ Results saved:");
193        println!("  - {}", evaluated_path.display());
194        println!("  - {}", parsed_path.display());
195        println!("  - {}", value_path.display());
196        println!("  - {}\n", validation_path.display());
197
198        // Optional comparison
199        if enable_comparison {
200            if let Some(comp_path) = &scenario.comparison_path {
201                if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
202                    comparison_failures += 1;
203                }
204                println!();
205            }
206        }
207    }
208    
209    // Print summary
210    println!("{}", "=".repeat(50));
211    println!("📊 Summary");
212    println!("{}", "=".repeat(50));
213    println!("Total scenarios run: {}", successful_scenarios);
214    println!("Total parse time: {:?}", total_parse_time);
215    println!("Total eval time: {:?}", total_eval_time);
216    println!("Total time: {:?}", total_parse_time + total_eval_time);
217    
218    if successful_scenarios > 1 {
219        println!("\nAverage per scenario:");
220        println!("  Parse: {:?}", total_parse_time / successful_scenarios as u32);
221        println!("  Eval: {:?}", total_eval_time / successful_scenarios as u32);
222    }
223    
224    if enable_comparison {
225        println!("Comparison failures: {}", comparison_failures);
226    }
227    
228    println!("\n✅ All scenarios completed!\n");
229}
examples/benchmark.rs (line 334)
32fn main() {
33    let args: Vec<String> = std::env::args().collect();
34    let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("benchmark");
35    
36    let mut iterations = 1usize;
37    let mut scenario_filter: Option<String> = None;
38    let mut show_cpu_info = false;
39    let mut use_parsed_schema = false;
40    let mut use_cache = false;
41    let mut concurrent_count: Option<usize> = None;
42    let mut enable_comparison = false;
43    let mut show_timing = false;
44    let mut i = 1;
45    
46    // Parse arguments
47    while i < args.len() {
48        let arg = &args[i];
49        
50        if arg == "-h" || arg == "--help" {
51            print_help(program_name);
52            return;
53        } else if arg == "--cpu-info" {
54            show_cpu_info = true;
55        } else if arg == "--parsed" {
56            use_parsed_schema = true;
57        } else if arg == "--cache" {
58            use_cache = true;
59        } else if arg == "--compare" {
60            enable_comparison = true;
61        } else if arg == "--timing" {
62            show_timing = true;
63        } else if arg == "--concurrent" {
64            if i + 1 >= args.len() {
65                eprintln!("Error: {} requires a value", arg);
66                print_help(program_name);
67                return;
68            }
69            i += 1;
70            match args[i].parse::<usize>() {
71                Ok(n) if n > 0 => concurrent_count = Some(n),
72                _ => {
73                    eprintln!("Error: concurrent count must be a positive integer, got '{}'", args[i]);
74                    return;
75                }
76            }
77        } else if arg == "-i" || arg == "--iterations" {
78            if i + 1 >= args.len() {
79                eprintln!("Error: {} requires a value", arg);
80                print_help(program_name);
81                return;
82            }
83            i += 1;
84            match args[i].parse::<usize>() {
85                Ok(n) if n > 0 => iterations = n,
86                _ => {
87                    eprintln!("Error: iterations must be a positive integer, got '{}'", args[i]);
88                    return;
89                }
90            }
91        } else if !arg.starts_with('-') {
92            scenario_filter = Some(arg.clone());
93        } else {
94            eprintln!("Error: unknown option '{}'", arg);
95            print_help(program_name);
96            return;
97        }
98        
99        i += 1;
100    }
101    
102    println!("\n🚀 JSON Evaluation - Benchmark\n");
103    
104    // Show CPU info if requested or if running benchmarks
105    if show_cpu_info || iterations > 1 || concurrent_count.is_some() {
106        common::print_cpu_info();
107    }
108    
109    if use_parsed_schema {
110        println!("📦 Mode: ParsedSchema (parse once, reuse for all iterations)\n");
111    }
112    
113    if use_cache {
114        println!("♻️  Mode: Cache (reuse JSONEval instance across iterations)\n");
115    }
116    
117    if let Some(count) = concurrent_count {
118        println!("🔀 Concurrent evaluations: {} threads\n", count);
119    } else if iterations > 1 {
120        println!("🔄 Iterations per scenario: {}\n", iterations);
121    }
122    
123    if enable_comparison {
124        println!("🔍 Comparison: enabled");
125    }
126    if show_timing {
127        println!("⏱️  Internal timing: enabled");
128    }
129    if enable_comparison || show_timing {
130        println!();
131    }
132
133    let samples_dir = Path::new("samples");
134    let mut scenarios = common::discover_scenarios(samples_dir);
135    
136    // Filter scenarios if a filter is provided
137    if let Some(ref filter) = scenario_filter {
138        scenarios.retain(|s| s.name.contains(filter));
139        println!("📋 Filtering scenarios matching: '{}'\n", filter);
140    }
141
142    if scenarios.is_empty() {
143        if let Some(filter) = scenario_filter {
144            println!(
145                "ℹ️  No scenarios found matching '{}' in `{}`.",
146                filter,
147                samples_dir.display()
148            );
149        } else {
150            println!(
151                "ℹ️  No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
152                samples_dir.display()
153            );
154        }
155        return;
156    }
157    
158    println!("📊 Found {} scenario(s)\n", scenarios.len());
159
160    let mut total_parse_time = std::time::Duration::ZERO;
161    let mut total_eval_time = std::time::Duration::ZERO;
162    let mut successful_scenarios = 0;
163    let mut comparison_failures = 0;
164
165    for scenario in &scenarios {
166        println!("==============================");
167        println!("Scenario: {}", scenario.name);
168        println!("Schema: {} ({})", 
169            scenario.schema_path.display(),
170            if scenario.is_msgpack { "MessagePack" } else { "JSON" }
171        );
172        println!("Data: {}\n", scenario.data_path.display());
173
174        // Clear timing data from previous scenarios
175        if show_timing {
176            json_eval_rs::enable_timing();
177            json_eval_rs::clear_timing_data();
178        }
179
180        let data_str = fs::read_to_string(&scenario.data_path)
181            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
182
183        println!("Running evaluation...\n");
184
185        let (parse_time, eval_time, evaluated_schema, eval, iteration_times) = if use_parsed_schema {
186            // ParsedSchema mode: parse once, reuse for all iterations/threads
187            let start_time = Instant::now();
188            
189            let parsed_schema = if scenario.is_msgpack {
190                let schema_msgpack = fs::read(&scenario.schema_path)
191                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
192                println!("  📦 MessagePack schema size: {} bytes", schema_msgpack.len());
193                Arc::new(ParsedSchema::parse_msgpack(&schema_msgpack)
194                    .unwrap_or_else(|e| panic!("failed to parse MessagePack schema: {}", e)))
195            } else {
196                let schema_str = fs::read_to_string(&scenario.schema_path)
197                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
198                Arc::new(ParsedSchema::parse(&schema_str)
199                    .unwrap_or_else(|e| panic!("failed to parse schema: {}", e)))
200            };
201            
202            let parse_time = start_time.elapsed();
203            println!("  Schema parsing & compilation: {:?}", parse_time);
204            
205            // Concurrent mode with ParsedSchema
206            if let Some(thread_count) = concurrent_count {
207                use std::thread;
208                
209                let eval_start = Instant::now();
210                let mut handles = vec![];
211                
212                for thread_id in 0..thread_count {
213                    let parsed_clone = parsed_schema.clone();
214                    let data_str_clone = data_str.clone();
215                    let iter_count = iterations;
216                    let thread_use_cache = use_cache;
217                    
218                    let handle = thread::spawn(move || {
219                        let mut thread_times = Vec::with_capacity(iter_count);
220                        let mut last_schema = Value::Null;
221                        
222                        let mut eval_instance = JSONEval::with_parsed_schema(
223                            parsed_clone.clone(),
224                            Some("{}"),
225                            Some(&data_str_clone)
226                        ).unwrap();
227                        
228                        for iter in 0..iter_count {
229                            let iter_start = Instant::now();
230                            
231                            if !thread_use_cache && iter > 0 {
232                                eval_instance = JSONEval::with_parsed_schema(
233                                    parsed_clone.clone(),
234                                    Some("{}"),
235                                    Some(&data_str_clone)
236                                ).unwrap();
237                            }
238                            
239                            eval_instance.evaluate(&data_str_clone, Some("{}"), None, None).unwrap();
240                            last_schema = eval_instance.get_evaluated_schema(false);
241                            thread_times.push(iter_start.elapsed());
242                        }
243                        
244                        (thread_times, last_schema, thread_id)
245                    });
246                    handles.push(handle);
247                }
248                
249                let mut all_iteration_times = Vec::new();
250                let mut evaluated_schema = Value::Null;
251                
252                for handle in handles {
253                    let (thread_times, thread_schema, thread_id) = handle.join().unwrap();
254                    println!("  Thread {} completed {} iterations", thread_id, thread_times.len());
255                    all_iteration_times.extend(thread_times);
256                    evaluated_schema = thread_schema; // Use last thread's result
257                }
258                
259                let eval_time = eval_start.elapsed();
260                
261                // Create a temp eval for metadata export
262                let temp_eval = JSONEval::with_parsed_schema(
263                    parsed_schema.clone(),
264                    Some("{}"),
265                    Some(&data_str)
266                ).unwrap();
267                
268                (parse_time, eval_time, evaluated_schema, temp_eval, all_iteration_times)
269            } else {
270                // Sequential iterations with ParsedSchema
271                let eval_start = Instant::now();
272                let mut evaluated_schema = Value::Null;
273                let mut iteration_times = Vec::with_capacity(iterations);
274                let mut eval_instance = JSONEval::with_parsed_schema(
275                    parsed_schema.clone(),
276                    Some("{}"),
277                    Some(&data_str)
278                ).unwrap();
279                
280                for iter in 0..iterations {
281                    let iter_start = Instant::now();
282                    
283                    if !use_cache && iter > 0 {
284                        eval_instance = JSONEval::with_parsed_schema(
285                            parsed_schema.clone(),
286                            Some("{}"),
287                            Some(&data_str)
288                        ).unwrap();
289                    }
290                    
291                    eval_instance.evaluate(&data_str, Some("{}"), None, None)
292                        .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
293                    evaluated_schema = eval_instance.get_evaluated_schema(false);
294                    iteration_times.push(iter_start.elapsed());
295                    
296                    if iterations > 1 && (iter + 1) % 10 == 0 {
297                        print!(".");
298                        if (iter + 1) % 50 == 0 {
299                            println!(" {}/{}", iter + 1, iterations);
300                        }
301                    }
302                }
303                
304                if iterations > 1 && iterations % 50 != 0 {
305                    println!(" {}/{}", iterations, iterations);
306                }
307                
308                let eval_time = eval_start.elapsed();
309                (parse_time, eval_time, evaluated_schema, eval_instance, iteration_times)
310            }
311        } else {
312            // Traditional mode: parse and create JSONEval each time
313            let schema_msgpack = if scenario.is_msgpack {
314                let bytes = fs::read(&scenario.schema_path)
315                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
316                println!("  📦 MessagePack schema size: {} bytes", bytes.len());
317                Some(bytes)
318            } else {
319                None
320            };
321            
322            let schema_str = if !scenario.is_msgpack {
323                Some(fs::read_to_string(&scenario.schema_path)
324                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e)))
325            } else {
326                None
327            };
328
329            let start_time = Instant::now();
330            let mut eval = if scenario.is_msgpack {
331                JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
332                    .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
333            } else {
334                JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
335                    .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
336            };
337            let parse_time = start_time.elapsed();
338            println!("  Schema parsing & compilation: {:?}", parse_time);
339            
340            let eval_start = Instant::now();
341            let mut evaluated_schema = Value::Null;
342            let mut iteration_times = Vec::with_capacity(iterations);
343            
344            for iter in 0..iterations {
345                let iter_start = Instant::now();
346                
347                if !use_cache && iter > 0 {
348                    eval = if scenario.is_msgpack {
349                        JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
350                            .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
351                    } else {
352                        JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
353                            .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
354                    };
355                }
356                
357                eval.evaluate(&data_str, Some("{}"), None, None)
358                    .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
359                evaluated_schema = eval.get_evaluated_schema(false);
360                iteration_times.push(iter_start.elapsed());
361                
362                if iterations > 1 && (iter + 1) % 10 == 0 {
363                    print!(".");
364                    if (iter + 1) % 50 == 0 {
365                        println!(" {}/{}", iter + 1, iterations);
366                    }
367                }
368            }
369            
370            if iterations > 1 && iterations % 50 != 0 {
371                println!(" {}/{}", iterations, iterations);
372            }
373            
374            let eval_time = eval_start.elapsed();
375            (parse_time, eval_time, evaluated_schema, eval, iteration_times)
376        };
377        
378        // Calculate statistics
379        let total_iterations = iteration_times.len();
380        if total_iterations == 1 {
381            println!("  Evaluation: {:?}", eval_time);
382        } else {
383            let avg_time = eval_time / total_iterations as u32;
384            let min_time = iteration_times.iter().min().unwrap();
385            let max_time = iteration_times.iter().max().unwrap();
386            
387            println!("  Total evaluation time: {:?}", eval_time);
388            println!("  Total iterations: {}", total_iterations);
389            println!("  Average per iteration: {:?}", avg_time);
390            println!("  Min: {:?} | Max: {:?}", min_time, max_time);
391            
392            // Show cache statistics
393            let cache_stats = eval.cache_stats();
394            println!("  Cache: {} entries, {} hits, {} misses ({:.1}% hit rate)",
395                cache_stats.entries,
396                cache_stats.hits,
397                cache_stats.misses,
398                cache_stats.hit_rate * 100.0
399            );
400        }
401
402        let total_time = parse_time + eval_time;
403        println!("⏱️  Execution time: {:?}\n", total_time);
404        
405        // Print detailed timing breakdown if --timing flag is set
406        if show_timing {
407            json_eval_rs::print_timing_summary();
408        }
409        
410        // Track statistics
411        total_parse_time += parse_time;
412        total_eval_time += eval_time;
413        successful_scenarios += 1;
414
415        let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
416        let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
417
418        fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
419            .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
420
421        let mut metadata_obj = Map::new();
422        metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
423        metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
424
425        fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
426            .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
427
428        println!("✅ Results saved:");
429        println!("  - {}", evaluated_path.display());
430        println!("  - {}\n", parsed_path.display());
431
432        // Optional comparison
433        if enable_comparison {
434            if let Some(comp_path) = &scenario.comparison_path {
435                if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
436                    comparison_failures += 1;
437                }
438                println!();
439            }
440        }
441    }
442    
443    // Print summary statistics
444    if successful_scenarios > 0 {
445        println!("\n{}", "=".repeat(50));
446        println!("📊 Summary Statistics");
447        println!("{}", "=".repeat(50));
448        println!("Total scenarios run: {}", successful_scenarios);
449        println!("Total parsing time: {:?}", total_parse_time);
450        println!("Total evaluation time: {:?}", total_eval_time);
451        println!("Total time: {:?}", total_parse_time + total_eval_time);
452        
453        if successful_scenarios > 1 {
454            println!("\nAverage per scenario:");
455            println!("  Parsing: {:?}", total_parse_time / successful_scenarios as u32);
456            println!("  Evaluation: {:?}", total_eval_time / successful_scenarios as u32);
457        }
458        
459        if enable_comparison {
460            println!("\nComparison failures: {}", comparison_failures);
461        }
462        
463        println!("\n✅ All scenarios completed successfully!\n");
464    }
465}
Source

pub fn new_from_msgpack( schema_msgpack: &[u8], context: Option<&str>, data: Option<&str>, ) -> Result<Self, String>

Create a new JSONEval instance from MessagePack-encoded schema

§Arguments
  • schema_msgpack - MessagePack-encoded schema bytes
  • context - Optional JSON context string
  • data - Optional JSON data string
§Returns

A Result containing the JSONEval instance or an error

Examples found in repository?
examples/basic_msgpack.rs (line 129)
28fn main() {
29    let args: Vec<String> = std::env::args().collect();
30    let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic_msgpack");
31    
32    let mut scenario_filter: Option<String> = None;
33    let mut enable_comparison = false;
34    let mut show_timing = false;
35    let mut i = 1;
36    
37    // Parse arguments
38    while i < args.len() {
39        let arg = &args[i];
40        
41        if arg == "-h" || arg == "--help" {
42            print_help(program_name);
43            return;
44        } else if arg == "--compare" {
45            enable_comparison = true;
46        } else if arg == "--timing" {
47            show_timing = true;
48        } else if !arg.starts_with('-') {
49            scenario_filter = Some(arg.clone());
50        } else {
51            eprintln!("Error: unknown option '{}'", arg);
52            print_help(program_name);
53            return;
54        }
55        
56        i += 1;
57    }
58    
59    println!("\n🚀 JSON Evaluation - Basic Example (MessagePack Schema)\n");
60    
61    if enable_comparison {
62        println!("🔍 Comparison: enabled");
63    }
64    if show_timing {
65        println!("⏱️  Internal timing: enabled");
66    }
67    if enable_comparison || show_timing {
68        println!();
69    }
70    
71    let samples_dir = Path::new("samples");
72    let mut scenarios = common::discover_scenarios(samples_dir);
73    
74    // Filter to only MessagePack scenarios
75    scenarios.retain(|s| s.is_msgpack);
76    
77    // Filter scenarios if a filter is provided
78    if let Some(ref filter) = scenario_filter {
79        scenarios.retain(|s| s.name.contains(filter));
80        println!("📋 Filtering scenarios matching: '{}'\n", filter);
81    }
82
83    if scenarios.is_empty() {
84        if let Some(filter) = scenario_filter {
85            println!(
86                "ℹ️  No MessagePack scenarios found matching '{}' in `{}`.",
87                filter,
88                samples_dir.display()
89            );
90        } else {
91            println!(
92                "ℹ️  No MessagePack scenarios discovered in `{}`. Add files like `name.bform` and `name-data.json`.",
93                samples_dir.display()
94            );
95        }
96        return;
97    }
98    
99    println!("📊 Found {} MessagePack scenario(s)\n", scenarios.len());
100
101    let mut total_parse_time = std::time::Duration::ZERO;
102    let mut total_eval_time = std::time::Duration::ZERO;
103    let mut successful_scenarios = 0;
104    let mut comparison_failures = 0;
105
106    for scenario in &scenarios {
107        println!("==============================");
108        println!("Scenario: {}", scenario.name);
109        println!("Schema: {} (MessagePack)", scenario.schema_path.display());
110        println!("Data: {}\n", scenario.data_path.display());
111
112        // Clear timing data from previous scenarios
113        if show_timing {
114            json_eval_rs::enable_timing();
115            json_eval_rs::clear_timing_data();
116        }
117
118        let data_str = fs::read_to_string(&scenario.data_path)
119            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
120
121        // Step 1: Parse schema (new_from_msgpack)
122        let parse_start = Instant::now();
123        
124        let schema_msgpack = fs::read(&scenario.schema_path)
125            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
126        
127        println!("  📦 MessagePack schema size: {} bytes", schema_msgpack.len());
128        
129        let mut eval = JSONEval::new_from_msgpack(&schema_msgpack, None, Some(&data_str))
130            .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e));
131        
132        let parse_time = parse_start.elapsed();
133        println!("  📝 Parse (msgpack): {:?}", parse_time);
134        
135        // Step 2: Evaluate
136        let eval_start = Instant::now();
137        
138        eval.evaluate(&data_str, Some("{}"), None, None)
139            .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
140        
141        let evaluated_schema = eval.get_evaluated_schema(false);
142        let eval_time = eval_start.elapsed();
143        
144        println!("  ⚡ Eval: {:?}", eval_time);
145        println!("  ⏱️  Total: {:?}\n", parse_time + eval_time);
146        
147        // Print detailed timing breakdown if --timing flag is set
148        if show_timing {
149            json_eval_rs::print_timing_summary();
150        }
151        
152        total_parse_time += parse_time;
153        total_eval_time += eval_time;
154        successful_scenarios += 1;
155
156        // Save results
157        let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
158        let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
159
160        fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
161            .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
162
163        let mut metadata_obj = Map::new();
164        metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
165        metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
166        metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
167
168        fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
169            .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
170
171        println!("✅ Results saved:");
172        println!("  - {}", evaluated_path.display());
173        println!("  - {}\n", parsed_path.display());
174
175        // Optional comparison
176        if enable_comparison {
177            if let Some(comp_path) = &scenario.comparison_path {
178                if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
179                    comparison_failures += 1;
180                }
181                println!();
182            }
183        }
184    }
185    
186    // Print summary
187    println!("{}", "=".repeat(50));
188    println!("📊 Summary");
189    println!("{}", "=".repeat(50));
190    println!("Total scenarios run: {}", successful_scenarios);
191    println!("Total parse time: {:?}", total_parse_time);
192    println!("Total eval time: {:?}", total_eval_time);
193    println!("Total time: {:?}", total_parse_time + total_eval_time);
194    
195    if successful_scenarios > 1 {
196        println!("\nAverage per scenario:");
197        println!("  Parse: {:?}", total_parse_time / successful_scenarios as u32);
198        println!("  Eval: {:?}", total_eval_time / successful_scenarios as u32);
199    }
200    
201    if enable_comparison {
202        println!("Comparison failures: {}", comparison_failures);
203    }
204    
205    println!("\n✅ All scenarios completed!\n");
206}
More examples
Hide additional examples
examples/benchmark.rs (line 331)
32fn main() {
33    let args: Vec<String> = std::env::args().collect();
34    let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("benchmark");
35    
36    let mut iterations = 1usize;
37    let mut scenario_filter: Option<String> = None;
38    let mut show_cpu_info = false;
39    let mut use_parsed_schema = false;
40    let mut use_cache = false;
41    let mut concurrent_count: Option<usize> = None;
42    let mut enable_comparison = false;
43    let mut show_timing = false;
44    let mut i = 1;
45    
46    // Parse arguments
47    while i < args.len() {
48        let arg = &args[i];
49        
50        if arg == "-h" || arg == "--help" {
51            print_help(program_name);
52            return;
53        } else if arg == "--cpu-info" {
54            show_cpu_info = true;
55        } else if arg == "--parsed" {
56            use_parsed_schema = true;
57        } else if arg == "--cache" {
58            use_cache = true;
59        } else if arg == "--compare" {
60            enable_comparison = true;
61        } else if arg == "--timing" {
62            show_timing = true;
63        } else if arg == "--concurrent" {
64            if i + 1 >= args.len() {
65                eprintln!("Error: {} requires a value", arg);
66                print_help(program_name);
67                return;
68            }
69            i += 1;
70            match args[i].parse::<usize>() {
71                Ok(n) if n > 0 => concurrent_count = Some(n),
72                _ => {
73                    eprintln!("Error: concurrent count must be a positive integer, got '{}'", args[i]);
74                    return;
75                }
76            }
77        } else if arg == "-i" || arg == "--iterations" {
78            if i + 1 >= args.len() {
79                eprintln!("Error: {} requires a value", arg);
80                print_help(program_name);
81                return;
82            }
83            i += 1;
84            match args[i].parse::<usize>() {
85                Ok(n) if n > 0 => iterations = n,
86                _ => {
87                    eprintln!("Error: iterations must be a positive integer, got '{}'", args[i]);
88                    return;
89                }
90            }
91        } else if !arg.starts_with('-') {
92            scenario_filter = Some(arg.clone());
93        } else {
94            eprintln!("Error: unknown option '{}'", arg);
95            print_help(program_name);
96            return;
97        }
98        
99        i += 1;
100    }
101    
102    println!("\n🚀 JSON Evaluation - Benchmark\n");
103    
104    // Show CPU info if requested or if running benchmarks
105    if show_cpu_info || iterations > 1 || concurrent_count.is_some() {
106        common::print_cpu_info();
107    }
108    
109    if use_parsed_schema {
110        println!("📦 Mode: ParsedSchema (parse once, reuse for all iterations)\n");
111    }
112    
113    if use_cache {
114        println!("♻️  Mode: Cache (reuse JSONEval instance across iterations)\n");
115    }
116    
117    if let Some(count) = concurrent_count {
118        println!("🔀 Concurrent evaluations: {} threads\n", count);
119    } else if iterations > 1 {
120        println!("🔄 Iterations per scenario: {}\n", iterations);
121    }
122    
123    if enable_comparison {
124        println!("🔍 Comparison: enabled");
125    }
126    if show_timing {
127        println!("⏱️  Internal timing: enabled");
128    }
129    if enable_comparison || show_timing {
130        println!();
131    }
132
133    let samples_dir = Path::new("samples");
134    let mut scenarios = common::discover_scenarios(samples_dir);
135    
136    // Filter scenarios if a filter is provided
137    if let Some(ref filter) = scenario_filter {
138        scenarios.retain(|s| s.name.contains(filter));
139        println!("📋 Filtering scenarios matching: '{}'\n", filter);
140    }
141
142    if scenarios.is_empty() {
143        if let Some(filter) = scenario_filter {
144            println!(
145                "ℹ️  No scenarios found matching '{}' in `{}`.",
146                filter,
147                samples_dir.display()
148            );
149        } else {
150            println!(
151                "ℹ️  No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
152                samples_dir.display()
153            );
154        }
155        return;
156    }
157    
158    println!("📊 Found {} scenario(s)\n", scenarios.len());
159
160    let mut total_parse_time = std::time::Duration::ZERO;
161    let mut total_eval_time = std::time::Duration::ZERO;
162    let mut successful_scenarios = 0;
163    let mut comparison_failures = 0;
164
165    for scenario in &scenarios {
166        println!("==============================");
167        println!("Scenario: {}", scenario.name);
168        println!("Schema: {} ({})", 
169            scenario.schema_path.display(),
170            if scenario.is_msgpack { "MessagePack" } else { "JSON" }
171        );
172        println!("Data: {}\n", scenario.data_path.display());
173
174        // Clear timing data from previous scenarios
175        if show_timing {
176            json_eval_rs::enable_timing();
177            json_eval_rs::clear_timing_data();
178        }
179
180        let data_str = fs::read_to_string(&scenario.data_path)
181            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
182
183        println!("Running evaluation...\n");
184
185        let (parse_time, eval_time, evaluated_schema, eval, iteration_times) = if use_parsed_schema {
186            // ParsedSchema mode: parse once, reuse for all iterations/threads
187            let start_time = Instant::now();
188            
189            let parsed_schema = if scenario.is_msgpack {
190                let schema_msgpack = fs::read(&scenario.schema_path)
191                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
192                println!("  📦 MessagePack schema size: {} bytes", schema_msgpack.len());
193                Arc::new(ParsedSchema::parse_msgpack(&schema_msgpack)
194                    .unwrap_or_else(|e| panic!("failed to parse MessagePack schema: {}", e)))
195            } else {
196                let schema_str = fs::read_to_string(&scenario.schema_path)
197                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
198                Arc::new(ParsedSchema::parse(&schema_str)
199                    .unwrap_or_else(|e| panic!("failed to parse schema: {}", e)))
200            };
201            
202            let parse_time = start_time.elapsed();
203            println!("  Schema parsing & compilation: {:?}", parse_time);
204            
205            // Concurrent mode with ParsedSchema
206            if let Some(thread_count) = concurrent_count {
207                use std::thread;
208                
209                let eval_start = Instant::now();
210                let mut handles = vec![];
211                
212                for thread_id in 0..thread_count {
213                    let parsed_clone = parsed_schema.clone();
214                    let data_str_clone = data_str.clone();
215                    let iter_count = iterations;
216                    let thread_use_cache = use_cache;
217                    
218                    let handle = thread::spawn(move || {
219                        let mut thread_times = Vec::with_capacity(iter_count);
220                        let mut last_schema = Value::Null;
221                        
222                        let mut eval_instance = JSONEval::with_parsed_schema(
223                            parsed_clone.clone(),
224                            Some("{}"),
225                            Some(&data_str_clone)
226                        ).unwrap();
227                        
228                        for iter in 0..iter_count {
229                            let iter_start = Instant::now();
230                            
231                            if !thread_use_cache && iter > 0 {
232                                eval_instance = JSONEval::with_parsed_schema(
233                                    parsed_clone.clone(),
234                                    Some("{}"),
235                                    Some(&data_str_clone)
236                                ).unwrap();
237                            }
238                            
239                            eval_instance.evaluate(&data_str_clone, Some("{}"), None, None).unwrap();
240                            last_schema = eval_instance.get_evaluated_schema(false);
241                            thread_times.push(iter_start.elapsed());
242                        }
243                        
244                        (thread_times, last_schema, thread_id)
245                    });
246                    handles.push(handle);
247                }
248                
249                let mut all_iteration_times = Vec::new();
250                let mut evaluated_schema = Value::Null;
251                
252                for handle in handles {
253                    let (thread_times, thread_schema, thread_id) = handle.join().unwrap();
254                    println!("  Thread {} completed {} iterations", thread_id, thread_times.len());
255                    all_iteration_times.extend(thread_times);
256                    evaluated_schema = thread_schema; // Use last thread's result
257                }
258                
259                let eval_time = eval_start.elapsed();
260                
261                // Create a temp eval for metadata export
262                let temp_eval = JSONEval::with_parsed_schema(
263                    parsed_schema.clone(),
264                    Some("{}"),
265                    Some(&data_str)
266                ).unwrap();
267                
268                (parse_time, eval_time, evaluated_schema, temp_eval, all_iteration_times)
269            } else {
270                // Sequential iterations with ParsedSchema
271                let eval_start = Instant::now();
272                let mut evaluated_schema = Value::Null;
273                let mut iteration_times = Vec::with_capacity(iterations);
274                let mut eval_instance = JSONEval::with_parsed_schema(
275                    parsed_schema.clone(),
276                    Some("{}"),
277                    Some(&data_str)
278                ).unwrap();
279                
280                for iter in 0..iterations {
281                    let iter_start = Instant::now();
282                    
283                    if !use_cache && iter > 0 {
284                        eval_instance = JSONEval::with_parsed_schema(
285                            parsed_schema.clone(),
286                            Some("{}"),
287                            Some(&data_str)
288                        ).unwrap();
289                    }
290                    
291                    eval_instance.evaluate(&data_str, Some("{}"), None, None)
292                        .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
293                    evaluated_schema = eval_instance.get_evaluated_schema(false);
294                    iteration_times.push(iter_start.elapsed());
295                    
296                    if iterations > 1 && (iter + 1) % 10 == 0 {
297                        print!(".");
298                        if (iter + 1) % 50 == 0 {
299                            println!(" {}/{}", iter + 1, iterations);
300                        }
301                    }
302                }
303                
304                if iterations > 1 && iterations % 50 != 0 {
305                    println!(" {}/{}", iterations, iterations);
306                }
307                
308                let eval_time = eval_start.elapsed();
309                (parse_time, eval_time, evaluated_schema, eval_instance, iteration_times)
310            }
311        } else {
312            // Traditional mode: parse and create JSONEval each time
313            let schema_msgpack = if scenario.is_msgpack {
314                let bytes = fs::read(&scenario.schema_path)
315                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
316                println!("  📦 MessagePack schema size: {} bytes", bytes.len());
317                Some(bytes)
318            } else {
319                None
320            };
321            
322            let schema_str = if !scenario.is_msgpack {
323                Some(fs::read_to_string(&scenario.schema_path)
324                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e)))
325            } else {
326                None
327            };
328
329            let start_time = Instant::now();
330            let mut eval = if scenario.is_msgpack {
331                JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
332                    .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
333            } else {
334                JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
335                    .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
336            };
337            let parse_time = start_time.elapsed();
338            println!("  Schema parsing & compilation: {:?}", parse_time);
339            
340            let eval_start = Instant::now();
341            let mut evaluated_schema = Value::Null;
342            let mut iteration_times = Vec::with_capacity(iterations);
343            
344            for iter in 0..iterations {
345                let iter_start = Instant::now();
346                
347                if !use_cache && iter > 0 {
348                    eval = if scenario.is_msgpack {
349                        JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
350                            .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
351                    } else {
352                        JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
353                            .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
354                    };
355                }
356                
357                eval.evaluate(&data_str, Some("{}"), None, None)
358                    .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
359                evaluated_schema = eval.get_evaluated_schema(false);
360                iteration_times.push(iter_start.elapsed());
361                
362                if iterations > 1 && (iter + 1) % 10 == 0 {
363                    print!(".");
364                    if (iter + 1) % 50 == 0 {
365                        println!(" {}/{}", iter + 1, iterations);
366                    }
367                }
368            }
369            
370            if iterations > 1 && iterations % 50 != 0 {
371                println!(" {}/{}", iterations, iterations);
372            }
373            
374            let eval_time = eval_start.elapsed();
375            (parse_time, eval_time, evaluated_schema, eval, iteration_times)
376        };
377        
378        // Calculate statistics
379        let total_iterations = iteration_times.len();
380        if total_iterations == 1 {
381            println!("  Evaluation: {:?}", eval_time);
382        } else {
383            let avg_time = eval_time / total_iterations as u32;
384            let min_time = iteration_times.iter().min().unwrap();
385            let max_time = iteration_times.iter().max().unwrap();
386            
387            println!("  Total evaluation time: {:?}", eval_time);
388            println!("  Total iterations: {}", total_iterations);
389            println!("  Average per iteration: {:?}", avg_time);
390            println!("  Min: {:?} | Max: {:?}", min_time, max_time);
391            
392            // Show cache statistics
393            let cache_stats = eval.cache_stats();
394            println!("  Cache: {} entries, {} hits, {} misses ({:.1}% hit rate)",
395                cache_stats.entries,
396                cache_stats.hits,
397                cache_stats.misses,
398                cache_stats.hit_rate * 100.0
399            );
400        }
401
402        let total_time = parse_time + eval_time;
403        println!("⏱️  Execution time: {:?}\n", total_time);
404        
405        // Print detailed timing breakdown if --timing flag is set
406        if show_timing {
407            json_eval_rs::print_timing_summary();
408        }
409        
410        // Track statistics
411        total_parse_time += parse_time;
412        total_eval_time += eval_time;
413        successful_scenarios += 1;
414
415        let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
416        let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
417
418        fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
419            .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
420
421        let mut metadata_obj = Map::new();
422        metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
423        metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
424
425        fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
426            .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
427
428        println!("✅ Results saved:");
429        println!("  - {}", evaluated_path.display());
430        println!("  - {}\n", parsed_path.display());
431
432        // Optional comparison
433        if enable_comparison {
434            if let Some(comp_path) = &scenario.comparison_path {
435                if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
436                    comparison_failures += 1;
437                }
438                println!();
439            }
440        }
441    }
442    
443    // Print summary statistics
444    if successful_scenarios > 0 {
445        println!("\n{}", "=".repeat(50));
446        println!("📊 Summary Statistics");
447        println!("{}", "=".repeat(50));
448        println!("Total scenarios run: {}", successful_scenarios);
449        println!("Total parsing time: {:?}", total_parse_time);
450        println!("Total evaluation time: {:?}", total_eval_time);
451        println!("Total time: {:?}", total_parse_time + total_eval_time);
452        
453        if successful_scenarios > 1 {
454            println!("\nAverage per scenario:");
455            println!("  Parsing: {:?}", total_parse_time / successful_scenarios as u32);
456            println!("  Evaluation: {:?}", total_eval_time / successful_scenarios as u32);
457        }
458        
459        if enable_comparison {
460            println!("\nComparison failures: {}", comparison_failures);
461        }
462        
463        println!("\n✅ All scenarios completed successfully!\n");
464    }
465}
Source

pub fn with_parsed_schema( parsed: Arc<ParsedSchema>, context: Option<&str>, data: Option<&str>, ) -> Result<Self, String>

Create a new JSONEval instance from a pre-parsed ParsedSchema

This enables schema caching: parse once, reuse across multiple evaluations with different data/context.

§Arguments
  • parsed - Arc-wrapped pre-parsed schema (can be cloned and cached)
  • context - Optional JSON context string
  • data - Optional JSON data string
§Returns

A Result containing the JSONEval instance or an error

§Example
use std::sync::Arc;

// Parse schema once and wrap in Arc for caching
let parsed = Arc::new(ParsedSchema::parse(schema_str)?);
cache.insert(schema_key, parsed.clone());

// Reuse across multiple evaluations (Arc::clone is cheap)
let eval1 = JSONEval::with_parsed_schema(parsed.clone(), Some(context1), Some(data1))?;
let eval2 = JSONEval::with_parsed_schema(parsed.clone(), Some(context2), Some(data2))?;
Examples found in repository?
examples/cache_demo.rs (line 73)
38fn demo_local_cache() -> Result<(), Box<dyn std::error::Error>> {
39    println!("📦 Example 1: Local Cache Instance");
40    println!("Creating a dedicated cache for this application...\n");
41    
42    let cache = ParsedSchemaCache::new();
43    
44    // Simple schema
45    let schema_json = r#"{
46        "$params": {
47            "rate": { "type": "number" }
48        },
49        "result": {
50            "type": "number",
51            "title": "Calculated Result",
52            "$evaluation": {
53                "logic": { "*": [{"var": "$rate"}, 100] }
54            }
55        }
56    }"#;
57    
58    // Parse and cache with a custom key
59    println!("📝 Parsing schema and caching with key 'calculation-v1'...");
60    let parsed = ParsedSchema::parse(schema_json)?;
61    cache.insert("calculation-v1".to_string(), Arc::new(parsed));
62    
63    println!("✅ Schema cached successfully");
64    println!("   Cache size: {} entries", cache.len());
65    println!("   Keys: {:?}\n", cache.keys());
66    
67    // Retrieve and use cached schema
68    println!("🔍 Retrieving cached schema...");
69    if let Some(cached_schema) = cache.get("calculation-v1") {
70        println!("✅ Retrieved from cache");
71        
72        // Create JSONEval from cached ParsedSchema
73        let mut eval = JSONEval::with_parsed_schema(cached_schema, Some(r#"{"rate": 1.5}"#), None)?;
74        eval.evaluate("{}", None, None, None)?;
75        
76        let evaluated = eval.get_evaluated_schema(false);
77        let result = evaluated.pointer("/result")
78            .and_then(|v| v.as_f64())
79            .unwrap_or(0.0);
80        println!("   Evaluation result: {}\n", result);
81    }
82    
83    // Check cache stats
84    let stats = cache.stats();
85    println!("📊 Cache Statistics: {}", stats);
86    
87    // Remove entry
88    println!("\n🗑️  Removing 'calculation-v1' from cache...");
89    cache.remove("calculation-v1");
90    println!("   Cache size after removal: {}", cache.len());
91    
92    Ok(())
93}
94
95fn demo_global_cache() -> Result<(), Box<dyn std::error::Error>> {
96    println!("🌍 Example 2: Global Cache Instance");
97    println!("Using the built-in PARSED_SCHEMA_CACHE...\n");
98    
99    let schema_json = r#"{
100        "$params": {
101            "x": { "type": "number" },
102            "y": { "type": "number" }
103        },
104        "sum": {
105            "type": "number",
106            "$evaluation": { "+": [{"var": "$x"}, {"var": "$y"}] }
107        }
108    }"#;
109    
110    // Use global cache
111    println!("📝 Caching schema globally with key 'math-operations'...");
112    let parsed = ParsedSchema::parse(schema_json)?;
113    PARSED_SCHEMA_CACHE.insert("math-operations".to_string(), Arc::new(parsed));
114    
115    println!("✅ Schema cached globally");
116    println!("   Global cache size: {}\n", PARSED_SCHEMA_CACHE.len());
117    
118    // Access from anywhere in the application
119    simulate_another_function()?;
120    
121    // Clean up
122    println!("\n🧹 Clearing global cache...");
123    PARSED_SCHEMA_CACHE.clear();
124    println!("   Global cache size: {}", PARSED_SCHEMA_CACHE.len());
125    
126    Ok(())
127}
128
129fn simulate_another_function() -> Result<(), Box<dyn std::error::Error>> {
130    println!("🔄 In another function, accessing global cache...");
131    
132    if let Some(cached) = PARSED_SCHEMA_CACHE.get("math-operations") {
133        println!("✅ Retrieved schema from global cache");
134        
135        let mut eval = JSONEval::with_parsed_schema(cached, Some(r#"{"x": 10, "y": 20}"#), None)?;
136        eval.evaluate("{}", None, None, None)?;
137        
138        let evaluated = eval.get_evaluated_schema(false);
139        let sum = evaluated.pointer("/sum")
140            .and_then(|v| v.as_f64())
141            .unwrap_or(0.0);
142        println!("   Result: {}", sum);
143    }
144    
145    Ok(())
146}
147
148fn demo_performance_comparison() -> Result<(), Box<dyn std::error::Error>> {
149    println!("⚡ Example 3: Performance Comparison");
150    println!("Comparing cached vs non-cached schema usage...\n");
151    
152    let schema_json = r#"{
153        "$params": {
154            "value": { "type": "number" }
155        },
156        "doubled": {
157            "type": "number",
158            "$evaluation": { "*": [{"var": "$value"}, 2] }
159        },
160        "tripled": {
161            "type": "number",
162            "$evaluation": { "*": [{"var": "$value"}, 3] }
163        }
164    }"#;
165    
166    let iterations = 100;
167    
168    // WITHOUT CACHE: Parse schema every time
169    println!("🐌 Without cache (parse + evaluate each time):");
170    let start = Instant::now();
171    for i in 0..iterations {
172        let context = format!(r#"{{"value": {}}}"#, i);
173        let mut eval = JSONEval::new(schema_json, Some(&context), None)?;
174        eval.evaluate("{}", None, None, None)?;
175    }
176    let without_cache = start.elapsed();
177    println!("   Time: {:?}", without_cache);
178    println!("   Avg per iteration: {:?}\n", without_cache / iterations);
179    
180    // WITH CACHE: Parse once, evaluate many times
181    println!("🚀 With cache (parse once, reuse for all evaluations):");
182    let cache = ParsedSchemaCache::new();
183    
184    // Parse once
185    let parse_start = Instant::now();
186    let parsed = ParsedSchema::parse(schema_json)?;
187    cache.insert("perf-test".to_string(), Arc::new(parsed));
188    let parse_time = parse_start.elapsed();
189    
190    // Evaluate many times
191    let eval_start = Instant::now();
192    for i in 0..iterations {
193        if let Some(cached) = cache.get("perf-test") {
194            let context = format!(r#"{{"value": {}}}"#, i);
195            let mut eval = JSONEval::with_parsed_schema(cached.clone(), Some(&context), None)?;
196            eval.evaluate("{}", None, None, None)?;
197        }
198    }
199    let eval_time = eval_start.elapsed();
200    let with_cache = parse_time + eval_time;
201    
202    println!("   Parse time: {:?}", parse_time);
203    println!("   Eval time: {:?}", eval_time);
204    println!("   Total time: {:?}", with_cache);
205    println!("   Avg per iteration: {:?}\n", eval_time / iterations);
206    
207    let speedup = without_cache.as_secs_f64() / with_cache.as_secs_f64();
208    println!("📈 Speedup: {:.2}x faster", speedup);
209    
210    Ok(())
211}
More examples
Hide additional examples
examples/basic_parsed.rs (lines 143-147)
30fn main() {
31    let args: Vec<String> = std::env::args().collect();
32    let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic_parsed");
33    
34    let mut scenario_filter: Option<String> = None;
35    let mut enable_comparison = false;
36    let mut show_timing = false;
37    let mut i = 1;
38    
39    // Parse arguments
40    while i < args.len() {
41        let arg = &args[i];
42        
43        if arg == "-h" || arg == "--help" {
44            print_help(program_name);
45            return;
46        } else if arg == "--compare" {
47            enable_comparison = true;
48        } else if arg == "--timing" {
49            show_timing = true;
50        } else if !arg.starts_with('-') {
51            scenario_filter = Some(arg.clone());
52        } else {
53            eprintln!("Error: unknown option '{}'", arg);
54            print_help(program_name);
55            return;
56        }
57        
58        i += 1;
59    }
60    
61    println!("\n🚀 JSON Evaluation - Basic Example (ParsedSchema)\n");
62    println!("📦 Using Arc<ParsedSchema> for efficient caching\n");
63    
64    if enable_comparison {
65        println!("🔍 Comparison: enabled");
66    }
67    if show_timing {
68        println!("⏱️  Internal timing: enabled");
69    }
70    if enable_comparison || show_timing {
71        println!();
72    }
73    
74    let samples_dir = Path::new("samples");
75    let mut scenarios = common::discover_scenarios(samples_dir);
76    
77    // Filter scenarios if a filter is provided
78    if let Some(ref filter) = scenario_filter {
79        scenarios.retain(|s| s.name.contains(filter));
80        println!("📋 Filtering scenarios matching: '{}'\n", filter);
81    }
82
83    if scenarios.is_empty() {
84        if let Some(filter) = scenario_filter {
85            println!(
86                "ℹ️  No scenarios found matching '{}' in `{}`.",
87                filter,
88                samples_dir.display()
89            );
90        } else {
91            println!(
92                "ℹ️  No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
93                samples_dir.display()
94            );
95        }
96        return;
97    }
98    
99    println!("📊 Found {} scenario(s)\n", scenarios.len());
100
101    let mut total_parse_time = std::time::Duration::ZERO;
102    let mut total_eval_time = std::time::Duration::ZERO;
103    let mut successful_scenarios = 0;
104    let mut comparison_failures = 0;
105
106    for scenario in &scenarios {
107        println!("==============================");
108        println!("Scenario: {}", scenario.name);
109        println!("Schema: {} ({})", 
110            scenario.schema_path.display(),
111            if scenario.is_msgpack { "MessagePack" } else { "JSON" }
112        );
113        println!("Data: {}\n", scenario.data_path.display());
114
115        // Clear timing data from previous scenarios
116        if show_timing {
117            json_eval_rs::enable_timing();
118            json_eval_rs::clear_timing_data();
119        }
120
121        let data_str = fs::read_to_string(&scenario.data_path)
122            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
123
124        // Step 1: Parse schema once
125        let parse_start = Instant::now();
126        let parsed_schema = if scenario.is_msgpack {
127            let schema_msgpack = fs::read(&scenario.schema_path)
128                .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
129            println!("  📦 MessagePack schema size: {} bytes", schema_msgpack.len());
130            Arc::new(ParsedSchema::parse_msgpack(&schema_msgpack)
131                .unwrap_or_else(|e| panic!("failed to parse MessagePack schema: {}", e)))
132        } else {
133            let schema_str = fs::read_to_string(&scenario.schema_path)
134                .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
135            Arc::new(ParsedSchema::parse(&schema_str)
136                .unwrap_or_else(|e| panic!("failed to parse schema: {}", e)))
137        };
138        let parse_time = parse_start.elapsed();
139        println!("  📝 Schema parsing: {:?}", parse_time);
140        
141        // Step 2: Create JSONEval from ParsedSchema (reuses compiled logic)
142        let eval_start = Instant::now();
143        let mut eval = JSONEval::with_parsed_schema(
144            parsed_schema.clone(),  // Arc::clone is cheap!
145            Some("{}"),
146            Some(&data_str)
147        ).unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e));
148
149        eval.evaluate(&data_str, Some("{}"), None, None)
150            .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
151        
152        let evaluated_schema = eval.get_evaluated_schema(false);
153        let eval_time = eval_start.elapsed();
154        
155        println!("  ⚡ Eval: {:?}", eval_time);
156        println!("  ⏱️  Total: {:?}\n", parse_time + eval_time);
157        
158        // Print detailed timing breakdown if --timing flag is set
159        if show_timing {
160            json_eval_rs::print_timing_summary();
161        }
162        
163        total_parse_time += parse_time;
164        total_eval_time += eval_time;
165        successful_scenarios += 1;
166
167        // Save results
168        let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
169        let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
170
171        fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
172            .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
173
174        let mut metadata_obj = Map::new();
175        metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
176        metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
177        metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
178
179        fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
180            .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
181
182        println!("✅ Results saved:");
183        println!("  - {}", evaluated_path.display());
184        println!("  - {}\n", parsed_path.display());
185
186        // Optional comparison
187        if enable_comparison {
188            if let Some(comp_path) = &scenario.comparison_path {
189                if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
190                    comparison_failures += 1;
191                }
192                println!();
193            }
194        }
195    }
196    
197    // Print summary
198    println!("{}", "=".repeat(50));
199    println!("📊 Summary");
200    println!("{}", "=".repeat(50));
201    println!("Total scenarios run: {}", successful_scenarios);
202    println!("Total parsing time: {:?}", total_parse_time);
203    println!("Total evaluation time: {:?}", total_eval_time);
204    println!("Total time: {:?}", total_parse_time + total_eval_time);
205    
206    if successful_scenarios > 1 {
207        println!("\nAverage per scenario:");
208        println!("  Parsing: {:?}", total_parse_time / successful_scenarios as u32);
209        println!("  Evaluation: {:?}", total_eval_time / successful_scenarios as u32);
210    }
211    
212    if enable_comparison {
213        println!("\nComparison failures: {}", comparison_failures);
214    }
215    
216    println!("\n✅ All scenarios completed!\n");
217}
examples/benchmark.rs (lines 222-226)
32fn main() {
33    let args: Vec<String> = std::env::args().collect();
34    let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("benchmark");
35    
36    let mut iterations = 1usize;
37    let mut scenario_filter: Option<String> = None;
38    let mut show_cpu_info = false;
39    let mut use_parsed_schema = false;
40    let mut use_cache = false;
41    let mut concurrent_count: Option<usize> = None;
42    let mut enable_comparison = false;
43    let mut show_timing = false;
44    let mut i = 1;
45    
46    // Parse arguments
47    while i < args.len() {
48        let arg = &args[i];
49        
50        if arg == "-h" || arg == "--help" {
51            print_help(program_name);
52            return;
53        } else if arg == "--cpu-info" {
54            show_cpu_info = true;
55        } else if arg == "--parsed" {
56            use_parsed_schema = true;
57        } else if arg == "--cache" {
58            use_cache = true;
59        } else if arg == "--compare" {
60            enable_comparison = true;
61        } else if arg == "--timing" {
62            show_timing = true;
63        } else if arg == "--concurrent" {
64            if i + 1 >= args.len() {
65                eprintln!("Error: {} requires a value", arg);
66                print_help(program_name);
67                return;
68            }
69            i += 1;
70            match args[i].parse::<usize>() {
71                Ok(n) if n > 0 => concurrent_count = Some(n),
72                _ => {
73                    eprintln!("Error: concurrent count must be a positive integer, got '{}'", args[i]);
74                    return;
75                }
76            }
77        } else if arg == "-i" || arg == "--iterations" {
78            if i + 1 >= args.len() {
79                eprintln!("Error: {} requires a value", arg);
80                print_help(program_name);
81                return;
82            }
83            i += 1;
84            match args[i].parse::<usize>() {
85                Ok(n) if n > 0 => iterations = n,
86                _ => {
87                    eprintln!("Error: iterations must be a positive integer, got '{}'", args[i]);
88                    return;
89                }
90            }
91        } else if !arg.starts_with('-') {
92            scenario_filter = Some(arg.clone());
93        } else {
94            eprintln!("Error: unknown option '{}'", arg);
95            print_help(program_name);
96            return;
97        }
98        
99        i += 1;
100    }
101    
102    println!("\n🚀 JSON Evaluation - Benchmark\n");
103    
104    // Show CPU info if requested or if running benchmarks
105    if show_cpu_info || iterations > 1 || concurrent_count.is_some() {
106        common::print_cpu_info();
107    }
108    
109    if use_parsed_schema {
110        println!("📦 Mode: ParsedSchema (parse once, reuse for all iterations)\n");
111    }
112    
113    if use_cache {
114        println!("♻️  Mode: Cache (reuse JSONEval instance across iterations)\n");
115    }
116    
117    if let Some(count) = concurrent_count {
118        println!("🔀 Concurrent evaluations: {} threads\n", count);
119    } else if iterations > 1 {
120        println!("🔄 Iterations per scenario: {}\n", iterations);
121    }
122    
123    if enable_comparison {
124        println!("🔍 Comparison: enabled");
125    }
126    if show_timing {
127        println!("⏱️  Internal timing: enabled");
128    }
129    if enable_comparison || show_timing {
130        println!();
131    }
132
133    let samples_dir = Path::new("samples");
134    let mut scenarios = common::discover_scenarios(samples_dir);
135    
136    // Filter scenarios if a filter is provided
137    if let Some(ref filter) = scenario_filter {
138        scenarios.retain(|s| s.name.contains(filter));
139        println!("📋 Filtering scenarios matching: '{}'\n", filter);
140    }
141
142    if scenarios.is_empty() {
143        if let Some(filter) = scenario_filter {
144            println!(
145                "ℹ️  No scenarios found matching '{}' in `{}`.",
146                filter,
147                samples_dir.display()
148            );
149        } else {
150            println!(
151                "ℹ️  No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
152                samples_dir.display()
153            );
154        }
155        return;
156    }
157    
158    println!("📊 Found {} scenario(s)\n", scenarios.len());
159
160    let mut total_parse_time = std::time::Duration::ZERO;
161    let mut total_eval_time = std::time::Duration::ZERO;
162    let mut successful_scenarios = 0;
163    let mut comparison_failures = 0;
164
165    for scenario in &scenarios {
166        println!("==============================");
167        println!("Scenario: {}", scenario.name);
168        println!("Schema: {} ({})", 
169            scenario.schema_path.display(),
170            if scenario.is_msgpack { "MessagePack" } else { "JSON" }
171        );
172        println!("Data: {}\n", scenario.data_path.display());
173
174        // Clear timing data from previous scenarios
175        if show_timing {
176            json_eval_rs::enable_timing();
177            json_eval_rs::clear_timing_data();
178        }
179
180        let data_str = fs::read_to_string(&scenario.data_path)
181            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
182
183        println!("Running evaluation...\n");
184
185        let (parse_time, eval_time, evaluated_schema, eval, iteration_times) = if use_parsed_schema {
186            // ParsedSchema mode: parse once, reuse for all iterations/threads
187            let start_time = Instant::now();
188            
189            let parsed_schema = if scenario.is_msgpack {
190                let schema_msgpack = fs::read(&scenario.schema_path)
191                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
192                println!("  📦 MessagePack schema size: {} bytes", schema_msgpack.len());
193                Arc::new(ParsedSchema::parse_msgpack(&schema_msgpack)
194                    .unwrap_or_else(|e| panic!("failed to parse MessagePack schema: {}", e)))
195            } else {
196                let schema_str = fs::read_to_string(&scenario.schema_path)
197                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
198                Arc::new(ParsedSchema::parse(&schema_str)
199                    .unwrap_or_else(|e| panic!("failed to parse schema: {}", e)))
200            };
201            
202            let parse_time = start_time.elapsed();
203            println!("  Schema parsing & compilation: {:?}", parse_time);
204            
205            // Concurrent mode with ParsedSchema
206            if let Some(thread_count) = concurrent_count {
207                use std::thread;
208                
209                let eval_start = Instant::now();
210                let mut handles = vec![];
211                
212                for thread_id in 0..thread_count {
213                    let parsed_clone = parsed_schema.clone();
214                    let data_str_clone = data_str.clone();
215                    let iter_count = iterations;
216                    let thread_use_cache = use_cache;
217                    
218                    let handle = thread::spawn(move || {
219                        let mut thread_times = Vec::with_capacity(iter_count);
220                        let mut last_schema = Value::Null;
221                        
222                        let mut eval_instance = JSONEval::with_parsed_schema(
223                            parsed_clone.clone(),
224                            Some("{}"),
225                            Some(&data_str_clone)
226                        ).unwrap();
227                        
228                        for iter in 0..iter_count {
229                            let iter_start = Instant::now();
230                            
231                            if !thread_use_cache && iter > 0 {
232                                eval_instance = JSONEval::with_parsed_schema(
233                                    parsed_clone.clone(),
234                                    Some("{}"),
235                                    Some(&data_str_clone)
236                                ).unwrap();
237                            }
238                            
239                            eval_instance.evaluate(&data_str_clone, Some("{}"), None, None).unwrap();
240                            last_schema = eval_instance.get_evaluated_schema(false);
241                            thread_times.push(iter_start.elapsed());
242                        }
243                        
244                        (thread_times, last_schema, thread_id)
245                    });
246                    handles.push(handle);
247                }
248                
249                let mut all_iteration_times = Vec::new();
250                let mut evaluated_schema = Value::Null;
251                
252                for handle in handles {
253                    let (thread_times, thread_schema, thread_id) = handle.join().unwrap();
254                    println!("  Thread {} completed {} iterations", thread_id, thread_times.len());
255                    all_iteration_times.extend(thread_times);
256                    evaluated_schema = thread_schema; // Use last thread's result
257                }
258                
259                let eval_time = eval_start.elapsed();
260                
261                // Create a temp eval for metadata export
262                let temp_eval = JSONEval::with_parsed_schema(
263                    parsed_schema.clone(),
264                    Some("{}"),
265                    Some(&data_str)
266                ).unwrap();
267                
268                (parse_time, eval_time, evaluated_schema, temp_eval, all_iteration_times)
269            } else {
270                // Sequential iterations with ParsedSchema
271                let eval_start = Instant::now();
272                let mut evaluated_schema = Value::Null;
273                let mut iteration_times = Vec::with_capacity(iterations);
274                let mut eval_instance = JSONEval::with_parsed_schema(
275                    parsed_schema.clone(),
276                    Some("{}"),
277                    Some(&data_str)
278                ).unwrap();
279                
280                for iter in 0..iterations {
281                    let iter_start = Instant::now();
282                    
283                    if !use_cache && iter > 0 {
284                        eval_instance = JSONEval::with_parsed_schema(
285                            parsed_schema.clone(),
286                            Some("{}"),
287                            Some(&data_str)
288                        ).unwrap();
289                    }
290                    
291                    eval_instance.evaluate(&data_str, Some("{}"), None, None)
292                        .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
293                    evaluated_schema = eval_instance.get_evaluated_schema(false);
294                    iteration_times.push(iter_start.elapsed());
295                    
296                    if iterations > 1 && (iter + 1) % 10 == 0 {
297                        print!(".");
298                        if (iter + 1) % 50 == 0 {
299                            println!(" {}/{}", iter + 1, iterations);
300                        }
301                    }
302                }
303                
304                if iterations > 1 && iterations % 50 != 0 {
305                    println!(" {}/{}", iterations, iterations);
306                }
307                
308                let eval_time = eval_start.elapsed();
309                (parse_time, eval_time, evaluated_schema, eval_instance, iteration_times)
310            }
311        } else {
312            // Traditional mode: parse and create JSONEval each time
313            let schema_msgpack = if scenario.is_msgpack {
314                let bytes = fs::read(&scenario.schema_path)
315                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
316                println!("  📦 MessagePack schema size: {} bytes", bytes.len());
317                Some(bytes)
318            } else {
319                None
320            };
321            
322            let schema_str = if !scenario.is_msgpack {
323                Some(fs::read_to_string(&scenario.schema_path)
324                    .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e)))
325            } else {
326                None
327            };
328
329            let start_time = Instant::now();
330            let mut eval = if scenario.is_msgpack {
331                JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
332                    .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
333            } else {
334                JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
335                    .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
336            };
337            let parse_time = start_time.elapsed();
338            println!("  Schema parsing & compilation: {:?}", parse_time);
339            
340            let eval_start = Instant::now();
341            let mut evaluated_schema = Value::Null;
342            let mut iteration_times = Vec::with_capacity(iterations);
343            
344            for iter in 0..iterations {
345                let iter_start = Instant::now();
346                
347                if !use_cache && iter > 0 {
348                    eval = if scenario.is_msgpack {
349                        JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
350                            .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
351                    } else {
352                        JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
353                            .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
354                    };
355                }
356                
357                eval.evaluate(&data_str, Some("{}"), None, None)
358                    .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
359                evaluated_schema = eval.get_evaluated_schema(false);
360                iteration_times.push(iter_start.elapsed());
361                
362                if iterations > 1 && (iter + 1) % 10 == 0 {
363                    print!(".");
364                    if (iter + 1) % 50 == 0 {
365                        println!(" {}/{}", iter + 1, iterations);
366                    }
367                }
368            }
369            
370            if iterations > 1 && iterations % 50 != 0 {
371                println!(" {}/{}", iterations, iterations);
372            }
373            
374            let eval_time = eval_start.elapsed();
375            (parse_time, eval_time, evaluated_schema, eval, iteration_times)
376        };
377        
378        // Calculate statistics
379        let total_iterations = iteration_times.len();
380        if total_iterations == 1 {
381            println!("  Evaluation: {:?}", eval_time);
382        } else {
383            let avg_time = eval_time / total_iterations as u32;
384            let min_time = iteration_times.iter().min().unwrap();
385            let max_time = iteration_times.iter().max().unwrap();
386            
387            println!("  Total evaluation time: {:?}", eval_time);
388            println!("  Total iterations: {}", total_iterations);
389            println!("  Average per iteration: {:?}", avg_time);
390            println!("  Min: {:?} | Max: {:?}", min_time, max_time);
391            
392            // Show cache statistics
393            let cache_stats = eval.cache_stats();
394            println!("  Cache: {} entries, {} hits, {} misses ({:.1}% hit rate)",
395                cache_stats.entries,
396                cache_stats.hits,
397                cache_stats.misses,
398                cache_stats.hit_rate * 100.0
399            );
400        }
401
402        let total_time = parse_time + eval_time;
403        println!("⏱️  Execution time: {:?}\n", total_time);
404        
405        // Print detailed timing breakdown if --timing flag is set
406        if show_timing {
407            json_eval_rs::print_timing_summary();
408        }
409        
410        // Track statistics
411        total_parse_time += parse_time;
412        total_eval_time += eval_time;
413        successful_scenarios += 1;
414
415        let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
416        let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
417
418        fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
419            .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
420
421        let mut metadata_obj = Map::new();
422        metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
423        metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
424
425        fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
426            .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
427
428        println!("✅ Results saved:");
429        println!("  - {}", evaluated_path.display());
430        println!("  - {}\n", parsed_path.display());
431
432        // Optional comparison
433        if enable_comparison {
434            if let Some(comp_path) = &scenario.comparison_path {
435                if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
436                    comparison_failures += 1;
437                }
438                println!();
439            }
440        }
441    }
442    
443    // Print summary statistics
444    if successful_scenarios > 0 {
445        println!("\n{}", "=".repeat(50));
446        println!("📊 Summary Statistics");
447        println!("{}", "=".repeat(50));
448        println!("Total scenarios run: {}", successful_scenarios);
449        println!("Total parsing time: {:?}", total_parse_time);
450        println!("Total evaluation time: {:?}", total_eval_time);
451        println!("Total time: {:?}", total_parse_time + total_eval_time);
452        
453        if successful_scenarios > 1 {
454            println!("\nAverage per scenario:");
455            println!("  Parsing: {:?}", total_parse_time / successful_scenarios as u32);
456            println!("  Evaluation: {:?}", total_eval_time / successful_scenarios as u32);
457        }
458        
459        if enable_comparison {
460            println!("\nComparison failures: {}", comparison_failures);
461        }
462        
463        println!("\n✅ All scenarios completed successfully!\n");
464    }
465}
Source

pub fn reload_schema( &mut self, schema: &str, context: Option<&str>, data: Option<&str>, ) -> Result<(), String>

Source

pub fn set_timezone_offset(&mut self, offset_minutes: Option<i32>)

Set the timezone offset for datetime operations (TODAY, NOW)

This method updates the RLogic engine configuration with a new timezone offset. The offset will be applied to all subsequent datetime evaluations.

§Arguments
  • offset_minutes - Timezone offset in minutes from UTC (e.g., 420 for UTC+7, -300 for UTC-5) Pass None to reset to UTC (no offset)
§Example
let mut eval = JSONEval::new(schema, None, None)?;

// Set to UTC+7 (Jakarta, Bangkok)
eval.set_timezone_offset(Some(420));

// Reset to UTC
eval.set_timezone_offset(None);
Source

pub fn reload_schema_msgpack( &mut self, schema_msgpack: &[u8], context: Option<&str>, data: Option<&str>, ) -> Result<(), String>

Reload schema from MessagePack-encoded bytes

§Arguments
  • schema_msgpack - MessagePack-encoded schema bytes
  • context - Optional context data JSON string
  • data - Optional initial data JSON string
§Returns

A Result indicating success or an error message

Source

pub fn reload_schema_parsed( &mut self, parsed: Arc<ParsedSchema>, context: Option<&str>, data: Option<&str>, ) -> Result<(), String>

Reload schema from a cached ParsedSchema

This is the most efficient way to reload as it reuses pre-parsed schema compilation.

§Arguments
  • parsed - Arc reference to a cached ParsedSchema
  • context - Optional context data JSON string
  • data - Optional initial data JSON string
§Returns

A Result indicating success or an error message

Source

pub fn reload_schema_from_cache( &mut self, cache_key: &str, context: Option<&str>, data: Option<&str>, ) -> Result<(), String>

Reload schema from ParsedSchemaCache using a cache key

This is the recommended way for cross-platform cached schema reloading.

§Arguments
  • cache_key - Key to lookup in the global ParsedSchemaCache
  • context - Optional context data JSON string
  • data - Optional initial data JSON string
§Returns

A Result indicating success or an error message

Source§

impl JSONEval

Source

pub fn resolve_layout(&mut self, evaluate: bool) -> Result<(), String>

Resolve layout references with optional evaluation

§Arguments
  • evaluate - If true, runs evaluation before resolving layout. If false, only resolves layout.
§Returns

A Result indicating success or an error message.

Source§

impl JSONEval

Source

pub fn run_logic( &mut self, logic_id: CompiledLogicId, data: Option<&Value>, context: Option<&Value>, ) -> Result<Value, String>

Run pre-compiled logic against current data

Source

pub fn compile_logic(&self, logic_str: &str) -> Result<CompiledLogicId, String>

Compile a logic expression from a JSON string and store it globally

Source

pub fn compile_logic_value( &self, logic: &Value, ) -> Result<CompiledLogicId, String>

Compile a logic expression from a Value and store it globally

Source

pub fn compile_and_run_logic( &mut self, logic_str: &str, data: Option<&str>, context: Option<&str>, ) -> Result<Value, String>

Compile and run logic in one go (convenience method)

Source§

impl JSONEval

Source

pub fn evaluate_subform( &mut self, subform_path: &str, data: &str, context: Option<&str>, paths: Option<&[String]>, token: Option<&CancellationToken>, ) -> Result<(), String>

Evaluate a subform with data Evaluate a subform with data and optional selective paths

Source

pub fn validate_subform( &mut self, subform_path: &str, data: &str, context: Option<&str>, paths: Option<&[String]>, token: Option<&CancellationToken>, ) -> Result<ValidationResult, String>

Validate subform data against its schema rules

Source

pub fn evaluate_dependents_subform( &mut self, subform_path: &str, changed_paths: &[String], data: Option<&str>, context: Option<&str>, re_evaluate: bool, token: Option<&CancellationToken>, canceled_paths: Option<&mut Vec<String>>, ) -> Result<Value, String>

Evaluate dependents in subform when a field changes

Source

pub fn resolve_layout_subform( &mut self, subform_path: &str, evaluate: bool, ) -> Result<(), String>

Resolve layout for subform

Source

pub fn get_evaluated_schema_subform( &mut self, subform_path: &str, resolve_layout: bool, ) -> Value

Get evaluated schema from subform

Source

pub fn get_schema_value_subform(&mut self, subform_path: &str) -> Value

Get schema value from subform in nested object format (all .value fields).

Source

pub fn get_schema_value_array_subform(&self, subform_path: &str) -> Value

Get schema values from subform as a flat array of path-value pairs.

Source

pub fn get_schema_value_object_subform(&self, subform_path: &str) -> Value

Get schema values from subform as a flat object with dotted path keys.

Source

pub fn get_evaluated_schema_without_params_subform( &mut self, subform_path: &str, resolve_layout: bool, ) -> Value

Get evaluated schema without $params from subform

Source

pub fn get_evaluated_schema_by_path_subform( &mut self, subform_path: &str, schema_path: &str, skip_layout: bool, ) -> Option<Value>

Get evaluated schema by specific path from subform

Source

pub fn get_evaluated_schema_by_paths_subform( &mut self, subform_path: &str, schema_paths: &[String], skip_layout: bool, format: Option<ReturnFormat>, ) -> Value

Get evaluated schema by multiple paths from subform

Source

pub fn get_schema_by_path_subform( &self, subform_path: &str, schema_path: &str, ) -> Option<Value>

Get schema by specific path from subform

Source

pub fn get_schema_by_paths_subform( &self, subform_path: &str, schema_paths: &[String], format: Option<ReturnFormat>, ) -> Value

Get schema by multiple paths from subform

Source

pub fn get_subform_paths(&self) -> Vec<String>

Get list of available subform paths

Source

pub fn has_subform(&self, subform_path: &str) -> bool

Check if a subform exists at the given path

Source§

impl JSONEval

Source

pub fn validate( &mut self, data: &str, context: Option<&str>, paths: Option<&[String]>, token: Option<&CancellationToken>, ) -> Result<ValidationResult, String>

Validate data against schema rules

Examples found in repository?
examples/basic.rs (line 144)
28fn main() {
29    let args: Vec<String> = std::env::args().collect();
30    let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic");
31    
32    let mut scenario_filter: Option<String> = None;
33    let mut enable_comparison = false;
34    let mut show_timing = false;
35    let mut i = 1;
36    
37    // Parse arguments
38    while i < args.len() {
39        let arg = &args[i];
40        
41        if arg == "-h" || arg == "--help" {
42            print_help(program_name);
43            return;
44        } else if arg == "--compare" {
45            enable_comparison = true;
46        } else if arg == "--timing" {
47            show_timing = true;
48        } else if !arg.starts_with('-') {
49            scenario_filter = Some(arg.clone());
50        } else {
51            eprintln!("Error: unknown option '{}'", arg);
52            print_help(program_name);
53            return;
54        }
55        
56        i += 1;
57    }
58    
59    println!("\n🚀 JSON Evaluation - Basic Example (JSON Schema)\n");
60    
61    if enable_comparison {
62        println!("🔍 Comparison: enabled");
63    }
64    if show_timing {
65        println!("⏱️  Internal timing: enabled");
66    }
67    if enable_comparison || show_timing {
68        println!();
69    }
70    
71    let samples_dir = Path::new("samples");
72    let mut scenarios = common::discover_scenarios(samples_dir);
73    
74    // Filter out MessagePack scenarios - only use JSON
75    scenarios.retain(|s| !s.is_msgpack);
76    
77    // Filter scenarios if a filter is provided
78    if let Some(ref filter) = scenario_filter {
79        scenarios.retain(|s| s.name.contains(filter));
80        println!("📋 Filtering scenarios matching: '{}'\n", filter);
81    }
82
83    if scenarios.is_empty() {
84        if let Some(filter) = scenario_filter {
85            println!(
86                "ℹ️  No scenarios found matching '{}' in `{}`.",
87                filter,
88                samples_dir.display()
89            );
90        } else {
91            println!(
92                "ℹ️  No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
93                samples_dir.display()
94            );
95        }
96        return;
97    }
98    
99    println!("📊 Found {} scenario(s)\n", scenarios.len());
100
101    let mut total_parse_time = std::time::Duration::ZERO;
102    let mut total_eval_time = std::time::Duration::ZERO;
103    let mut successful_scenarios = 0;
104    let mut comparison_failures = 0;
105
106    for scenario in &scenarios {
107        println!("==============================");
108        println!("Scenario: {}", scenario.name);
109        println!("Schema: {} ({})", 
110            scenario.schema_path.display(),
111            if scenario.is_msgpack { "MessagePack" } else { "JSON" }
112        );
113        println!("Data: {}\n", scenario.data_path.display());
114
115        // Clear timing data from previous scenarios
116        if show_timing {
117            json_eval_rs::enable_timing();
118            json_eval_rs::clear_timing_data();
119        }
120
121        let data_str = fs::read_to_string(&scenario.data_path)
122            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
123
124        // Step 1: Parse schema (JSONEval::new)
125        let parse_start = Instant::now();
126        
127        let schema_str = fs::read_to_string(&scenario.schema_path)
128            .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
129        
130        let mut eval = JSONEval::new(&schema_str, None, Some(&data_str))
131            .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e));
132        
133        let parse_time = parse_start.elapsed();
134        println!("  📝 Parse (new): {:?}", parse_time);
135        
136        // Step 2: Evaluate
137        let eval_start = Instant::now();
138        
139        eval.evaluate(&data_str, Some("{}"), None, None)
140            .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
141
142        // Step 3: Validate
143        let validation_start = Instant::now();
144        let validation_result = eval.validate(&data_str, None, None, None)
145            .unwrap_or_else(|e| panic!("validation failed: {}", e));
146        let validation_time = validation_start.elapsed();
147        println!("  🛡️ Validate: {:?}", validation_time);
148        
149        // Legacy behavior: get_evaluated_schema takes skip_layout: bool
150        // We pass false to ensure layout IS resolved
151        let evaluated_schema = eval.get_evaluated_schema(false);
152        let schema_value = eval.get_schema_value();
153        let eval_time = eval_start.elapsed();
154        
155        println!("  ⚡ Eval: {:?}", eval_time);
156        println!("  ⏱️  Total: {:?}\n", parse_time + eval_time);
157        
158        // Print detailed timing breakdown if --timing flag is set
159        if show_timing {
160            json_eval_rs::print_timing_summary();
161        }
162        
163        total_parse_time += parse_time;
164        total_eval_time += eval_time;
165        successful_scenarios += 1;
166
167        // Save results
168        let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
169        let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
170        let value_path = samples_dir.join(format!("{}-schema-value.json", scenario.name));
171        let validation_path = samples_dir.join(format!("{}-validation.json", scenario.name));
172
173        fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
174            .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
175
176        let mut metadata_obj = Map::new();
177        metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
178        metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
179        metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
180
181        fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
182            .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
183
184        fs::write(&value_path, common::pretty_json(&schema_value))
185            .unwrap_or_else(|e| panic!("failed to write {}: {}", value_path.display(), e));
186
187        let validation_value = serde_json::to_value(&validation_result)
188            .unwrap_or_else(|e| panic!("failed to serialize validation result: {}", e));
189        fs::write(&validation_path, common::pretty_json(&validation_value))
190            .unwrap_or_else(|e| panic!("failed to write {}: {}", validation_path.display(), e));
191
192        println!("✅ Results saved:");
193        println!("  - {}", evaluated_path.display());
194        println!("  - {}", parsed_path.display());
195        println!("  - {}", value_path.display());
196        println!("  - {}\n", validation_path.display());
197
198        // Optional comparison
199        if enable_comparison {
200            if let Some(comp_path) = &scenario.comparison_path {
201                if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
202                    comparison_failures += 1;
203                }
204                println!();
205            }
206        }
207    }
208    
209    // Print summary
210    println!("{}", "=".repeat(50));
211    println!("📊 Summary");
212    println!("{}", "=".repeat(50));
213    println!("Total scenarios run: {}", successful_scenarios);
214    println!("Total parse time: {:?}", total_parse_time);
215    println!("Total eval time: {:?}", total_eval_time);
216    println!("Total time: {:?}", total_parse_time + total_eval_time);
217    
218    if successful_scenarios > 1 {
219        println!("\nAverage per scenario:");
220        println!("  Parse: {:?}", total_parse_time / successful_scenarios as u32);
221        println!("  Eval: {:?}", total_eval_time / successful_scenarios as u32);
222    }
223    
224    if enable_comparison {
225        println!("Comparison failures: {}", comparison_failures);
226    }
227    
228    println!("\n✅ All scenarios completed!\n");
229}

Trait Implementations§

Source§

impl Clone for JSONEval

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.