Skip to main content

JSONEval

Struct JSONEval 

Source
pub struct JSONEval {
Show 25 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 dep_formula_triggers: Arc<IndexMap<String, Vec<(String, usize)>>>, pub conditional_hidden_fields: Arc<Vec<String>>, pub conditional_readonly_fields: Arc<Vec<String>>, pub static_arrays: Arc<IndexMap<String, Arc<Value>>>, pub context: Value, pub data: Value, pub evaluated_schema: Value, pub eval_data: EvalData, pub eval_cache: EvalCache, /* 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>>>§dep_formula_triggers: Arc<IndexMap<String, Vec<(String, usize)>>>

Reverse map: data path → list of source field schema paths whose dependent value/clear formulas reference that path (excluding $value/$refValue context vars). When field X changes, source fields in dep_formula_triggers[X] are re-queued so their downstream dependents are re-evaluated with the new context.

§conditional_hidden_fields: Arc<Vec<String>>§conditional_readonly_fields: Arc<Vec<String>>§static_arrays: Arc<IndexMap<String, Arc<Value>>>§context: Value§data: Value§evaluated_schema: Value§eval_data: EvalData§eval_cache: EvalCache

Implementations§

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

Evaluate fields that depend on a changed path. Processes all dependent fields transitively, then optionally performs a full re-evaluation pass (for read-only / hide effects) and cascades into subforms.

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

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

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

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

The path may include a trailing item index to bind the evaluation to a specific array element and enable the two-tier cache-swap strategy automatically:

// Evaluate riders item 1 with index-aware cache
eval.evaluate_subform("illustration.product_benefit.riders.1", data, ctx, None, None)?;

Without a trailing index, the subform is evaluated in isolation (no cache swap).

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.

Supports the same trailing-index path syntax as evaluate_subform. When an index is present the parent cache is swapped in first, ensuring rule evaluations that depend on $params tables share already-computed parent-form results.

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>>, include_subforms: bool, ) -> Result<Value, String>

Evaluate dependents in a subform when a field changes.

Supports the same trailing-index path syntax as evaluate_subform. When an index is present the parent cache is swapped in, so dependent evaluation runs with Tier-2 entries visible and item-scoped version bumps propagate to eval_generation.

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

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.