pub struct JSONEval {Show 24 fields
pub schema: Arc<Value>,
pub engine: Arc<RLogic>,
pub evaluations: Arc<IndexMap<String, LogicId>>,
pub tables: Arc<IndexMap<String, Value>>,
pub table_metadata: Arc<IndexMap<String, TableMetadata>>,
pub dependencies: Arc<IndexMap<String, IndexSet<String>>>,
pub sorted_evaluations: Arc<Vec<Vec<String>>>,
pub dependents_evaluations: Arc<IndexMap<String, Vec<DependentItem>>>,
pub rules_evaluations: Arc<Vec<String>>,
pub fields_with_rules: Arc<Vec<String>>,
pub others_evaluations: Arc<Vec<String>>,
pub value_evaluations: Arc<Vec<String>>,
pub layout_paths: Arc<Vec<String>>,
pub options_templates: Arc<Vec<(String, String, String)>>,
pub subforms: IndexMap<String, Box<JSONEval>>,
pub reffed_by: Arc<IndexMap<String, Vec<String>>>,
pub conditional_hidden_fields: Arc<Vec<String>>,
pub conditional_readonly_fields: Arc<Vec<String>>,
pub context: Value,
pub data: Value,
pub evaluated_schema: Value,
pub eval_data: EvalData,
pub eval_cache: EvalCache,
pub cache_enabled: bool,
/* private fields */
}Fields§
§schema: Arc<Value>§engine: Arc<RLogic>§evaluations: Arc<IndexMap<String, LogicId>>§tables: Arc<IndexMap<String, Value>>§table_metadata: Arc<IndexMap<String, TableMetadata>>§dependencies: Arc<IndexMap<String, IndexSet<String>>>§sorted_evaluations: Arc<Vec<Vec<String>>>§dependents_evaluations: Arc<IndexMap<String, Vec<DependentItem>>>§rules_evaluations: Arc<Vec<String>>§fields_with_rules: Arc<Vec<String>>§others_evaluations: Arc<Vec<String>>§value_evaluations: Arc<Vec<String>>§layout_paths: Arc<Vec<String>>§options_templates: Arc<Vec<(String, String, String)>>§subforms: IndexMap<String, Box<JSONEval>>§reffed_by: Arc<IndexMap<String, Vec<String>>>§conditional_readonly_fields: Arc<Vec<String>>§context: Value§data: Value§evaluated_schema: Value§eval_data: EvalData§eval_cache: EvalCache§cache_enabled: boolImplementations§
Source§impl JSONEval
impl JSONEval
Sourcepub fn should_cache_dependency(&self, key: &str) -> bool
pub fn should_cache_dependency(&self, key: &str) -> bool
Check if a dependency should be part of the cache key Check if a dependency should be cached Caches everything except keys starting with $ (except $context)
Sourcepub fn purge_cache_for_changed_data_with_comparison(
&self,
old_data: &Value,
new_data: &Value,
)
pub fn purge_cache_for_changed_data_with_comparison( &self, old_data: &Value, new_data: &Value, )
Compares old vs new values by deep diffing and purges affected entries
Sourcepub fn purge_cache_for_changed_data(&self, changed_data_paths: &[String])
pub fn purge_cache_for_changed_data(&self, changed_data_paths: &[String])
Selectively purge cache entries that depend on changed data paths Finds all eval_keys that depend on the changed paths and removes them Selectively purge cache entries that depend on changed data paths Simpler version without value comparison for cases where we don’t have old data
Sourcepub fn purge_cache_for_context_change(&self)
pub fn purge_cache_for_context_change(&self)
Purge cache entries affected by context changes Purge cache entries that depend on context
Sourcepub fn cache_stats(&self) -> CacheStats
pub fn cache_stats(&self) -> CacheStats
Get cache statistics
Examples found in repository?
4fn main() {
5 let schema = json!({
6 "type": "object",
7 "properties": {
8 "price": {
9 "type": "number"
10 },
11 "tax": {
12 "type": "number",
13 "value": {
14 "$evaluation": {
15 "*": [
16 { "$ref": "#/properties/price" },
17 0.1
18 ]
19 }
20 }
21 },
22 "total": {
23 "type": "number",
24 "value": {
25 "$evaluation": {
26 "+": [
27 { "$ref": "#/properties/price" },
28 { "$ref": "#/properties/tax" }
29 ]
30 }
31 }
32 }
33 }
34 });
35
36 let schema_str = serde_json::to_string(&schema).unwrap();
37
38 println!("=== Example 1: With Caching (Default) ===");
39 {
40 let data = json!({ "price": 100 });
41 let data_str = serde_json::to_string(&data).unwrap();
42
43 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
44
45 println!("Cache enabled: {}", eval.is_cache_enabled());
46 println!("Initial cache size: {}", eval.cache_len());
47
48 eval.evaluate(&data_str, None, None, None).unwrap();
49
50 println!("After evaluation cache size: {}", eval.cache_len());
51 let stats = eval.cache_stats();
52 println!("Cache stats: {}", stats);
53 }
54
55 println!("\n=== Example 2: Without Caching (Web API Mode) ===");
56 {
57 let data = json!({ "price": 200 });
58 let data_str = serde_json::to_string(&data).unwrap();
59
60 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
61
62 // Disable caching for single-use web API scenario
63 eval.disable_cache();
64
65 println!("Cache enabled: {}", eval.is_cache_enabled());
66 println!("Initial cache size: {}", eval.cache_len());
67
68 eval.evaluate(&data_str, None, None, None).unwrap();
69
70 println!("After evaluation cache size: {}", eval.cache_len());
71 let stats = eval.cache_stats();
72 println!("Cache stats: {}", stats);
73
74 println!("\n✅ No cache overhead - perfect for web APIs!");
75 }
76
77 println!("\n=== Example 3: Re-enabling Cache ===");
78 {
79 let data = json!({ "price": 300 });
80 let data_str = serde_json::to_string(&data).unwrap();
81
82 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
83
84 // Disable then re-enable
85 eval.disable_cache();
86 eval.enable_cache();
87
88 println!("Cache enabled: {}", eval.is_cache_enabled());
89 eval.evaluate(&data_str, None, None, None).unwrap();
90
91 println!("Cache size after evaluation: {}", eval.cache_len());
92 println!("\n✅ Cache can be toggled as needed!");
93 }
94}More examples
32fn main() {
33 let args: Vec<String> = std::env::args().collect();
34 let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("benchmark");
35
36 let mut iterations = 1usize;
37 let mut scenario_filter: Option<String> = None;
38 let mut show_cpu_info = false;
39 let mut use_parsed_schema = false;
40 let mut use_cache = false;
41 let mut concurrent_count: Option<usize> = None;
42 let mut enable_comparison = false;
43 let mut show_timing = false;
44 let mut i = 1;
45
46 // Parse arguments
47 while i < args.len() {
48 let arg = &args[i];
49
50 if arg == "-h" || arg == "--help" {
51 print_help(program_name);
52 return;
53 } else if arg == "--cpu-info" {
54 show_cpu_info = true;
55 } else if arg == "--parsed" {
56 use_parsed_schema = true;
57 } else if arg == "--cache" {
58 use_cache = true;
59 } else if arg == "--compare" {
60 enable_comparison = true;
61 } else if arg == "--timing" {
62 show_timing = true;
63 } else if arg == "--concurrent" {
64 if i + 1 >= args.len() {
65 eprintln!("Error: {} requires a value", arg);
66 print_help(program_name);
67 return;
68 }
69 i += 1;
70 match args[i].parse::<usize>() {
71 Ok(n) if n > 0 => concurrent_count = Some(n),
72 _ => {
73 eprintln!("Error: concurrent count must be a positive integer, got '{}'", args[i]);
74 return;
75 }
76 }
77 } else if arg == "-i" || arg == "--iterations" {
78 if i + 1 >= args.len() {
79 eprintln!("Error: {} requires a value", arg);
80 print_help(program_name);
81 return;
82 }
83 i += 1;
84 match args[i].parse::<usize>() {
85 Ok(n) if n > 0 => iterations = n,
86 _ => {
87 eprintln!("Error: iterations must be a positive integer, got '{}'", args[i]);
88 return;
89 }
90 }
91 } else if !arg.starts_with('-') {
92 scenario_filter = Some(arg.clone());
93 } else {
94 eprintln!("Error: unknown option '{}'", arg);
95 print_help(program_name);
96 return;
97 }
98
99 i += 1;
100 }
101
102 println!("\n🚀 JSON Evaluation - Benchmark\n");
103
104 // Show CPU info if requested or if running benchmarks
105 if show_cpu_info || iterations > 1 || concurrent_count.is_some() {
106 common::print_cpu_info();
107 }
108
109 if use_parsed_schema {
110 println!("📦 Mode: ParsedSchema (parse once, reuse for all iterations)\n");
111 }
112
113 if use_cache {
114 println!("♻️ Mode: Cache (reuse JSONEval instance across iterations)\n");
115 }
116
117 if let Some(count) = concurrent_count {
118 println!("🔀 Concurrent evaluations: {} threads\n", count);
119 } else if iterations > 1 {
120 println!("🔄 Iterations per scenario: {}\n", iterations);
121 }
122
123 if enable_comparison {
124 println!("🔍 Comparison: enabled");
125 }
126 if show_timing {
127 println!("⏱️ Internal timing: enabled");
128 }
129 if enable_comparison || show_timing {
130 println!();
131 }
132
133 let samples_dir = Path::new("samples");
134 let mut scenarios = common::discover_scenarios(samples_dir);
135
136 // Filter scenarios if a filter is provided
137 if let Some(ref filter) = scenario_filter {
138 scenarios.retain(|s| s.name.contains(filter));
139 println!("📋 Filtering scenarios matching: '{}'\n", filter);
140 }
141
142 if scenarios.is_empty() {
143 if let Some(filter) = scenario_filter {
144 println!(
145 "ℹ️ No scenarios found matching '{}' in `{}`.",
146 filter,
147 samples_dir.display()
148 );
149 } else {
150 println!(
151 "ℹ️ No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
152 samples_dir.display()
153 );
154 }
155 return;
156 }
157
158 println!("📊 Found {} scenario(s)\n", scenarios.len());
159
160 let mut total_parse_time = std::time::Duration::ZERO;
161 let mut total_eval_time = std::time::Duration::ZERO;
162 let mut successful_scenarios = 0;
163 let mut comparison_failures = 0;
164
165 for scenario in &scenarios {
166 println!("==============================");
167 println!("Scenario: {}", scenario.name);
168 println!("Schema: {} ({})",
169 scenario.schema_path.display(),
170 if scenario.is_msgpack { "MessagePack" } else { "JSON" }
171 );
172 println!("Data: {}\n", scenario.data_path.display());
173
174 // Clear timing data from previous scenarios
175 if show_timing {
176 json_eval_rs::enable_timing();
177 json_eval_rs::clear_timing_data();
178 }
179
180 let data_str = fs::read_to_string(&scenario.data_path)
181 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
182
183 println!("Running evaluation...\n");
184
185 let (parse_time, eval_time, evaluated_schema, eval, iteration_times) = if use_parsed_schema {
186 // ParsedSchema mode: parse once, reuse for all iterations/threads
187 let start_time = Instant::now();
188
189 let parsed_schema = if scenario.is_msgpack {
190 let schema_msgpack = fs::read(&scenario.schema_path)
191 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
192 println!(" 📦 MessagePack schema size: {} bytes", schema_msgpack.len());
193 Arc::new(ParsedSchema::parse_msgpack(&schema_msgpack)
194 .unwrap_or_else(|e| panic!("failed to parse MessagePack schema: {}", e)))
195 } else {
196 let schema_str = fs::read_to_string(&scenario.schema_path)
197 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
198 Arc::new(ParsedSchema::parse(&schema_str)
199 .unwrap_or_else(|e| panic!("failed to parse schema: {}", e)))
200 };
201
202 let parse_time = start_time.elapsed();
203 println!(" Schema parsing & compilation: {:?}", parse_time);
204
205 // Concurrent mode with ParsedSchema
206 if let Some(thread_count) = concurrent_count {
207 use std::thread;
208
209 let eval_start = Instant::now();
210 let mut handles = vec![];
211
212 for thread_id in 0..thread_count {
213 let parsed_clone = parsed_schema.clone();
214 let data_str_clone = data_str.clone();
215 let iter_count = iterations;
216 let thread_use_cache = use_cache;
217
218 let handle = thread::spawn(move || {
219 let mut thread_times = Vec::with_capacity(iter_count);
220 let mut last_schema = Value::Null;
221
222 let mut eval_instance = JSONEval::with_parsed_schema(
223 parsed_clone.clone(),
224 Some("{}"),
225 Some(&data_str_clone)
226 ).unwrap();
227
228 for iter in 0..iter_count {
229 let iter_start = Instant::now();
230
231 if !thread_use_cache && iter > 0 {
232 eval_instance = JSONEval::with_parsed_schema(
233 parsed_clone.clone(),
234 Some("{}"),
235 Some(&data_str_clone)
236 ).unwrap();
237 }
238
239 eval_instance.evaluate(&data_str_clone, Some("{}"), None, None).unwrap();
240 last_schema = eval_instance.get_evaluated_schema(false);
241 thread_times.push(iter_start.elapsed());
242 }
243
244 (thread_times, last_schema, thread_id)
245 });
246 handles.push(handle);
247 }
248
249 let mut all_iteration_times = Vec::new();
250 let mut evaluated_schema = Value::Null;
251
252 for handle in handles {
253 let (thread_times, thread_schema, thread_id) = handle.join().unwrap();
254 println!(" Thread {} completed {} iterations", thread_id, thread_times.len());
255 all_iteration_times.extend(thread_times);
256 evaluated_schema = thread_schema; // Use last thread's result
257 }
258
259 let eval_time = eval_start.elapsed();
260
261 // Create a temp eval for metadata export
262 let temp_eval = JSONEval::with_parsed_schema(
263 parsed_schema.clone(),
264 Some("{}"),
265 Some(&data_str)
266 ).unwrap();
267
268 (parse_time, eval_time, evaluated_schema, temp_eval, all_iteration_times)
269 } else {
270 // Sequential iterations with ParsedSchema
271 let eval_start = Instant::now();
272 let mut evaluated_schema = Value::Null;
273 let mut iteration_times = Vec::with_capacity(iterations);
274 let mut eval_instance = JSONEval::with_parsed_schema(
275 parsed_schema.clone(),
276 Some("{}"),
277 Some(&data_str)
278 ).unwrap();
279
280 for iter in 0..iterations {
281 let iter_start = Instant::now();
282
283 if !use_cache && iter > 0 {
284 eval_instance = JSONEval::with_parsed_schema(
285 parsed_schema.clone(),
286 Some("{}"),
287 Some(&data_str)
288 ).unwrap();
289 }
290
291 eval_instance.evaluate(&data_str, Some("{}"), None, None)
292 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
293 evaluated_schema = eval_instance.get_evaluated_schema(false);
294 iteration_times.push(iter_start.elapsed());
295
296 if iterations > 1 && (iter + 1) % 10 == 0 {
297 print!(".");
298 if (iter + 1) % 50 == 0 {
299 println!(" {}/{}", iter + 1, iterations);
300 }
301 }
302 }
303
304 if iterations > 1 && iterations % 50 != 0 {
305 println!(" {}/{}", iterations, iterations);
306 }
307
308 let eval_time = eval_start.elapsed();
309 (parse_time, eval_time, evaluated_schema, eval_instance, iteration_times)
310 }
311 } else {
312 // Traditional mode: parse and create JSONEval each time
313 let schema_msgpack = if scenario.is_msgpack {
314 let bytes = fs::read(&scenario.schema_path)
315 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
316 println!(" 📦 MessagePack schema size: {} bytes", bytes.len());
317 Some(bytes)
318 } else {
319 None
320 };
321
322 let schema_str = if !scenario.is_msgpack {
323 Some(fs::read_to_string(&scenario.schema_path)
324 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e)))
325 } else {
326 None
327 };
328
329 let start_time = Instant::now();
330 let mut eval = if scenario.is_msgpack {
331 JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
332 .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
333 } else {
334 JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
335 .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
336 };
337 let parse_time = start_time.elapsed();
338 println!(" Schema parsing & compilation: {:?}", parse_time);
339
340 let eval_start = Instant::now();
341 let mut evaluated_schema = Value::Null;
342 let mut iteration_times = Vec::with_capacity(iterations);
343
344 for iter in 0..iterations {
345 let iter_start = Instant::now();
346
347 if !use_cache && iter > 0 {
348 eval = if scenario.is_msgpack {
349 JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
350 .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
351 } else {
352 JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
353 .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
354 };
355 }
356
357 eval.evaluate(&data_str, Some("{}"), None, None)
358 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
359 evaluated_schema = eval.get_evaluated_schema(false);
360 iteration_times.push(iter_start.elapsed());
361
362 if iterations > 1 && (iter + 1) % 10 == 0 {
363 print!(".");
364 if (iter + 1) % 50 == 0 {
365 println!(" {}/{}", iter + 1, iterations);
366 }
367 }
368 }
369
370 if iterations > 1 && iterations % 50 != 0 {
371 println!(" {}/{}", iterations, iterations);
372 }
373
374 let eval_time = eval_start.elapsed();
375 (parse_time, eval_time, evaluated_schema, eval, iteration_times)
376 };
377
378 // Calculate statistics
379 let total_iterations = iteration_times.len();
380 if total_iterations == 1 {
381 println!(" Evaluation: {:?}", eval_time);
382 } else {
383 let avg_time = eval_time / total_iterations as u32;
384 let min_time = iteration_times.iter().min().unwrap();
385 let max_time = iteration_times.iter().max().unwrap();
386
387 println!(" Total evaluation time: {:?}", eval_time);
388 println!(" Total iterations: {}", total_iterations);
389 println!(" Average per iteration: {:?}", avg_time);
390 println!(" Min: {:?} | Max: {:?}", min_time, max_time);
391
392 // Show cache statistics
393 let cache_stats = eval.cache_stats();
394 println!(" Cache: {} entries, {} hits, {} misses ({:.1}% hit rate)",
395 cache_stats.entries,
396 cache_stats.hits,
397 cache_stats.misses,
398 cache_stats.hit_rate * 100.0
399 );
400 }
401
402 let total_time = parse_time + eval_time;
403 println!("⏱️ Execution time: {:?}\n", total_time);
404
405 // Print detailed timing breakdown if --timing flag is set
406 if show_timing {
407 json_eval_rs::print_timing_summary();
408 }
409
410 // Track statistics
411 total_parse_time += parse_time;
412 total_eval_time += eval_time;
413 successful_scenarios += 1;
414
415 let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
416 let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
417
418 fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
419 .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
420
421 let mut metadata_obj = Map::new();
422 metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
423 metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
424
425 fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
426 .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
427
428 println!("✅ Results saved:");
429 println!(" - {}", evaluated_path.display());
430 println!(" - {}\n", parsed_path.display());
431
432 // Optional comparison
433 if enable_comparison {
434 if let Some(comp_path) = &scenario.comparison_path {
435 if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
436 comparison_failures += 1;
437 }
438 println!();
439 }
440 }
441 }
442
443 // Print summary statistics
444 if successful_scenarios > 0 {
445 println!("\n{}", "=".repeat(50));
446 println!("📊 Summary Statistics");
447 println!("{}", "=".repeat(50));
448 println!("Total scenarios run: {}", successful_scenarios);
449 println!("Total parsing time: {:?}", total_parse_time);
450 println!("Total evaluation time: {:?}", total_eval_time);
451 println!("Total time: {:?}", total_parse_time + total_eval_time);
452
453 if successful_scenarios > 1 {
454 println!("\nAverage per scenario:");
455 println!(" Parsing: {:?}", total_parse_time / successful_scenarios as u32);
456 println!(" Evaluation: {:?}", total_eval_time / successful_scenarios as u32);
457 }
458
459 if enable_comparison {
460 println!("\nComparison failures: {}", comparison_failures);
461 }
462
463 println!("\n✅ All scenarios completed successfully!\n");
464 }
465}Sourcepub fn clear_cache(&self)
pub fn clear_cache(&self)
Clear the cache manually
Sourcepub fn enable_cache(&mut self)
pub fn enable_cache(&mut self)
Enable caching
Examples found in repository?
4fn main() {
5 let schema = json!({
6 "type": "object",
7 "properties": {
8 "price": {
9 "type": "number"
10 },
11 "tax": {
12 "type": "number",
13 "value": {
14 "$evaluation": {
15 "*": [
16 { "$ref": "#/properties/price" },
17 0.1
18 ]
19 }
20 }
21 },
22 "total": {
23 "type": "number",
24 "value": {
25 "$evaluation": {
26 "+": [
27 { "$ref": "#/properties/price" },
28 { "$ref": "#/properties/tax" }
29 ]
30 }
31 }
32 }
33 }
34 });
35
36 let schema_str = serde_json::to_string(&schema).unwrap();
37
38 println!("=== Example 1: With Caching (Default) ===");
39 {
40 let data = json!({ "price": 100 });
41 let data_str = serde_json::to_string(&data).unwrap();
42
43 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
44
45 println!("Cache enabled: {}", eval.is_cache_enabled());
46 println!("Initial cache size: {}", eval.cache_len());
47
48 eval.evaluate(&data_str, None, None, None).unwrap();
49
50 println!("After evaluation cache size: {}", eval.cache_len());
51 let stats = eval.cache_stats();
52 println!("Cache stats: {}", stats);
53 }
54
55 println!("\n=== Example 2: Without Caching (Web API Mode) ===");
56 {
57 let data = json!({ "price": 200 });
58 let data_str = serde_json::to_string(&data).unwrap();
59
60 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
61
62 // Disable caching for single-use web API scenario
63 eval.disable_cache();
64
65 println!("Cache enabled: {}", eval.is_cache_enabled());
66 println!("Initial cache size: {}", eval.cache_len());
67
68 eval.evaluate(&data_str, None, None, None).unwrap();
69
70 println!("After evaluation cache size: {}", eval.cache_len());
71 let stats = eval.cache_stats();
72 println!("Cache stats: {}", stats);
73
74 println!("\n✅ No cache overhead - perfect for web APIs!");
75 }
76
77 println!("\n=== Example 3: Re-enabling Cache ===");
78 {
79 let data = json!({ "price": 300 });
80 let data_str = serde_json::to_string(&data).unwrap();
81
82 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
83
84 // Disable then re-enable
85 eval.disable_cache();
86 eval.enable_cache();
87
88 println!("Cache enabled: {}", eval.is_cache_enabled());
89 eval.evaluate(&data_str, None, None, None).unwrap();
90
91 println!("Cache size after evaluation: {}", eval.cache_len());
92 println!("\n✅ Cache can be toggled as needed!");
93 }
94}Sourcepub fn disable_cache(&mut self)
pub fn disable_cache(&mut self)
Disable caching
Examples found in repository?
4fn main() {
5 let schema = json!({
6 "type": "object",
7 "properties": {
8 "price": {
9 "type": "number"
10 },
11 "tax": {
12 "type": "number",
13 "value": {
14 "$evaluation": {
15 "*": [
16 { "$ref": "#/properties/price" },
17 0.1
18 ]
19 }
20 }
21 },
22 "total": {
23 "type": "number",
24 "value": {
25 "$evaluation": {
26 "+": [
27 { "$ref": "#/properties/price" },
28 { "$ref": "#/properties/tax" }
29 ]
30 }
31 }
32 }
33 }
34 });
35
36 let schema_str = serde_json::to_string(&schema).unwrap();
37
38 println!("=== Example 1: With Caching (Default) ===");
39 {
40 let data = json!({ "price": 100 });
41 let data_str = serde_json::to_string(&data).unwrap();
42
43 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
44
45 println!("Cache enabled: {}", eval.is_cache_enabled());
46 println!("Initial cache size: {}", eval.cache_len());
47
48 eval.evaluate(&data_str, None, None, None).unwrap();
49
50 println!("After evaluation cache size: {}", eval.cache_len());
51 let stats = eval.cache_stats();
52 println!("Cache stats: {}", stats);
53 }
54
55 println!("\n=== Example 2: Without Caching (Web API Mode) ===");
56 {
57 let data = json!({ "price": 200 });
58 let data_str = serde_json::to_string(&data).unwrap();
59
60 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
61
62 // Disable caching for single-use web API scenario
63 eval.disable_cache();
64
65 println!("Cache enabled: {}", eval.is_cache_enabled());
66 println!("Initial cache size: {}", eval.cache_len());
67
68 eval.evaluate(&data_str, None, None, None).unwrap();
69
70 println!("After evaluation cache size: {}", eval.cache_len());
71 let stats = eval.cache_stats();
72 println!("Cache stats: {}", stats);
73
74 println!("\n✅ No cache overhead - perfect for web APIs!");
75 }
76
77 println!("\n=== Example 3: Re-enabling Cache ===");
78 {
79 let data = json!({ "price": 300 });
80 let data_str = serde_json::to_string(&data).unwrap();
81
82 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
83
84 // Disable then re-enable
85 eval.disable_cache();
86 eval.enable_cache();
87
88 println!("Cache enabled: {}", eval.is_cache_enabled());
89 eval.evaluate(&data_str, None, None, None).unwrap();
90
91 println!("Cache size after evaluation: {}", eval.cache_len());
92 println!("\n✅ Cache can be toggled as needed!");
93 }
94}Sourcepub fn is_cache_enabled(&self) -> bool
pub fn is_cache_enabled(&self) -> bool
Check if cache is enabled
Examples found in repository?
4fn main() {
5 let schema = json!({
6 "type": "object",
7 "properties": {
8 "price": {
9 "type": "number"
10 },
11 "tax": {
12 "type": "number",
13 "value": {
14 "$evaluation": {
15 "*": [
16 { "$ref": "#/properties/price" },
17 0.1
18 ]
19 }
20 }
21 },
22 "total": {
23 "type": "number",
24 "value": {
25 "$evaluation": {
26 "+": [
27 { "$ref": "#/properties/price" },
28 { "$ref": "#/properties/tax" }
29 ]
30 }
31 }
32 }
33 }
34 });
35
36 let schema_str = serde_json::to_string(&schema).unwrap();
37
38 println!("=== Example 1: With Caching (Default) ===");
39 {
40 let data = json!({ "price": 100 });
41 let data_str = serde_json::to_string(&data).unwrap();
42
43 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
44
45 println!("Cache enabled: {}", eval.is_cache_enabled());
46 println!("Initial cache size: {}", eval.cache_len());
47
48 eval.evaluate(&data_str, None, None, None).unwrap();
49
50 println!("After evaluation cache size: {}", eval.cache_len());
51 let stats = eval.cache_stats();
52 println!("Cache stats: {}", stats);
53 }
54
55 println!("\n=== Example 2: Without Caching (Web API Mode) ===");
56 {
57 let data = json!({ "price": 200 });
58 let data_str = serde_json::to_string(&data).unwrap();
59
60 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
61
62 // Disable caching for single-use web API scenario
63 eval.disable_cache();
64
65 println!("Cache enabled: {}", eval.is_cache_enabled());
66 println!("Initial cache size: {}", eval.cache_len());
67
68 eval.evaluate(&data_str, None, None, None).unwrap();
69
70 println!("After evaluation cache size: {}", eval.cache_len());
71 let stats = eval.cache_stats();
72 println!("Cache stats: {}", stats);
73
74 println!("\n✅ No cache overhead - perfect for web APIs!");
75 }
76
77 println!("\n=== Example 3: Re-enabling Cache ===");
78 {
79 let data = json!({ "price": 300 });
80 let data_str = serde_json::to_string(&data).unwrap();
81
82 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
83
84 // Disable then re-enable
85 eval.disable_cache();
86 eval.enable_cache();
87
88 println!("Cache enabled: {}", eval.is_cache_enabled());
89 eval.evaluate(&data_str, None, None, None).unwrap();
90
91 println!("Cache size after evaluation: {}", eval.cache_len());
92 println!("\n✅ Cache can be toggled as needed!");
93 }
94}Sourcepub fn cache_len(&self) -> usize
pub fn cache_len(&self) -> usize
Get cache size
Examples found in repository?
4fn main() {
5 let schema = json!({
6 "type": "object",
7 "properties": {
8 "price": {
9 "type": "number"
10 },
11 "tax": {
12 "type": "number",
13 "value": {
14 "$evaluation": {
15 "*": [
16 { "$ref": "#/properties/price" },
17 0.1
18 ]
19 }
20 }
21 },
22 "total": {
23 "type": "number",
24 "value": {
25 "$evaluation": {
26 "+": [
27 { "$ref": "#/properties/price" },
28 { "$ref": "#/properties/tax" }
29 ]
30 }
31 }
32 }
33 }
34 });
35
36 let schema_str = serde_json::to_string(&schema).unwrap();
37
38 println!("=== Example 1: With Caching (Default) ===");
39 {
40 let data = json!({ "price": 100 });
41 let data_str = serde_json::to_string(&data).unwrap();
42
43 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
44
45 println!("Cache enabled: {}", eval.is_cache_enabled());
46 println!("Initial cache size: {}", eval.cache_len());
47
48 eval.evaluate(&data_str, None, None, None).unwrap();
49
50 println!("After evaluation cache size: {}", eval.cache_len());
51 let stats = eval.cache_stats();
52 println!("Cache stats: {}", stats);
53 }
54
55 println!("\n=== Example 2: Without Caching (Web API Mode) ===");
56 {
57 let data = json!({ "price": 200 });
58 let data_str = serde_json::to_string(&data).unwrap();
59
60 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
61
62 // Disable caching for single-use web API scenario
63 eval.disable_cache();
64
65 println!("Cache enabled: {}", eval.is_cache_enabled());
66 println!("Initial cache size: {}", eval.cache_len());
67
68 eval.evaluate(&data_str, None, None, None).unwrap();
69
70 println!("After evaluation cache size: {}", eval.cache_len());
71 let stats = eval.cache_stats();
72 println!("Cache stats: {}", stats);
73
74 println!("\n✅ No cache overhead - perfect for web APIs!");
75 }
76
77 println!("\n=== Example 3: Re-enabling Cache ===");
78 {
79 let data = json!({ "price": 300 });
80 let data_str = serde_json::to_string(&data).unwrap();
81
82 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
83
84 // Disable then re-enable
85 eval.disable_cache();
86 eval.enable_cache();
87
88 println!("Cache enabled: {}", eval.is_cache_enabled());
89 eval.evaluate(&data_str, None, None, None).unwrap();
90
91 println!("Cache size after evaluation: {}", eval.cache_len());
92 println!("\n✅ Cache can be toggled as needed!");
93 }
94}Source§impl JSONEval
impl JSONEval
Sourcepub fn evaluate_dependents(
&mut self,
changed_paths: &[String],
data: Option<&str>,
context: Option<&str>,
re_evaluate: bool,
token: Option<&CancellationToken>,
canceled_paths: Option<&mut Vec<String>>,
) -> Result<Value, String>
pub fn evaluate_dependents( &mut self, changed_paths: &[String], data: Option<&str>, context: Option<&str>, re_evaluate: bool, token: Option<&CancellationToken>, canceled_paths: Option<&mut Vec<String>>, ) -> Result<Value, String>
Evaluate fields that depend on a changed path This processes all dependent fields transitively when a source field changes
Source§impl JSONEval
impl JSONEval
Sourcepub fn evaluate(
&mut self,
data: &str,
context: Option<&str>,
paths: Option<&[String]>,
token: Option<&CancellationToken>,
) -> Result<(), String>
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?
38fn demo_local_cache() -> Result<(), Box<dyn std::error::Error>> {
39 println!("📦 Example 1: Local Cache Instance");
40 println!("Creating a dedicated cache for this application...\n");
41
42 let cache = ParsedSchemaCache::new();
43
44 // Simple schema
45 let schema_json = r#"{
46 "$params": {
47 "rate": { "type": "number" }
48 },
49 "result": {
50 "type": "number",
51 "title": "Calculated Result",
52 "$evaluation": {
53 "logic": { "*": [{"var": "$rate"}, 100] }
54 }
55 }
56 }"#;
57
58 // Parse and cache with a custom key
59 println!("📝 Parsing schema and caching with key 'calculation-v1'...");
60 let parsed = ParsedSchema::parse(schema_json)?;
61 cache.insert("calculation-v1".to_string(), Arc::new(parsed));
62
63 println!("✅ Schema cached successfully");
64 println!(" Cache size: {} entries", cache.len());
65 println!(" Keys: {:?}\n", cache.keys());
66
67 // Retrieve and use cached schema
68 println!("🔍 Retrieving cached schema...");
69 if let Some(cached_schema) = cache.get("calculation-v1") {
70 println!("✅ Retrieved from cache");
71
72 // Create JSONEval from cached ParsedSchema
73 let mut eval = JSONEval::with_parsed_schema(cached_schema, Some(r#"{"rate": 1.5}"#), None)?;
74 eval.evaluate("{}", None, None, None)?;
75
76 let evaluated = eval.get_evaluated_schema(false);
77 let result = evaluated.pointer("/result")
78 .and_then(|v| v.as_f64())
79 .unwrap_or(0.0);
80 println!(" Evaluation result: {}\n", result);
81 }
82
83 // Check cache stats
84 let stats = cache.stats();
85 println!("📊 Cache Statistics: {}", stats);
86
87 // Remove entry
88 println!("\n🗑️ Removing 'calculation-v1' from cache...");
89 cache.remove("calculation-v1");
90 println!(" Cache size after removal: {}", cache.len());
91
92 Ok(())
93}
94
95fn demo_global_cache() -> Result<(), Box<dyn std::error::Error>> {
96 println!("🌍 Example 2: Global Cache Instance");
97 println!("Using the built-in PARSED_SCHEMA_CACHE...\n");
98
99 let schema_json = r#"{
100 "$params": {
101 "x": { "type": "number" },
102 "y": { "type": "number" }
103 },
104 "sum": {
105 "type": "number",
106 "$evaluation": { "+": [{"var": "$x"}, {"var": "$y"}] }
107 }
108 }"#;
109
110 // Use global cache
111 println!("📝 Caching schema globally with key 'math-operations'...");
112 let parsed = ParsedSchema::parse(schema_json)?;
113 PARSED_SCHEMA_CACHE.insert("math-operations".to_string(), Arc::new(parsed));
114
115 println!("✅ Schema cached globally");
116 println!(" Global cache size: {}\n", PARSED_SCHEMA_CACHE.len());
117
118 // Access from anywhere in the application
119 simulate_another_function()?;
120
121 // Clean up
122 println!("\n🧹 Clearing global cache...");
123 PARSED_SCHEMA_CACHE.clear();
124 println!(" Global cache size: {}", PARSED_SCHEMA_CACHE.len());
125
126 Ok(())
127}
128
129fn simulate_another_function() -> Result<(), Box<dyn std::error::Error>> {
130 println!("🔄 In another function, accessing global cache...");
131
132 if let Some(cached) = PARSED_SCHEMA_CACHE.get("math-operations") {
133 println!("✅ Retrieved schema from global cache");
134
135 let mut eval = JSONEval::with_parsed_schema(cached, Some(r#"{"x": 10, "y": 20}"#), None)?;
136 eval.evaluate("{}", None, None, None)?;
137
138 let evaluated = eval.get_evaluated_schema(false);
139 let sum = evaluated.pointer("/sum")
140 .and_then(|v| v.as_f64())
141 .unwrap_or(0.0);
142 println!(" Result: {}", sum);
143 }
144
145 Ok(())
146}
147
148fn demo_performance_comparison() -> Result<(), Box<dyn std::error::Error>> {
149 println!("⚡ Example 3: Performance Comparison");
150 println!("Comparing cached vs non-cached schema usage...\n");
151
152 let schema_json = r#"{
153 "$params": {
154 "value": { "type": "number" }
155 },
156 "doubled": {
157 "type": "number",
158 "$evaluation": { "*": [{"var": "$value"}, 2] }
159 },
160 "tripled": {
161 "type": "number",
162 "$evaluation": { "*": [{"var": "$value"}, 3] }
163 }
164 }"#;
165
166 let iterations = 100;
167
168 // WITHOUT CACHE: Parse schema every time
169 println!("🐌 Without cache (parse + evaluate each time):");
170 let start = Instant::now();
171 for i in 0..iterations {
172 let context = format!(r#"{{"value": {}}}"#, i);
173 let mut eval = JSONEval::new(schema_json, Some(&context), None)?;
174 eval.evaluate("{}", None, None, None)?;
175 }
176 let without_cache = start.elapsed();
177 println!(" Time: {:?}", without_cache);
178 println!(" Avg per iteration: {:?}\n", without_cache / iterations);
179
180 // WITH CACHE: Parse once, evaluate many times
181 println!("🚀 With cache (parse once, reuse for all evaluations):");
182 let cache = ParsedSchemaCache::new();
183
184 // Parse once
185 let parse_start = Instant::now();
186 let parsed = ParsedSchema::parse(schema_json)?;
187 cache.insert("perf-test".to_string(), Arc::new(parsed));
188 let parse_time = parse_start.elapsed();
189
190 // Evaluate many times
191 let eval_start = Instant::now();
192 for i in 0..iterations {
193 if let Some(cached) = cache.get("perf-test") {
194 let context = format!(r#"{{"value": {}}}"#, i);
195 let mut eval = JSONEval::with_parsed_schema(cached.clone(), Some(&context), None)?;
196 eval.evaluate("{}", None, None, None)?;
197 }
198 }
199 let eval_time = eval_start.elapsed();
200 let with_cache = parse_time + eval_time;
201
202 println!(" Parse time: {:?}", parse_time);
203 println!(" Eval time: {:?}", eval_time);
204 println!(" Total time: {:?}", with_cache);
205 println!(" Avg per iteration: {:?}\n", eval_time / iterations);
206
207 let speedup = without_cache.as_secs_f64() / with_cache.as_secs_f64();
208 println!("📈 Speedup: {:.2}x faster", speedup);
209
210 Ok(())
211}More examples
4fn main() {
5 let schema = json!({
6 "type": "object",
7 "properties": {
8 "price": {
9 "type": "number"
10 },
11 "tax": {
12 "type": "number",
13 "value": {
14 "$evaluation": {
15 "*": [
16 { "$ref": "#/properties/price" },
17 0.1
18 ]
19 }
20 }
21 },
22 "total": {
23 "type": "number",
24 "value": {
25 "$evaluation": {
26 "+": [
27 { "$ref": "#/properties/price" },
28 { "$ref": "#/properties/tax" }
29 ]
30 }
31 }
32 }
33 }
34 });
35
36 let schema_str = serde_json::to_string(&schema).unwrap();
37
38 println!("=== Example 1: With Caching (Default) ===");
39 {
40 let data = json!({ "price": 100 });
41 let data_str = serde_json::to_string(&data).unwrap();
42
43 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
44
45 println!("Cache enabled: {}", eval.is_cache_enabled());
46 println!("Initial cache size: {}", eval.cache_len());
47
48 eval.evaluate(&data_str, None, None, None).unwrap();
49
50 println!("After evaluation cache size: {}", eval.cache_len());
51 let stats = eval.cache_stats();
52 println!("Cache stats: {}", stats);
53 }
54
55 println!("\n=== Example 2: Without Caching (Web API Mode) ===");
56 {
57 let data = json!({ "price": 200 });
58 let data_str = serde_json::to_string(&data).unwrap();
59
60 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
61
62 // Disable caching for single-use web API scenario
63 eval.disable_cache();
64
65 println!("Cache enabled: {}", eval.is_cache_enabled());
66 println!("Initial cache size: {}", eval.cache_len());
67
68 eval.evaluate(&data_str, None, None, None).unwrap();
69
70 println!("After evaluation cache size: {}", eval.cache_len());
71 let stats = eval.cache_stats();
72 println!("Cache stats: {}", stats);
73
74 println!("\n✅ No cache overhead - perfect for web APIs!");
75 }
76
77 println!("\n=== Example 3: Re-enabling Cache ===");
78 {
79 let data = json!({ "price": 300 });
80 let data_str = serde_json::to_string(&data).unwrap();
81
82 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
83
84 // Disable then re-enable
85 eval.disable_cache();
86 eval.enable_cache();
87
88 println!("Cache enabled: {}", eval.is_cache_enabled());
89 eval.evaluate(&data_str, None, None, None).unwrap();
90
91 println!("Cache size after evaluation: {}", eval.cache_len());
92 println!("\n✅ Cache can be toggled as needed!");
93 }
94}6fn main() {
7 println!("\n🚀 JSON Evaluation - SPAJ Toggle Example\n");
8
9 let schema_path = Path::new("samples/spaj.json");
10 let schema_str = fs::read_to_string(schema_path).expect("Failed to read schema");
11
12 // Initial data with minimal context required
13 let context_str = json!({
14 "agentProfile": { "sob": "AG" }
15 }).to_string();
16
17 let initial_data = json!({
18 "illustration": {
19 "basicinformation": {
20 "print_polflag": false
21 }
22 }
23 }).to_string();
24
25 // Initialize logic
26 let mut eval = JSONEval::new(&schema_str, Some(&context_str), Some(&initial_data))
27 .expect("Failed to create JSONEval");
28
29 // Helper to check visibility
30 let check_visibility = |eval: &mut JSONEval, expected_hidden: bool, step: &str| {
31 let result = eval.get_evaluated_schema(false);
32 let hidden = result.pointer("/illustration/properties/basicinformation/properties/print_poladdress/condition/hidden")
33 .and_then(|v| v.as_bool());
34
35 match hidden {
36 Some(val) => {
37 if val == expected_hidden {
38 println!("✅ {}: Hidden = {} (Expected: {})", step, val, expected_hidden);
39 } else {
40 println!("❌ {}: Hidden = {} (Expected: {})", step, val, expected_hidden);
41 }
42 },
43 None => println!("❌ {}: 'hidden' property not found", step),
44 }
45 };
46
47 // Step 1: Initial state (false)
48 println!("Step 1: Initial State (print_polflag: false)");
49 eval.evaluate(&initial_data, Some(&context_str), None, None).expect("Evaluation failed");
50 check_visibility(&mut eval, true, "Initial check");
51
52 // Step 2: Toggle to true
53 println!("\nStep 2: Toggle True (print_polflag: true)");
54 let data_true = json!({
55 "illustration": {
56 "basicinformation": {
57 "print_polflag": true
58 }
59 }
60 }).to_string();
61 eval.evaluate(&data_true, Some(&context_str), None, None).expect("Evaluation failed");
62 check_visibility(&mut eval, false, "Toggle ON check");
63
64 // Step 3: Toggle back to false
65 println!("\nStep 3: Toggle False (print_polflag: false)");
66 let data_false = json!({
67 "illustration": {
68 "basicinformation": {
69 "print_polflag": false
70 }
71 }
72 }).to_string();
73 eval.evaluate(&data_false, Some(&context_str), None, None).expect("Evaluation failed");
74
75 let hidden_path = "#/illustration/properties/basicinformation/properties/print_poladdress/condition/hidden";
76 if let Some(deps) = eval.dependencies.get(hidden_path) {
77 println!("Debug: Dependencies for hidden: {:?}", deps);
78 } else {
79 println!("Debug: No dependencies found for hidden path");
80 }
81
82 // Debug: Print current flag value
83 if let Some(val) = eval.get_evaluated_schema(false).pointer("/illustration/properties/basicinformation/properties/print_polflag/value") {
84 println!("Debug: print_polflag value is: {}", val);
85 }
86
87 check_visibility(&mut eval, true, "Toggle OFF check");
88}28fn main() {
29 let args: Vec<String> = std::env::args().collect();
30 let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic_msgpack");
31
32 let mut scenario_filter: Option<String> = None;
33 let mut enable_comparison = false;
34 let mut show_timing = false;
35 let mut i = 1;
36
37 // Parse arguments
38 while i < args.len() {
39 let arg = &args[i];
40
41 if arg == "-h" || arg == "--help" {
42 print_help(program_name);
43 return;
44 } else if arg == "--compare" {
45 enable_comparison = true;
46 } else if arg == "--timing" {
47 show_timing = true;
48 } else if !arg.starts_with('-') {
49 scenario_filter = Some(arg.clone());
50 } else {
51 eprintln!("Error: unknown option '{}'", arg);
52 print_help(program_name);
53 return;
54 }
55
56 i += 1;
57 }
58
59 println!("\n🚀 JSON Evaluation - Basic Example (MessagePack Schema)\n");
60
61 if enable_comparison {
62 println!("🔍 Comparison: enabled");
63 }
64 if show_timing {
65 println!("⏱️ Internal timing: enabled");
66 }
67 if enable_comparison || show_timing {
68 println!();
69 }
70
71 let samples_dir = Path::new("samples");
72 let mut scenarios = common::discover_scenarios(samples_dir);
73
74 // Filter to only MessagePack scenarios
75 scenarios.retain(|s| s.is_msgpack);
76
77 // Filter scenarios if a filter is provided
78 if let Some(ref filter) = scenario_filter {
79 scenarios.retain(|s| s.name.contains(filter));
80 println!("📋 Filtering scenarios matching: '{}'\n", filter);
81 }
82
83 if scenarios.is_empty() {
84 if let Some(filter) = scenario_filter {
85 println!(
86 "ℹ️ No MessagePack scenarios found matching '{}' in `{}`.",
87 filter,
88 samples_dir.display()
89 );
90 } else {
91 println!(
92 "ℹ️ No MessagePack scenarios discovered in `{}`. Add files like `name.bform` and `name-data.json`.",
93 samples_dir.display()
94 );
95 }
96 return;
97 }
98
99 println!("📊 Found {} MessagePack scenario(s)\n", scenarios.len());
100
101 let mut total_parse_time = std::time::Duration::ZERO;
102 let mut total_eval_time = std::time::Duration::ZERO;
103 let mut successful_scenarios = 0;
104 let mut comparison_failures = 0;
105
106 for scenario in &scenarios {
107 println!("==============================");
108 println!("Scenario: {}", scenario.name);
109 println!("Schema: {} (MessagePack)", scenario.schema_path.display());
110 println!("Data: {}\n", scenario.data_path.display());
111
112 // Clear timing data from previous scenarios
113 if show_timing {
114 json_eval_rs::enable_timing();
115 json_eval_rs::clear_timing_data();
116 }
117
118 let data_str = fs::read_to_string(&scenario.data_path)
119 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
120
121 // Step 1: Parse schema (new_from_msgpack)
122 let parse_start = Instant::now();
123
124 let schema_msgpack = fs::read(&scenario.schema_path)
125 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
126
127 println!(" 📦 MessagePack schema size: {} bytes", schema_msgpack.len());
128
129 let mut eval = JSONEval::new_from_msgpack(&schema_msgpack, None, Some(&data_str))
130 .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e));
131
132 let parse_time = parse_start.elapsed();
133 println!(" 📝 Parse (msgpack): {:?}", parse_time);
134
135 // Step 2: Evaluate
136 let eval_start = Instant::now();
137
138 eval.evaluate(&data_str, Some("{}"), None, None)
139 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
140
141 let evaluated_schema = eval.get_evaluated_schema(false);
142 let eval_time = eval_start.elapsed();
143
144 println!(" ⚡ Eval: {:?}", eval_time);
145 println!(" ⏱️ Total: {:?}\n", parse_time + eval_time);
146
147 // Print detailed timing breakdown if --timing flag is set
148 if show_timing {
149 json_eval_rs::print_timing_summary();
150 }
151
152 total_parse_time += parse_time;
153 total_eval_time += eval_time;
154 successful_scenarios += 1;
155
156 // Save results
157 let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
158 let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
159
160 fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
161 .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
162
163 let mut metadata_obj = Map::new();
164 metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
165 metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
166 metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
167
168 fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
169 .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
170
171 println!("✅ Results saved:");
172 println!(" - {}", evaluated_path.display());
173 println!(" - {}\n", parsed_path.display());
174
175 // Optional comparison
176 if enable_comparison {
177 if let Some(comp_path) = &scenario.comparison_path {
178 if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
179 comparison_failures += 1;
180 }
181 println!();
182 }
183 }
184 }
185
186 // Print summary
187 println!("{}", "=".repeat(50));
188 println!("📊 Summary");
189 println!("{}", "=".repeat(50));
190 println!("Total scenarios run: {}", successful_scenarios);
191 println!("Total parse time: {:?}", total_parse_time);
192 println!("Total eval time: {:?}", total_eval_time);
193 println!("Total time: {:?}", total_parse_time + total_eval_time);
194
195 if successful_scenarios > 1 {
196 println!("\nAverage per scenario:");
197 println!(" Parse: {:?}", total_parse_time / successful_scenarios as u32);
198 println!(" Eval: {:?}", total_eval_time / successful_scenarios as u32);
199 }
200
201 if enable_comparison {
202 println!("Comparison failures: {}", comparison_failures);
203 }
204
205 println!("\n✅ All scenarios completed!\n");
206}30fn main() {
31 let args: Vec<String> = std::env::args().collect();
32 let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic_parsed");
33
34 let mut scenario_filter: Option<String> = None;
35 let mut enable_comparison = false;
36 let mut show_timing = false;
37 let mut i = 1;
38
39 // Parse arguments
40 while i < args.len() {
41 let arg = &args[i];
42
43 if arg == "-h" || arg == "--help" {
44 print_help(program_name);
45 return;
46 } else if arg == "--compare" {
47 enable_comparison = true;
48 } else if arg == "--timing" {
49 show_timing = true;
50 } else if !arg.starts_with('-') {
51 scenario_filter = Some(arg.clone());
52 } else {
53 eprintln!("Error: unknown option '{}'", arg);
54 print_help(program_name);
55 return;
56 }
57
58 i += 1;
59 }
60
61 println!("\n🚀 JSON Evaluation - Basic Example (ParsedSchema)\n");
62 println!("📦 Using Arc<ParsedSchema> for efficient caching\n");
63
64 if enable_comparison {
65 println!("🔍 Comparison: enabled");
66 }
67 if show_timing {
68 println!("⏱️ Internal timing: enabled");
69 }
70 if enable_comparison || show_timing {
71 println!();
72 }
73
74 let samples_dir = Path::new("samples");
75 let mut scenarios = common::discover_scenarios(samples_dir);
76
77 // Filter scenarios if a filter is provided
78 if let Some(ref filter) = scenario_filter {
79 scenarios.retain(|s| s.name.contains(filter));
80 println!("📋 Filtering scenarios matching: '{}'\n", filter);
81 }
82
83 if scenarios.is_empty() {
84 if let Some(filter) = scenario_filter {
85 println!(
86 "ℹ️ No scenarios found matching '{}' in `{}`.",
87 filter,
88 samples_dir.display()
89 );
90 } else {
91 println!(
92 "ℹ️ No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
93 samples_dir.display()
94 );
95 }
96 return;
97 }
98
99 println!("📊 Found {} scenario(s)\n", scenarios.len());
100
101 let mut total_parse_time = std::time::Duration::ZERO;
102 let mut total_eval_time = std::time::Duration::ZERO;
103 let mut successful_scenarios = 0;
104 let mut comparison_failures = 0;
105
106 for scenario in &scenarios {
107 println!("==============================");
108 println!("Scenario: {}", scenario.name);
109 println!("Schema: {} ({})",
110 scenario.schema_path.display(),
111 if scenario.is_msgpack { "MessagePack" } else { "JSON" }
112 );
113 println!("Data: {}\n", scenario.data_path.display());
114
115 // Clear timing data from previous scenarios
116 if show_timing {
117 json_eval_rs::enable_timing();
118 json_eval_rs::clear_timing_data();
119 }
120
121 let data_str = fs::read_to_string(&scenario.data_path)
122 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
123
124 // Step 1: Parse schema once
125 let parse_start = Instant::now();
126 let parsed_schema = if scenario.is_msgpack {
127 let schema_msgpack = fs::read(&scenario.schema_path)
128 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
129 println!(" 📦 MessagePack schema size: {} bytes", schema_msgpack.len());
130 Arc::new(ParsedSchema::parse_msgpack(&schema_msgpack)
131 .unwrap_or_else(|e| panic!("failed to parse MessagePack schema: {}", e)))
132 } else {
133 let schema_str = fs::read_to_string(&scenario.schema_path)
134 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
135 Arc::new(ParsedSchema::parse(&schema_str)
136 .unwrap_or_else(|e| panic!("failed to parse schema: {}", e)))
137 };
138 let parse_time = parse_start.elapsed();
139 println!(" 📝 Schema parsing: {:?}", parse_time);
140
141 // Step 2: Create JSONEval from ParsedSchema (reuses compiled logic)
142 let eval_start = Instant::now();
143 let mut eval = JSONEval::with_parsed_schema(
144 parsed_schema.clone(), // Arc::clone is cheap!
145 Some("{}"),
146 Some(&data_str)
147 ).unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e));
148
149 eval.evaluate(&data_str, Some("{}"), None, None)
150 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
151
152 let evaluated_schema = eval.get_evaluated_schema(false);
153 let eval_time = eval_start.elapsed();
154
155 println!(" ⚡ Eval: {:?}", eval_time);
156 println!(" ⏱️ Total: {:?}\n", parse_time + eval_time);
157
158 // Print detailed timing breakdown if --timing flag is set
159 if show_timing {
160 json_eval_rs::print_timing_summary();
161 }
162
163 total_parse_time += parse_time;
164 total_eval_time += eval_time;
165 successful_scenarios += 1;
166
167 // Save results
168 let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
169 let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
170
171 fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
172 .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
173
174 let mut metadata_obj = Map::new();
175 metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
176 metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
177 metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
178
179 fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
180 .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
181
182 println!("✅ Results saved:");
183 println!(" - {}", evaluated_path.display());
184 println!(" - {}\n", parsed_path.display());
185
186 // Optional comparison
187 if enable_comparison {
188 if let Some(comp_path) = &scenario.comparison_path {
189 if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
190 comparison_failures += 1;
191 }
192 println!();
193 }
194 }
195 }
196
197 // Print summary
198 println!("{}", "=".repeat(50));
199 println!("📊 Summary");
200 println!("{}", "=".repeat(50));
201 println!("Total scenarios run: {}", successful_scenarios);
202 println!("Total parsing time: {:?}", total_parse_time);
203 println!("Total evaluation time: {:?}", total_eval_time);
204 println!("Total time: {:?}", total_parse_time + total_eval_time);
205
206 if successful_scenarios > 1 {
207 println!("\nAverage per scenario:");
208 println!(" Parsing: {:?}", total_parse_time / successful_scenarios as u32);
209 println!(" Evaluation: {:?}", total_eval_time / successful_scenarios as u32);
210 }
211
212 if enable_comparison {
213 println!("\nComparison failures: {}", comparison_failures);
214 }
215
216 println!("\n✅ All scenarios completed!\n");
217}28fn main() {
29 let args: Vec<String> = std::env::args().collect();
30 let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic");
31
32 let mut scenario_filter: Option<String> = None;
33 let mut enable_comparison = false;
34 let mut show_timing = false;
35 let mut i = 1;
36
37 // Parse arguments
38 while i < args.len() {
39 let arg = &args[i];
40
41 if arg == "-h" || arg == "--help" {
42 print_help(program_name);
43 return;
44 } else if arg == "--compare" {
45 enable_comparison = true;
46 } else if arg == "--timing" {
47 show_timing = true;
48 } else if !arg.starts_with('-') {
49 scenario_filter = Some(arg.clone());
50 } else {
51 eprintln!("Error: unknown option '{}'", arg);
52 print_help(program_name);
53 return;
54 }
55
56 i += 1;
57 }
58
59 println!("\n🚀 JSON Evaluation - Basic Example (JSON Schema)\n");
60
61 if enable_comparison {
62 println!("🔍 Comparison: enabled");
63 }
64 if show_timing {
65 println!("⏱️ Internal timing: enabled");
66 }
67 if enable_comparison || show_timing {
68 println!();
69 }
70
71 let samples_dir = Path::new("samples");
72 let mut scenarios = common::discover_scenarios(samples_dir);
73
74 // Filter out MessagePack scenarios - only use JSON
75 scenarios.retain(|s| !s.is_msgpack);
76
77 // Filter scenarios if a filter is provided
78 if let Some(ref filter) = scenario_filter {
79 scenarios.retain(|s| s.name.contains(filter));
80 println!("📋 Filtering scenarios matching: '{}'\n", filter);
81 }
82
83 if scenarios.is_empty() {
84 if let Some(filter) = scenario_filter {
85 println!(
86 "ℹ️ No scenarios found matching '{}' in `{}`.",
87 filter,
88 samples_dir.display()
89 );
90 } else {
91 println!(
92 "ℹ️ No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
93 samples_dir.display()
94 );
95 }
96 return;
97 }
98
99 println!("📊 Found {} scenario(s)\n", scenarios.len());
100
101 let mut total_parse_time = std::time::Duration::ZERO;
102 let mut total_eval_time = std::time::Duration::ZERO;
103 let mut successful_scenarios = 0;
104 let mut comparison_failures = 0;
105
106 for scenario in &scenarios {
107 println!("==============================");
108 println!("Scenario: {}", scenario.name);
109 println!("Schema: {} ({})",
110 scenario.schema_path.display(),
111 if scenario.is_msgpack { "MessagePack" } else { "JSON" }
112 );
113 println!("Data: {}\n", scenario.data_path.display());
114
115 // Clear timing data from previous scenarios
116 if show_timing {
117 json_eval_rs::enable_timing();
118 json_eval_rs::clear_timing_data();
119 }
120
121 let data_str = fs::read_to_string(&scenario.data_path)
122 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
123
124 // Step 1: Parse schema (JSONEval::new)
125 let parse_start = Instant::now();
126
127 let schema_str = fs::read_to_string(&scenario.schema_path)
128 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
129
130 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str))
131 .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e));
132
133 let parse_time = parse_start.elapsed();
134 println!(" 📝 Parse (new): {:?}", parse_time);
135
136 // Step 2: Evaluate
137 let eval_start = Instant::now();
138
139 eval.evaluate(&data_str, Some("{}"), None, None)
140 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
141
142 // Step 3: Validate
143 let validation_start = Instant::now();
144 let validation_result = eval.validate(&data_str, None, None, None)
145 .unwrap_or_else(|e| panic!("validation failed: {}", e));
146 let validation_time = validation_start.elapsed();
147 println!(" 🛡️ Validate: {:?}", validation_time);
148
149 // Legacy behavior: get_evaluated_schema takes skip_layout: bool
150 // We pass false to ensure layout IS resolved
151 let evaluated_schema = eval.get_evaluated_schema(false);
152 let schema_value = eval.get_schema_value();
153 let eval_time = eval_start.elapsed();
154
155 println!(" ⚡ Eval: {:?}", eval_time);
156 println!(" ⏱️ Total: {:?}\n", parse_time + eval_time);
157
158 // Print detailed timing breakdown if --timing flag is set
159 if show_timing {
160 json_eval_rs::print_timing_summary();
161 }
162
163 total_parse_time += parse_time;
164 total_eval_time += eval_time;
165 successful_scenarios += 1;
166
167 // Save results
168 let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
169 let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
170 let value_path = samples_dir.join(format!("{}-schema-value.json", scenario.name));
171 let validation_path = samples_dir.join(format!("{}-validation.json", scenario.name));
172
173 fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
174 .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
175
176 let mut metadata_obj = Map::new();
177 metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
178 metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
179 metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
180
181 fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
182 .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
183
184 fs::write(&value_path, common::pretty_json(&schema_value))
185 .unwrap_or_else(|e| panic!("failed to write {}: {}", value_path.display(), e));
186
187 let validation_value = serde_json::to_value(&validation_result)
188 .unwrap_or_else(|e| panic!("failed to serialize validation result: {}", e));
189 fs::write(&validation_path, common::pretty_json(&validation_value))
190 .unwrap_or_else(|e| panic!("failed to write {}: {}", validation_path.display(), e));
191
192 println!("✅ Results saved:");
193 println!(" - {}", evaluated_path.display());
194 println!(" - {}", parsed_path.display());
195 println!(" - {}", value_path.display());
196 println!(" - {}\n", validation_path.display());
197
198 // Optional comparison
199 if enable_comparison {
200 if let Some(comp_path) = &scenario.comparison_path {
201 if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
202 comparison_failures += 1;
203 }
204 println!();
205 }
206 }
207 }
208
209 // Print summary
210 println!("{}", "=".repeat(50));
211 println!("📊 Summary");
212 println!("{}", "=".repeat(50));
213 println!("Total scenarios run: {}", successful_scenarios);
214 println!("Total parse time: {:?}", total_parse_time);
215 println!("Total eval time: {:?}", total_eval_time);
216 println!("Total time: {:?}", total_parse_time + total_eval_time);
217
218 if successful_scenarios > 1 {
219 println!("\nAverage per scenario:");
220 println!(" Parse: {:?}", total_parse_time / successful_scenarios as u32);
221 println!(" Eval: {:?}", total_eval_time / successful_scenarios as u32);
222 }
223
224 if enable_comparison {
225 println!("Comparison failures: {}", comparison_failures);
226 }
227
228 println!("\n✅ All scenarios completed!\n");
229}Source§impl JSONEval
impl JSONEval
Sourcepub fn get_evaluated_schema(&mut self, skip_layout: bool) -> Value
pub fn get_evaluated_schema(&mut self, skip_layout: bool) -> Value
Get the evaluated schema with optional layout resolution.
§Arguments
skip_layout- Whether to skip layout resolution.
§Returns
The evaluated schema as a JSON value.
Examples found in repository?
38fn demo_local_cache() -> Result<(), Box<dyn std::error::Error>> {
39 println!("📦 Example 1: Local Cache Instance");
40 println!("Creating a dedicated cache for this application...\n");
41
42 let cache = ParsedSchemaCache::new();
43
44 // Simple schema
45 let schema_json = r#"{
46 "$params": {
47 "rate": { "type": "number" }
48 },
49 "result": {
50 "type": "number",
51 "title": "Calculated Result",
52 "$evaluation": {
53 "logic": { "*": [{"var": "$rate"}, 100] }
54 }
55 }
56 }"#;
57
58 // Parse and cache with a custom key
59 println!("📝 Parsing schema and caching with key 'calculation-v1'...");
60 let parsed = ParsedSchema::parse(schema_json)?;
61 cache.insert("calculation-v1".to_string(), Arc::new(parsed));
62
63 println!("✅ Schema cached successfully");
64 println!(" Cache size: {} entries", cache.len());
65 println!(" Keys: {:?}\n", cache.keys());
66
67 // Retrieve and use cached schema
68 println!("🔍 Retrieving cached schema...");
69 if let Some(cached_schema) = cache.get("calculation-v1") {
70 println!("✅ Retrieved from cache");
71
72 // Create JSONEval from cached ParsedSchema
73 let mut eval = JSONEval::with_parsed_schema(cached_schema, Some(r#"{"rate": 1.5}"#), None)?;
74 eval.evaluate("{}", None, None, None)?;
75
76 let evaluated = eval.get_evaluated_schema(false);
77 let result = evaluated.pointer("/result")
78 .and_then(|v| v.as_f64())
79 .unwrap_or(0.0);
80 println!(" Evaluation result: {}\n", result);
81 }
82
83 // Check cache stats
84 let stats = cache.stats();
85 println!("📊 Cache Statistics: {}", stats);
86
87 // Remove entry
88 println!("\n🗑️ Removing 'calculation-v1' from cache...");
89 cache.remove("calculation-v1");
90 println!(" Cache size after removal: {}", cache.len());
91
92 Ok(())
93}
94
95fn demo_global_cache() -> Result<(), Box<dyn std::error::Error>> {
96 println!("🌍 Example 2: Global Cache Instance");
97 println!("Using the built-in PARSED_SCHEMA_CACHE...\n");
98
99 let schema_json = r#"{
100 "$params": {
101 "x": { "type": "number" },
102 "y": { "type": "number" }
103 },
104 "sum": {
105 "type": "number",
106 "$evaluation": { "+": [{"var": "$x"}, {"var": "$y"}] }
107 }
108 }"#;
109
110 // Use global cache
111 println!("📝 Caching schema globally with key 'math-operations'...");
112 let parsed = ParsedSchema::parse(schema_json)?;
113 PARSED_SCHEMA_CACHE.insert("math-operations".to_string(), Arc::new(parsed));
114
115 println!("✅ Schema cached globally");
116 println!(" Global cache size: {}\n", PARSED_SCHEMA_CACHE.len());
117
118 // Access from anywhere in the application
119 simulate_another_function()?;
120
121 // Clean up
122 println!("\n🧹 Clearing global cache...");
123 PARSED_SCHEMA_CACHE.clear();
124 println!(" Global cache size: {}", PARSED_SCHEMA_CACHE.len());
125
126 Ok(())
127}
128
129fn simulate_another_function() -> Result<(), Box<dyn std::error::Error>> {
130 println!("🔄 In another function, accessing global cache...");
131
132 if let Some(cached) = PARSED_SCHEMA_CACHE.get("math-operations") {
133 println!("✅ Retrieved schema from global cache");
134
135 let mut eval = JSONEval::with_parsed_schema(cached, Some(r#"{"x": 10, "y": 20}"#), None)?;
136 eval.evaluate("{}", None, None, None)?;
137
138 let evaluated = eval.get_evaluated_schema(false);
139 let sum = evaluated.pointer("/sum")
140 .and_then(|v| v.as_f64())
141 .unwrap_or(0.0);
142 println!(" Result: {}", sum);
143 }
144
145 Ok(())
146}More examples
6fn main() {
7 println!("\n🚀 JSON Evaluation - SPAJ Toggle Example\n");
8
9 let schema_path = Path::new("samples/spaj.json");
10 let schema_str = fs::read_to_string(schema_path).expect("Failed to read schema");
11
12 // Initial data with minimal context required
13 let context_str = json!({
14 "agentProfile": { "sob": "AG" }
15 }).to_string();
16
17 let initial_data = json!({
18 "illustration": {
19 "basicinformation": {
20 "print_polflag": false
21 }
22 }
23 }).to_string();
24
25 // Initialize logic
26 let mut eval = JSONEval::new(&schema_str, Some(&context_str), Some(&initial_data))
27 .expect("Failed to create JSONEval");
28
29 // Helper to check visibility
30 let check_visibility = |eval: &mut JSONEval, expected_hidden: bool, step: &str| {
31 let result = eval.get_evaluated_schema(false);
32 let hidden = result.pointer("/illustration/properties/basicinformation/properties/print_poladdress/condition/hidden")
33 .and_then(|v| v.as_bool());
34
35 match hidden {
36 Some(val) => {
37 if val == expected_hidden {
38 println!("✅ {}: Hidden = {} (Expected: {})", step, val, expected_hidden);
39 } else {
40 println!("❌ {}: Hidden = {} (Expected: {})", step, val, expected_hidden);
41 }
42 },
43 None => println!("❌ {}: 'hidden' property not found", step),
44 }
45 };
46
47 // Step 1: Initial state (false)
48 println!("Step 1: Initial State (print_polflag: false)");
49 eval.evaluate(&initial_data, Some(&context_str), None, None).expect("Evaluation failed");
50 check_visibility(&mut eval, true, "Initial check");
51
52 // Step 2: Toggle to true
53 println!("\nStep 2: Toggle True (print_polflag: true)");
54 let data_true = json!({
55 "illustration": {
56 "basicinformation": {
57 "print_polflag": true
58 }
59 }
60 }).to_string();
61 eval.evaluate(&data_true, Some(&context_str), None, None).expect("Evaluation failed");
62 check_visibility(&mut eval, false, "Toggle ON check");
63
64 // Step 3: Toggle back to false
65 println!("\nStep 3: Toggle False (print_polflag: false)");
66 let data_false = json!({
67 "illustration": {
68 "basicinformation": {
69 "print_polflag": false
70 }
71 }
72 }).to_string();
73 eval.evaluate(&data_false, Some(&context_str), None, None).expect("Evaluation failed");
74
75 let hidden_path = "#/illustration/properties/basicinformation/properties/print_poladdress/condition/hidden";
76 if let Some(deps) = eval.dependencies.get(hidden_path) {
77 println!("Debug: Dependencies for hidden: {:?}", deps);
78 } else {
79 println!("Debug: No dependencies found for hidden path");
80 }
81
82 // Debug: Print current flag value
83 if let Some(val) = eval.get_evaluated_schema(false).pointer("/illustration/properties/basicinformation/properties/print_polflag/value") {
84 println!("Debug: print_polflag value is: {}", val);
85 }
86
87 check_visibility(&mut eval, true, "Toggle OFF check");
88}28fn main() {
29 let args: Vec<String> = std::env::args().collect();
30 let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic_msgpack");
31
32 let mut scenario_filter: Option<String> = None;
33 let mut enable_comparison = false;
34 let mut show_timing = false;
35 let mut i = 1;
36
37 // Parse arguments
38 while i < args.len() {
39 let arg = &args[i];
40
41 if arg == "-h" || arg == "--help" {
42 print_help(program_name);
43 return;
44 } else if arg == "--compare" {
45 enable_comparison = true;
46 } else if arg == "--timing" {
47 show_timing = true;
48 } else if !arg.starts_with('-') {
49 scenario_filter = Some(arg.clone());
50 } else {
51 eprintln!("Error: unknown option '{}'", arg);
52 print_help(program_name);
53 return;
54 }
55
56 i += 1;
57 }
58
59 println!("\n🚀 JSON Evaluation - Basic Example (MessagePack Schema)\n");
60
61 if enable_comparison {
62 println!("🔍 Comparison: enabled");
63 }
64 if show_timing {
65 println!("⏱️ Internal timing: enabled");
66 }
67 if enable_comparison || show_timing {
68 println!();
69 }
70
71 let samples_dir = Path::new("samples");
72 let mut scenarios = common::discover_scenarios(samples_dir);
73
74 // Filter to only MessagePack scenarios
75 scenarios.retain(|s| s.is_msgpack);
76
77 // Filter scenarios if a filter is provided
78 if let Some(ref filter) = scenario_filter {
79 scenarios.retain(|s| s.name.contains(filter));
80 println!("📋 Filtering scenarios matching: '{}'\n", filter);
81 }
82
83 if scenarios.is_empty() {
84 if let Some(filter) = scenario_filter {
85 println!(
86 "ℹ️ No MessagePack scenarios found matching '{}' in `{}`.",
87 filter,
88 samples_dir.display()
89 );
90 } else {
91 println!(
92 "ℹ️ No MessagePack scenarios discovered in `{}`. Add files like `name.bform` and `name-data.json`.",
93 samples_dir.display()
94 );
95 }
96 return;
97 }
98
99 println!("📊 Found {} MessagePack scenario(s)\n", scenarios.len());
100
101 let mut total_parse_time = std::time::Duration::ZERO;
102 let mut total_eval_time = std::time::Duration::ZERO;
103 let mut successful_scenarios = 0;
104 let mut comparison_failures = 0;
105
106 for scenario in &scenarios {
107 println!("==============================");
108 println!("Scenario: {}", scenario.name);
109 println!("Schema: {} (MessagePack)", scenario.schema_path.display());
110 println!("Data: {}\n", scenario.data_path.display());
111
112 // Clear timing data from previous scenarios
113 if show_timing {
114 json_eval_rs::enable_timing();
115 json_eval_rs::clear_timing_data();
116 }
117
118 let data_str = fs::read_to_string(&scenario.data_path)
119 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
120
121 // Step 1: Parse schema (new_from_msgpack)
122 let parse_start = Instant::now();
123
124 let schema_msgpack = fs::read(&scenario.schema_path)
125 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
126
127 println!(" 📦 MessagePack schema size: {} bytes", schema_msgpack.len());
128
129 let mut eval = JSONEval::new_from_msgpack(&schema_msgpack, None, Some(&data_str))
130 .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e));
131
132 let parse_time = parse_start.elapsed();
133 println!(" 📝 Parse (msgpack): {:?}", parse_time);
134
135 // Step 2: Evaluate
136 let eval_start = Instant::now();
137
138 eval.evaluate(&data_str, Some("{}"), None, None)
139 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
140
141 let evaluated_schema = eval.get_evaluated_schema(false);
142 let eval_time = eval_start.elapsed();
143
144 println!(" ⚡ Eval: {:?}", eval_time);
145 println!(" ⏱️ Total: {:?}\n", parse_time + eval_time);
146
147 // Print detailed timing breakdown if --timing flag is set
148 if show_timing {
149 json_eval_rs::print_timing_summary();
150 }
151
152 total_parse_time += parse_time;
153 total_eval_time += eval_time;
154 successful_scenarios += 1;
155
156 // Save results
157 let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
158 let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
159
160 fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
161 .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
162
163 let mut metadata_obj = Map::new();
164 metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
165 metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
166 metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
167
168 fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
169 .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
170
171 println!("✅ Results saved:");
172 println!(" - {}", evaluated_path.display());
173 println!(" - {}\n", parsed_path.display());
174
175 // Optional comparison
176 if enable_comparison {
177 if let Some(comp_path) = &scenario.comparison_path {
178 if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
179 comparison_failures += 1;
180 }
181 println!();
182 }
183 }
184 }
185
186 // Print summary
187 println!("{}", "=".repeat(50));
188 println!("📊 Summary");
189 println!("{}", "=".repeat(50));
190 println!("Total scenarios run: {}", successful_scenarios);
191 println!("Total parse time: {:?}", total_parse_time);
192 println!("Total eval time: {:?}", total_eval_time);
193 println!("Total time: {:?}", total_parse_time + total_eval_time);
194
195 if successful_scenarios > 1 {
196 println!("\nAverage per scenario:");
197 println!(" Parse: {:?}", total_parse_time / successful_scenarios as u32);
198 println!(" Eval: {:?}", total_eval_time / successful_scenarios as u32);
199 }
200
201 if enable_comparison {
202 println!("Comparison failures: {}", comparison_failures);
203 }
204
205 println!("\n✅ All scenarios completed!\n");
206}30fn main() {
31 let args: Vec<String> = std::env::args().collect();
32 let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic_parsed");
33
34 let mut scenario_filter: Option<String> = None;
35 let mut enable_comparison = false;
36 let mut show_timing = false;
37 let mut i = 1;
38
39 // Parse arguments
40 while i < args.len() {
41 let arg = &args[i];
42
43 if arg == "-h" || arg == "--help" {
44 print_help(program_name);
45 return;
46 } else if arg == "--compare" {
47 enable_comparison = true;
48 } else if arg == "--timing" {
49 show_timing = true;
50 } else if !arg.starts_with('-') {
51 scenario_filter = Some(arg.clone());
52 } else {
53 eprintln!("Error: unknown option '{}'", arg);
54 print_help(program_name);
55 return;
56 }
57
58 i += 1;
59 }
60
61 println!("\n🚀 JSON Evaluation - Basic Example (ParsedSchema)\n");
62 println!("📦 Using Arc<ParsedSchema> for efficient caching\n");
63
64 if enable_comparison {
65 println!("🔍 Comparison: enabled");
66 }
67 if show_timing {
68 println!("⏱️ Internal timing: enabled");
69 }
70 if enable_comparison || show_timing {
71 println!();
72 }
73
74 let samples_dir = Path::new("samples");
75 let mut scenarios = common::discover_scenarios(samples_dir);
76
77 // Filter scenarios if a filter is provided
78 if let Some(ref filter) = scenario_filter {
79 scenarios.retain(|s| s.name.contains(filter));
80 println!("📋 Filtering scenarios matching: '{}'\n", filter);
81 }
82
83 if scenarios.is_empty() {
84 if let Some(filter) = scenario_filter {
85 println!(
86 "ℹ️ No scenarios found matching '{}' in `{}`.",
87 filter,
88 samples_dir.display()
89 );
90 } else {
91 println!(
92 "ℹ️ No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
93 samples_dir.display()
94 );
95 }
96 return;
97 }
98
99 println!("📊 Found {} scenario(s)\n", scenarios.len());
100
101 let mut total_parse_time = std::time::Duration::ZERO;
102 let mut total_eval_time = std::time::Duration::ZERO;
103 let mut successful_scenarios = 0;
104 let mut comparison_failures = 0;
105
106 for scenario in &scenarios {
107 println!("==============================");
108 println!("Scenario: {}", scenario.name);
109 println!("Schema: {} ({})",
110 scenario.schema_path.display(),
111 if scenario.is_msgpack { "MessagePack" } else { "JSON" }
112 );
113 println!("Data: {}\n", scenario.data_path.display());
114
115 // Clear timing data from previous scenarios
116 if show_timing {
117 json_eval_rs::enable_timing();
118 json_eval_rs::clear_timing_data();
119 }
120
121 let data_str = fs::read_to_string(&scenario.data_path)
122 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
123
124 // Step 1: Parse schema once
125 let parse_start = Instant::now();
126 let parsed_schema = if scenario.is_msgpack {
127 let schema_msgpack = fs::read(&scenario.schema_path)
128 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
129 println!(" 📦 MessagePack schema size: {} bytes", schema_msgpack.len());
130 Arc::new(ParsedSchema::parse_msgpack(&schema_msgpack)
131 .unwrap_or_else(|e| panic!("failed to parse MessagePack schema: {}", e)))
132 } else {
133 let schema_str = fs::read_to_string(&scenario.schema_path)
134 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
135 Arc::new(ParsedSchema::parse(&schema_str)
136 .unwrap_or_else(|e| panic!("failed to parse schema: {}", e)))
137 };
138 let parse_time = parse_start.elapsed();
139 println!(" 📝 Schema parsing: {:?}", parse_time);
140
141 // Step 2: Create JSONEval from ParsedSchema (reuses compiled logic)
142 let eval_start = Instant::now();
143 let mut eval = JSONEval::with_parsed_schema(
144 parsed_schema.clone(), // Arc::clone is cheap!
145 Some("{}"),
146 Some(&data_str)
147 ).unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e));
148
149 eval.evaluate(&data_str, Some("{}"), None, None)
150 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
151
152 let evaluated_schema = eval.get_evaluated_schema(false);
153 let eval_time = eval_start.elapsed();
154
155 println!(" ⚡ Eval: {:?}", eval_time);
156 println!(" ⏱️ Total: {:?}\n", parse_time + eval_time);
157
158 // Print detailed timing breakdown if --timing flag is set
159 if show_timing {
160 json_eval_rs::print_timing_summary();
161 }
162
163 total_parse_time += parse_time;
164 total_eval_time += eval_time;
165 successful_scenarios += 1;
166
167 // Save results
168 let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
169 let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
170
171 fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
172 .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
173
174 let mut metadata_obj = Map::new();
175 metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
176 metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
177 metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
178
179 fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
180 .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
181
182 println!("✅ Results saved:");
183 println!(" - {}", evaluated_path.display());
184 println!(" - {}\n", parsed_path.display());
185
186 // Optional comparison
187 if enable_comparison {
188 if let Some(comp_path) = &scenario.comparison_path {
189 if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
190 comparison_failures += 1;
191 }
192 println!();
193 }
194 }
195 }
196
197 // Print summary
198 println!("{}", "=".repeat(50));
199 println!("📊 Summary");
200 println!("{}", "=".repeat(50));
201 println!("Total scenarios run: {}", successful_scenarios);
202 println!("Total parsing time: {:?}", total_parse_time);
203 println!("Total evaluation time: {:?}", total_eval_time);
204 println!("Total time: {:?}", total_parse_time + total_eval_time);
205
206 if successful_scenarios > 1 {
207 println!("\nAverage per scenario:");
208 println!(" Parsing: {:?}", total_parse_time / successful_scenarios as u32);
209 println!(" Evaluation: {:?}", total_eval_time / successful_scenarios as u32);
210 }
211
212 if enable_comparison {
213 println!("\nComparison failures: {}", comparison_failures);
214 }
215
216 println!("\n✅ All scenarios completed!\n");
217}28fn main() {
29 let args: Vec<String> = std::env::args().collect();
30 let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic");
31
32 let mut scenario_filter: Option<String> = None;
33 let mut enable_comparison = false;
34 let mut show_timing = false;
35 let mut i = 1;
36
37 // Parse arguments
38 while i < args.len() {
39 let arg = &args[i];
40
41 if arg == "-h" || arg == "--help" {
42 print_help(program_name);
43 return;
44 } else if arg == "--compare" {
45 enable_comparison = true;
46 } else if arg == "--timing" {
47 show_timing = true;
48 } else if !arg.starts_with('-') {
49 scenario_filter = Some(arg.clone());
50 } else {
51 eprintln!("Error: unknown option '{}'", arg);
52 print_help(program_name);
53 return;
54 }
55
56 i += 1;
57 }
58
59 println!("\n🚀 JSON Evaluation - Basic Example (JSON Schema)\n");
60
61 if enable_comparison {
62 println!("🔍 Comparison: enabled");
63 }
64 if show_timing {
65 println!("⏱️ Internal timing: enabled");
66 }
67 if enable_comparison || show_timing {
68 println!();
69 }
70
71 let samples_dir = Path::new("samples");
72 let mut scenarios = common::discover_scenarios(samples_dir);
73
74 // Filter out MessagePack scenarios - only use JSON
75 scenarios.retain(|s| !s.is_msgpack);
76
77 // Filter scenarios if a filter is provided
78 if let Some(ref filter) = scenario_filter {
79 scenarios.retain(|s| s.name.contains(filter));
80 println!("📋 Filtering scenarios matching: '{}'\n", filter);
81 }
82
83 if scenarios.is_empty() {
84 if let Some(filter) = scenario_filter {
85 println!(
86 "ℹ️ No scenarios found matching '{}' in `{}`.",
87 filter,
88 samples_dir.display()
89 );
90 } else {
91 println!(
92 "ℹ️ No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
93 samples_dir.display()
94 );
95 }
96 return;
97 }
98
99 println!("📊 Found {} scenario(s)\n", scenarios.len());
100
101 let mut total_parse_time = std::time::Duration::ZERO;
102 let mut total_eval_time = std::time::Duration::ZERO;
103 let mut successful_scenarios = 0;
104 let mut comparison_failures = 0;
105
106 for scenario in &scenarios {
107 println!("==============================");
108 println!("Scenario: {}", scenario.name);
109 println!("Schema: {} ({})",
110 scenario.schema_path.display(),
111 if scenario.is_msgpack { "MessagePack" } else { "JSON" }
112 );
113 println!("Data: {}\n", scenario.data_path.display());
114
115 // Clear timing data from previous scenarios
116 if show_timing {
117 json_eval_rs::enable_timing();
118 json_eval_rs::clear_timing_data();
119 }
120
121 let data_str = fs::read_to_string(&scenario.data_path)
122 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
123
124 // Step 1: Parse schema (JSONEval::new)
125 let parse_start = Instant::now();
126
127 let schema_str = fs::read_to_string(&scenario.schema_path)
128 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
129
130 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str))
131 .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e));
132
133 let parse_time = parse_start.elapsed();
134 println!(" 📝 Parse (new): {:?}", parse_time);
135
136 // Step 2: Evaluate
137 let eval_start = Instant::now();
138
139 eval.evaluate(&data_str, Some("{}"), None, None)
140 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
141
142 // Step 3: Validate
143 let validation_start = Instant::now();
144 let validation_result = eval.validate(&data_str, None, None, None)
145 .unwrap_or_else(|e| panic!("validation failed: {}", e));
146 let validation_time = validation_start.elapsed();
147 println!(" 🛡️ Validate: {:?}", validation_time);
148
149 // Legacy behavior: get_evaluated_schema takes skip_layout: bool
150 // We pass false to ensure layout IS resolved
151 let evaluated_schema = eval.get_evaluated_schema(false);
152 let schema_value = eval.get_schema_value();
153 let eval_time = eval_start.elapsed();
154
155 println!(" ⚡ Eval: {:?}", eval_time);
156 println!(" ⏱️ Total: {:?}\n", parse_time + eval_time);
157
158 // Print detailed timing breakdown if --timing flag is set
159 if show_timing {
160 json_eval_rs::print_timing_summary();
161 }
162
163 total_parse_time += parse_time;
164 total_eval_time += eval_time;
165 successful_scenarios += 1;
166
167 // Save results
168 let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
169 let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
170 let value_path = samples_dir.join(format!("{}-schema-value.json", scenario.name));
171 let validation_path = samples_dir.join(format!("{}-validation.json", scenario.name));
172
173 fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
174 .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
175
176 let mut metadata_obj = Map::new();
177 metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
178 metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
179 metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
180
181 fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
182 .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
183
184 fs::write(&value_path, common::pretty_json(&schema_value))
185 .unwrap_or_else(|e| panic!("failed to write {}: {}", value_path.display(), e));
186
187 let validation_value = serde_json::to_value(&validation_result)
188 .unwrap_or_else(|e| panic!("failed to serialize validation result: {}", e));
189 fs::write(&validation_path, common::pretty_json(&validation_value))
190 .unwrap_or_else(|e| panic!("failed to write {}: {}", validation_path.display(), e));
191
192 println!("✅ Results saved:");
193 println!(" - {}", evaluated_path.display());
194 println!(" - {}", parsed_path.display());
195 println!(" - {}", value_path.display());
196 println!(" - {}\n", validation_path.display());
197
198 // Optional comparison
199 if enable_comparison {
200 if let Some(comp_path) = &scenario.comparison_path {
201 if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
202 comparison_failures += 1;
203 }
204 println!();
205 }
206 }
207 }
208
209 // Print summary
210 println!("{}", "=".repeat(50));
211 println!("📊 Summary");
212 println!("{}", "=".repeat(50));
213 println!("Total scenarios run: {}", successful_scenarios);
214 println!("Total parse time: {:?}", total_parse_time);
215 println!("Total eval time: {:?}", total_eval_time);
216 println!("Total time: {:?}", total_parse_time + total_eval_time);
217
218 if successful_scenarios > 1 {
219 println!("\nAverage per scenario:");
220 println!(" Parse: {:?}", total_parse_time / successful_scenarios as u32);
221 println!(" Eval: {:?}", total_eval_time / successful_scenarios as u32);
222 }
223
224 if enable_comparison {
225 println!("Comparison failures: {}", comparison_failures);
226 }
227
228 println!("\n✅ All scenarios completed!\n");
229}32fn main() {
33 let args: Vec<String> = std::env::args().collect();
34 let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("benchmark");
35
36 let mut iterations = 1usize;
37 let mut scenario_filter: Option<String> = None;
38 let mut show_cpu_info = false;
39 let mut use_parsed_schema = false;
40 let mut use_cache = false;
41 let mut concurrent_count: Option<usize> = None;
42 let mut enable_comparison = false;
43 let mut show_timing = false;
44 let mut i = 1;
45
46 // Parse arguments
47 while i < args.len() {
48 let arg = &args[i];
49
50 if arg == "-h" || arg == "--help" {
51 print_help(program_name);
52 return;
53 } else if arg == "--cpu-info" {
54 show_cpu_info = true;
55 } else if arg == "--parsed" {
56 use_parsed_schema = true;
57 } else if arg == "--cache" {
58 use_cache = true;
59 } else if arg == "--compare" {
60 enable_comparison = true;
61 } else if arg == "--timing" {
62 show_timing = true;
63 } else if arg == "--concurrent" {
64 if i + 1 >= args.len() {
65 eprintln!("Error: {} requires a value", arg);
66 print_help(program_name);
67 return;
68 }
69 i += 1;
70 match args[i].parse::<usize>() {
71 Ok(n) if n > 0 => concurrent_count = Some(n),
72 _ => {
73 eprintln!("Error: concurrent count must be a positive integer, got '{}'", args[i]);
74 return;
75 }
76 }
77 } else if arg == "-i" || arg == "--iterations" {
78 if i + 1 >= args.len() {
79 eprintln!("Error: {} requires a value", arg);
80 print_help(program_name);
81 return;
82 }
83 i += 1;
84 match args[i].parse::<usize>() {
85 Ok(n) if n > 0 => iterations = n,
86 _ => {
87 eprintln!("Error: iterations must be a positive integer, got '{}'", args[i]);
88 return;
89 }
90 }
91 } else if !arg.starts_with('-') {
92 scenario_filter = Some(arg.clone());
93 } else {
94 eprintln!("Error: unknown option '{}'", arg);
95 print_help(program_name);
96 return;
97 }
98
99 i += 1;
100 }
101
102 println!("\n🚀 JSON Evaluation - Benchmark\n");
103
104 // Show CPU info if requested or if running benchmarks
105 if show_cpu_info || iterations > 1 || concurrent_count.is_some() {
106 common::print_cpu_info();
107 }
108
109 if use_parsed_schema {
110 println!("📦 Mode: ParsedSchema (parse once, reuse for all iterations)\n");
111 }
112
113 if use_cache {
114 println!("♻️ Mode: Cache (reuse JSONEval instance across iterations)\n");
115 }
116
117 if let Some(count) = concurrent_count {
118 println!("🔀 Concurrent evaluations: {} threads\n", count);
119 } else if iterations > 1 {
120 println!("🔄 Iterations per scenario: {}\n", iterations);
121 }
122
123 if enable_comparison {
124 println!("🔍 Comparison: enabled");
125 }
126 if show_timing {
127 println!("⏱️ Internal timing: enabled");
128 }
129 if enable_comparison || show_timing {
130 println!();
131 }
132
133 let samples_dir = Path::new("samples");
134 let mut scenarios = common::discover_scenarios(samples_dir);
135
136 // Filter scenarios if a filter is provided
137 if let Some(ref filter) = scenario_filter {
138 scenarios.retain(|s| s.name.contains(filter));
139 println!("📋 Filtering scenarios matching: '{}'\n", filter);
140 }
141
142 if scenarios.is_empty() {
143 if let Some(filter) = scenario_filter {
144 println!(
145 "ℹ️ No scenarios found matching '{}' in `{}`.",
146 filter,
147 samples_dir.display()
148 );
149 } else {
150 println!(
151 "ℹ️ No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
152 samples_dir.display()
153 );
154 }
155 return;
156 }
157
158 println!("📊 Found {} scenario(s)\n", scenarios.len());
159
160 let mut total_parse_time = std::time::Duration::ZERO;
161 let mut total_eval_time = std::time::Duration::ZERO;
162 let mut successful_scenarios = 0;
163 let mut comparison_failures = 0;
164
165 for scenario in &scenarios {
166 println!("==============================");
167 println!("Scenario: {}", scenario.name);
168 println!("Schema: {} ({})",
169 scenario.schema_path.display(),
170 if scenario.is_msgpack { "MessagePack" } else { "JSON" }
171 );
172 println!("Data: {}\n", scenario.data_path.display());
173
174 // Clear timing data from previous scenarios
175 if show_timing {
176 json_eval_rs::enable_timing();
177 json_eval_rs::clear_timing_data();
178 }
179
180 let data_str = fs::read_to_string(&scenario.data_path)
181 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
182
183 println!("Running evaluation...\n");
184
185 let (parse_time, eval_time, evaluated_schema, eval, iteration_times) = if use_parsed_schema {
186 // ParsedSchema mode: parse once, reuse for all iterations/threads
187 let start_time = Instant::now();
188
189 let parsed_schema = if scenario.is_msgpack {
190 let schema_msgpack = fs::read(&scenario.schema_path)
191 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
192 println!(" 📦 MessagePack schema size: {} bytes", schema_msgpack.len());
193 Arc::new(ParsedSchema::parse_msgpack(&schema_msgpack)
194 .unwrap_or_else(|e| panic!("failed to parse MessagePack schema: {}", e)))
195 } else {
196 let schema_str = fs::read_to_string(&scenario.schema_path)
197 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
198 Arc::new(ParsedSchema::parse(&schema_str)
199 .unwrap_or_else(|e| panic!("failed to parse schema: {}", e)))
200 };
201
202 let parse_time = start_time.elapsed();
203 println!(" Schema parsing & compilation: {:?}", parse_time);
204
205 // Concurrent mode with ParsedSchema
206 if let Some(thread_count) = concurrent_count {
207 use std::thread;
208
209 let eval_start = Instant::now();
210 let mut handles = vec![];
211
212 for thread_id in 0..thread_count {
213 let parsed_clone = parsed_schema.clone();
214 let data_str_clone = data_str.clone();
215 let iter_count = iterations;
216 let thread_use_cache = use_cache;
217
218 let handle = thread::spawn(move || {
219 let mut thread_times = Vec::with_capacity(iter_count);
220 let mut last_schema = Value::Null;
221
222 let mut eval_instance = JSONEval::with_parsed_schema(
223 parsed_clone.clone(),
224 Some("{}"),
225 Some(&data_str_clone)
226 ).unwrap();
227
228 for iter in 0..iter_count {
229 let iter_start = Instant::now();
230
231 if !thread_use_cache && iter > 0 {
232 eval_instance = JSONEval::with_parsed_schema(
233 parsed_clone.clone(),
234 Some("{}"),
235 Some(&data_str_clone)
236 ).unwrap();
237 }
238
239 eval_instance.evaluate(&data_str_clone, Some("{}"), None, None).unwrap();
240 last_schema = eval_instance.get_evaluated_schema(false);
241 thread_times.push(iter_start.elapsed());
242 }
243
244 (thread_times, last_schema, thread_id)
245 });
246 handles.push(handle);
247 }
248
249 let mut all_iteration_times = Vec::new();
250 let mut evaluated_schema = Value::Null;
251
252 for handle in handles {
253 let (thread_times, thread_schema, thread_id) = handle.join().unwrap();
254 println!(" Thread {} completed {} iterations", thread_id, thread_times.len());
255 all_iteration_times.extend(thread_times);
256 evaluated_schema = thread_schema; // Use last thread's result
257 }
258
259 let eval_time = eval_start.elapsed();
260
261 // Create a temp eval for metadata export
262 let temp_eval = JSONEval::with_parsed_schema(
263 parsed_schema.clone(),
264 Some("{}"),
265 Some(&data_str)
266 ).unwrap();
267
268 (parse_time, eval_time, evaluated_schema, temp_eval, all_iteration_times)
269 } else {
270 // Sequential iterations with ParsedSchema
271 let eval_start = Instant::now();
272 let mut evaluated_schema = Value::Null;
273 let mut iteration_times = Vec::with_capacity(iterations);
274 let mut eval_instance = JSONEval::with_parsed_schema(
275 parsed_schema.clone(),
276 Some("{}"),
277 Some(&data_str)
278 ).unwrap();
279
280 for iter in 0..iterations {
281 let iter_start = Instant::now();
282
283 if !use_cache && iter > 0 {
284 eval_instance = JSONEval::with_parsed_schema(
285 parsed_schema.clone(),
286 Some("{}"),
287 Some(&data_str)
288 ).unwrap();
289 }
290
291 eval_instance.evaluate(&data_str, Some("{}"), None, None)
292 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
293 evaluated_schema = eval_instance.get_evaluated_schema(false);
294 iteration_times.push(iter_start.elapsed());
295
296 if iterations > 1 && (iter + 1) % 10 == 0 {
297 print!(".");
298 if (iter + 1) % 50 == 0 {
299 println!(" {}/{}", iter + 1, iterations);
300 }
301 }
302 }
303
304 if iterations > 1 && iterations % 50 != 0 {
305 println!(" {}/{}", iterations, iterations);
306 }
307
308 let eval_time = eval_start.elapsed();
309 (parse_time, eval_time, evaluated_schema, eval_instance, iteration_times)
310 }
311 } else {
312 // Traditional mode: parse and create JSONEval each time
313 let schema_msgpack = if scenario.is_msgpack {
314 let bytes = fs::read(&scenario.schema_path)
315 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
316 println!(" 📦 MessagePack schema size: {} bytes", bytes.len());
317 Some(bytes)
318 } else {
319 None
320 };
321
322 let schema_str = if !scenario.is_msgpack {
323 Some(fs::read_to_string(&scenario.schema_path)
324 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e)))
325 } else {
326 None
327 };
328
329 let start_time = Instant::now();
330 let mut eval = if scenario.is_msgpack {
331 JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
332 .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
333 } else {
334 JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
335 .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
336 };
337 let parse_time = start_time.elapsed();
338 println!(" Schema parsing & compilation: {:?}", parse_time);
339
340 let eval_start = Instant::now();
341 let mut evaluated_schema = Value::Null;
342 let mut iteration_times = Vec::with_capacity(iterations);
343
344 for iter in 0..iterations {
345 let iter_start = Instant::now();
346
347 if !use_cache && iter > 0 {
348 eval = if scenario.is_msgpack {
349 JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
350 .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
351 } else {
352 JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
353 .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
354 };
355 }
356
357 eval.evaluate(&data_str, Some("{}"), None, None)
358 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
359 evaluated_schema = eval.get_evaluated_schema(false);
360 iteration_times.push(iter_start.elapsed());
361
362 if iterations > 1 && (iter + 1) % 10 == 0 {
363 print!(".");
364 if (iter + 1) % 50 == 0 {
365 println!(" {}/{}", iter + 1, iterations);
366 }
367 }
368 }
369
370 if iterations > 1 && iterations % 50 != 0 {
371 println!(" {}/{}", iterations, iterations);
372 }
373
374 let eval_time = eval_start.elapsed();
375 (parse_time, eval_time, evaluated_schema, eval, iteration_times)
376 };
377
378 // Calculate statistics
379 let total_iterations = iteration_times.len();
380 if total_iterations == 1 {
381 println!(" Evaluation: {:?}", eval_time);
382 } else {
383 let avg_time = eval_time / total_iterations as u32;
384 let min_time = iteration_times.iter().min().unwrap();
385 let max_time = iteration_times.iter().max().unwrap();
386
387 println!(" Total evaluation time: {:?}", eval_time);
388 println!(" Total iterations: {}", total_iterations);
389 println!(" Average per iteration: {:?}", avg_time);
390 println!(" Min: {:?} | Max: {:?}", min_time, max_time);
391
392 // Show cache statistics
393 let cache_stats = eval.cache_stats();
394 println!(" Cache: {} entries, {} hits, {} misses ({:.1}% hit rate)",
395 cache_stats.entries,
396 cache_stats.hits,
397 cache_stats.misses,
398 cache_stats.hit_rate * 100.0
399 );
400 }
401
402 let total_time = parse_time + eval_time;
403 println!("⏱️ Execution time: {:?}\n", total_time);
404
405 // Print detailed timing breakdown if --timing flag is set
406 if show_timing {
407 json_eval_rs::print_timing_summary();
408 }
409
410 // Track statistics
411 total_parse_time += parse_time;
412 total_eval_time += eval_time;
413 successful_scenarios += 1;
414
415 let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
416 let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
417
418 fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
419 .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
420
421 let mut metadata_obj = Map::new();
422 metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
423 metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
424
425 fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
426 .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
427
428 println!("✅ Results saved:");
429 println!(" - {}", evaluated_path.display());
430 println!(" - {}\n", parsed_path.display());
431
432 // Optional comparison
433 if enable_comparison {
434 if let Some(comp_path) = &scenario.comparison_path {
435 if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
436 comparison_failures += 1;
437 }
438 println!();
439 }
440 }
441 }
442
443 // Print summary statistics
444 if successful_scenarios > 0 {
445 println!("\n{}", "=".repeat(50));
446 println!("📊 Summary Statistics");
447 println!("{}", "=".repeat(50));
448 println!("Total scenarios run: {}", successful_scenarios);
449 println!("Total parsing time: {:?}", total_parse_time);
450 println!("Total evaluation time: {:?}", total_eval_time);
451 println!("Total time: {:?}", total_parse_time + total_eval_time);
452
453 if successful_scenarios > 1 {
454 println!("\nAverage per scenario:");
455 println!(" Parsing: {:?}", total_parse_time / successful_scenarios as u32);
456 println!(" Evaluation: {:?}", total_eval_time / successful_scenarios as u32);
457 }
458
459 if enable_comparison {
460 println!("\nComparison failures: {}", comparison_failures);
461 }
462
463 println!("\n✅ All scenarios completed successfully!\n");
464 }
465}Sourcepub fn get_schema_value_by_path(&self, path: &str) -> Option<Value>
pub fn get_schema_value_by_path(&self, path: &str) -> Option<Value>
Get specific schema value by path
Sourcepub fn get_schema_value(&mut self) -> Value
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?
28fn main() {
29 let args: Vec<String> = std::env::args().collect();
30 let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic");
31
32 let mut scenario_filter: Option<String> = None;
33 let mut enable_comparison = false;
34 let mut show_timing = false;
35 let mut i = 1;
36
37 // Parse arguments
38 while i < args.len() {
39 let arg = &args[i];
40
41 if arg == "-h" || arg == "--help" {
42 print_help(program_name);
43 return;
44 } else if arg == "--compare" {
45 enable_comparison = true;
46 } else if arg == "--timing" {
47 show_timing = true;
48 } else if !arg.starts_with('-') {
49 scenario_filter = Some(arg.clone());
50 } else {
51 eprintln!("Error: unknown option '{}'", arg);
52 print_help(program_name);
53 return;
54 }
55
56 i += 1;
57 }
58
59 println!("\n🚀 JSON Evaluation - Basic Example (JSON Schema)\n");
60
61 if enable_comparison {
62 println!("🔍 Comparison: enabled");
63 }
64 if show_timing {
65 println!("⏱️ Internal timing: enabled");
66 }
67 if enable_comparison || show_timing {
68 println!();
69 }
70
71 let samples_dir = Path::new("samples");
72 let mut scenarios = common::discover_scenarios(samples_dir);
73
74 // Filter out MessagePack scenarios - only use JSON
75 scenarios.retain(|s| !s.is_msgpack);
76
77 // Filter scenarios if a filter is provided
78 if let Some(ref filter) = scenario_filter {
79 scenarios.retain(|s| s.name.contains(filter));
80 println!("📋 Filtering scenarios matching: '{}'\n", filter);
81 }
82
83 if scenarios.is_empty() {
84 if let Some(filter) = scenario_filter {
85 println!(
86 "ℹ️ No scenarios found matching '{}' in `{}`.",
87 filter,
88 samples_dir.display()
89 );
90 } else {
91 println!(
92 "ℹ️ No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
93 samples_dir.display()
94 );
95 }
96 return;
97 }
98
99 println!("📊 Found {} scenario(s)\n", scenarios.len());
100
101 let mut total_parse_time = std::time::Duration::ZERO;
102 let mut total_eval_time = std::time::Duration::ZERO;
103 let mut successful_scenarios = 0;
104 let mut comparison_failures = 0;
105
106 for scenario in &scenarios {
107 println!("==============================");
108 println!("Scenario: {}", scenario.name);
109 println!("Schema: {} ({})",
110 scenario.schema_path.display(),
111 if scenario.is_msgpack { "MessagePack" } else { "JSON" }
112 );
113 println!("Data: {}\n", scenario.data_path.display());
114
115 // Clear timing data from previous scenarios
116 if show_timing {
117 json_eval_rs::enable_timing();
118 json_eval_rs::clear_timing_data();
119 }
120
121 let data_str = fs::read_to_string(&scenario.data_path)
122 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
123
124 // Step 1: Parse schema (JSONEval::new)
125 let parse_start = Instant::now();
126
127 let schema_str = fs::read_to_string(&scenario.schema_path)
128 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
129
130 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str))
131 .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e));
132
133 let parse_time = parse_start.elapsed();
134 println!(" 📝 Parse (new): {:?}", parse_time);
135
136 // Step 2: Evaluate
137 let eval_start = Instant::now();
138
139 eval.evaluate(&data_str, Some("{}"), None, None)
140 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
141
142 // Step 3: Validate
143 let validation_start = Instant::now();
144 let validation_result = eval.validate(&data_str, None, None, None)
145 .unwrap_or_else(|e| panic!("validation failed: {}", e));
146 let validation_time = validation_start.elapsed();
147 println!(" 🛡️ Validate: {:?}", validation_time);
148
149 // Legacy behavior: get_evaluated_schema takes skip_layout: bool
150 // We pass false to ensure layout IS resolved
151 let evaluated_schema = eval.get_evaluated_schema(false);
152 let schema_value = eval.get_schema_value();
153 let eval_time = eval_start.elapsed();
154
155 println!(" ⚡ Eval: {:?}", eval_time);
156 println!(" ⏱️ Total: {:?}\n", parse_time + eval_time);
157
158 // Print detailed timing breakdown if --timing flag is set
159 if show_timing {
160 json_eval_rs::print_timing_summary();
161 }
162
163 total_parse_time += parse_time;
164 total_eval_time += eval_time;
165 successful_scenarios += 1;
166
167 // Save results
168 let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
169 let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
170 let value_path = samples_dir.join(format!("{}-schema-value.json", scenario.name));
171 let validation_path = samples_dir.join(format!("{}-validation.json", scenario.name));
172
173 fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
174 .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
175
176 let mut metadata_obj = Map::new();
177 metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
178 metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
179 metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
180
181 fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
182 .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
183
184 fs::write(&value_path, common::pretty_json(&schema_value))
185 .unwrap_or_else(|e| panic!("failed to write {}: {}", value_path.display(), e));
186
187 let validation_value = serde_json::to_value(&validation_result)
188 .unwrap_or_else(|e| panic!("failed to serialize validation result: {}", e));
189 fs::write(&validation_path, common::pretty_json(&validation_value))
190 .unwrap_or_else(|e| panic!("failed to write {}: {}", validation_path.display(), e));
191
192 println!("✅ Results saved:");
193 println!(" - {}", evaluated_path.display());
194 println!(" - {}", parsed_path.display());
195 println!(" - {}", value_path.display());
196 println!(" - {}\n", validation_path.display());
197
198 // Optional comparison
199 if enable_comparison {
200 if let Some(comp_path) = &scenario.comparison_path {
201 if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
202 comparison_failures += 1;
203 }
204 println!();
205 }
206 }
207 }
208
209 // Print summary
210 println!("{}", "=".repeat(50));
211 println!("📊 Summary");
212 println!("{}", "=".repeat(50));
213 println!("Total scenarios run: {}", successful_scenarios);
214 println!("Total parse time: {:?}", total_parse_time);
215 println!("Total eval time: {:?}", total_eval_time);
216 println!("Total time: {:?}", total_parse_time + total_eval_time);
217
218 if successful_scenarios > 1 {
219 println!("\nAverage per scenario:");
220 println!(" Parse: {:?}", total_parse_time / successful_scenarios as u32);
221 println!(" Eval: {:?}", total_eval_time / successful_scenarios as u32);
222 }
223
224 if enable_comparison {
225 println!("Comparison failures: {}", comparison_failures);
226 }
227
228 println!("\n✅ All scenarios completed!\n");
229}Sourcepub fn get_schema_value_array(&self) -> Value
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
Sourcepub fn get_schema_value_object(&self) -> Value
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
Sourcepub fn get_evaluated_schema_without_params(
&mut self,
skip_layout: bool,
) -> Value
pub fn get_evaluated_schema_without_params( &mut self, skip_layout: bool, ) -> Value
Get evaluated schema without $params
Sourcepub fn get_evaluated_schema_msgpack(
&mut self,
skip_layout: bool,
) -> Result<Vec<u8>, String>
pub fn get_evaluated_schema_msgpack( &mut self, skip_layout: bool, ) -> Result<Vec<u8>, String>
Get evaluated schema as MessagePack bytes
Sourcepub fn get_evaluated_schema_by_path(
&mut self,
path: &str,
skip_layout: bool,
) -> Option<Value>
pub fn get_evaluated_schema_by_path( &mut self, path: &str, skip_layout: bool, ) -> Option<Value>
Get value from evaluated schema by path
Sourcepub fn get_evaluated_schema_by_paths(
&mut self,
paths: &[String],
skip_layout: bool,
format: Option<ReturnFormat>,
) -> Value
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
Sourcepub fn get_schema_by_path(&self, path: &str) -> Option<Value>
pub fn get_schema_by_path(&self, path: &str) -> Option<Value>
Get original (unevaluated) schema by path
Sourcepub fn get_schema_by_paths(
&self,
paths: &[String],
format: Option<ReturnFormat>,
) -> Value
pub fn get_schema_by_paths( &self, paths: &[String], format: Option<ReturnFormat>, ) -> Value
Get original schema by multiple paths
Sourcepub fn flatten_object(
prefix: &str,
value: &Value,
result: &mut Map<String, Value>,
)
pub fn flatten_object( prefix: &str, value: &Value, result: &mut Map<String, Value>, )
Flatten a nested object key-value pair to dotted keys
pub fn convert_to_format(value: Value, format: ReturnFormat) -> Value
Source§impl JSONEval
impl JSONEval
Sourcepub fn new(
schema: &str,
context: Option<&str>,
data: Option<&str>,
) -> Result<Self, Error>
pub fn new( schema: &str, context: Option<&str>, data: Option<&str>, ) -> Result<Self, Error>
Examples found in repository?
148fn demo_performance_comparison() -> Result<(), Box<dyn std::error::Error>> {
149 println!("⚡ Example 3: Performance Comparison");
150 println!("Comparing cached vs non-cached schema usage...\n");
151
152 let schema_json = r#"{
153 "$params": {
154 "value": { "type": "number" }
155 },
156 "doubled": {
157 "type": "number",
158 "$evaluation": { "*": [{"var": "$value"}, 2] }
159 },
160 "tripled": {
161 "type": "number",
162 "$evaluation": { "*": [{"var": "$value"}, 3] }
163 }
164 }"#;
165
166 let iterations = 100;
167
168 // WITHOUT CACHE: Parse schema every time
169 println!("🐌 Without cache (parse + evaluate each time):");
170 let start = Instant::now();
171 for i in 0..iterations {
172 let context = format!(r#"{{"value": {}}}"#, i);
173 let mut eval = JSONEval::new(schema_json, Some(&context), None)?;
174 eval.evaluate("{}", None, None, None)?;
175 }
176 let without_cache = start.elapsed();
177 println!(" Time: {:?}", without_cache);
178 println!(" Avg per iteration: {:?}\n", without_cache / iterations);
179
180 // WITH CACHE: Parse once, evaluate many times
181 println!("🚀 With cache (parse once, reuse for all evaluations):");
182 let cache = ParsedSchemaCache::new();
183
184 // Parse once
185 let parse_start = Instant::now();
186 let parsed = ParsedSchema::parse(schema_json)?;
187 cache.insert("perf-test".to_string(), Arc::new(parsed));
188 let parse_time = parse_start.elapsed();
189
190 // Evaluate many times
191 let eval_start = Instant::now();
192 for i in 0..iterations {
193 if let Some(cached) = cache.get("perf-test") {
194 let context = format!(r#"{{"value": {}}}"#, i);
195 let mut eval = JSONEval::with_parsed_schema(cached.clone(), Some(&context), None)?;
196 eval.evaluate("{}", None, None, None)?;
197 }
198 }
199 let eval_time = eval_start.elapsed();
200 let with_cache = parse_time + eval_time;
201
202 println!(" Parse time: {:?}", parse_time);
203 println!(" Eval time: {:?}", eval_time);
204 println!(" Total time: {:?}", with_cache);
205 println!(" Avg per iteration: {:?}\n", eval_time / iterations);
206
207 let speedup = without_cache.as_secs_f64() / with_cache.as_secs_f64();
208 println!("📈 Speedup: {:.2}x faster", speedup);
209
210 Ok(())
211}More examples
4fn main() {
5 let schema = json!({
6 "type": "object",
7 "properties": {
8 "price": {
9 "type": "number"
10 },
11 "tax": {
12 "type": "number",
13 "value": {
14 "$evaluation": {
15 "*": [
16 { "$ref": "#/properties/price" },
17 0.1
18 ]
19 }
20 }
21 },
22 "total": {
23 "type": "number",
24 "value": {
25 "$evaluation": {
26 "+": [
27 { "$ref": "#/properties/price" },
28 { "$ref": "#/properties/tax" }
29 ]
30 }
31 }
32 }
33 }
34 });
35
36 let schema_str = serde_json::to_string(&schema).unwrap();
37
38 println!("=== Example 1: With Caching (Default) ===");
39 {
40 let data = json!({ "price": 100 });
41 let data_str = serde_json::to_string(&data).unwrap();
42
43 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
44
45 println!("Cache enabled: {}", eval.is_cache_enabled());
46 println!("Initial cache size: {}", eval.cache_len());
47
48 eval.evaluate(&data_str, None, None, None).unwrap();
49
50 println!("After evaluation cache size: {}", eval.cache_len());
51 let stats = eval.cache_stats();
52 println!("Cache stats: {}", stats);
53 }
54
55 println!("\n=== Example 2: Without Caching (Web API Mode) ===");
56 {
57 let data = json!({ "price": 200 });
58 let data_str = serde_json::to_string(&data).unwrap();
59
60 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
61
62 // Disable caching for single-use web API scenario
63 eval.disable_cache();
64
65 println!("Cache enabled: {}", eval.is_cache_enabled());
66 println!("Initial cache size: {}", eval.cache_len());
67
68 eval.evaluate(&data_str, None, None, None).unwrap();
69
70 println!("After evaluation cache size: {}", eval.cache_len());
71 let stats = eval.cache_stats();
72 println!("Cache stats: {}", stats);
73
74 println!("\n✅ No cache overhead - perfect for web APIs!");
75 }
76
77 println!("\n=== Example 3: Re-enabling Cache ===");
78 {
79 let data = json!({ "price": 300 });
80 let data_str = serde_json::to_string(&data).unwrap();
81
82 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
83
84 // Disable then re-enable
85 eval.disable_cache();
86 eval.enable_cache();
87
88 println!("Cache enabled: {}", eval.is_cache_enabled());
89 eval.evaluate(&data_str, None, None, None).unwrap();
90
91 println!("Cache size after evaluation: {}", eval.cache_len());
92 println!("\n✅ Cache can be toggled as needed!");
93 }
94}6fn main() {
7 println!("\n🚀 JSON Evaluation - SPAJ Toggle Example\n");
8
9 let schema_path = Path::new("samples/spaj.json");
10 let schema_str = fs::read_to_string(schema_path).expect("Failed to read schema");
11
12 // Initial data with minimal context required
13 let context_str = json!({
14 "agentProfile": { "sob": "AG" }
15 }).to_string();
16
17 let initial_data = json!({
18 "illustration": {
19 "basicinformation": {
20 "print_polflag": false
21 }
22 }
23 }).to_string();
24
25 // Initialize logic
26 let mut eval = JSONEval::new(&schema_str, Some(&context_str), Some(&initial_data))
27 .expect("Failed to create JSONEval");
28
29 // Helper to check visibility
30 let check_visibility = |eval: &mut JSONEval, expected_hidden: bool, step: &str| {
31 let result = eval.get_evaluated_schema(false);
32 let hidden = result.pointer("/illustration/properties/basicinformation/properties/print_poladdress/condition/hidden")
33 .and_then(|v| v.as_bool());
34
35 match hidden {
36 Some(val) => {
37 if val == expected_hidden {
38 println!("✅ {}: Hidden = {} (Expected: {})", step, val, expected_hidden);
39 } else {
40 println!("❌ {}: Hidden = {} (Expected: {})", step, val, expected_hidden);
41 }
42 },
43 None => println!("❌ {}: 'hidden' property not found", step),
44 }
45 };
46
47 // Step 1: Initial state (false)
48 println!("Step 1: Initial State (print_polflag: false)");
49 eval.evaluate(&initial_data, Some(&context_str), None, None).expect("Evaluation failed");
50 check_visibility(&mut eval, true, "Initial check");
51
52 // Step 2: Toggle to true
53 println!("\nStep 2: Toggle True (print_polflag: true)");
54 let data_true = json!({
55 "illustration": {
56 "basicinformation": {
57 "print_polflag": true
58 }
59 }
60 }).to_string();
61 eval.evaluate(&data_true, Some(&context_str), None, None).expect("Evaluation failed");
62 check_visibility(&mut eval, false, "Toggle ON check");
63
64 // Step 3: Toggle back to false
65 println!("\nStep 3: Toggle False (print_polflag: false)");
66 let data_false = json!({
67 "illustration": {
68 "basicinformation": {
69 "print_polflag": false
70 }
71 }
72 }).to_string();
73 eval.evaluate(&data_false, Some(&context_str), None, None).expect("Evaluation failed");
74
75 let hidden_path = "#/illustration/properties/basicinformation/properties/print_poladdress/condition/hidden";
76 if let Some(deps) = eval.dependencies.get(hidden_path) {
77 println!("Debug: Dependencies for hidden: {:?}", deps);
78 } else {
79 println!("Debug: No dependencies found for hidden path");
80 }
81
82 // Debug: Print current flag value
83 if let Some(val) = eval.get_evaluated_schema(false).pointer("/illustration/properties/basicinformation/properties/print_polflag/value") {
84 println!("Debug: print_polflag value is: {}", val);
85 }
86
87 check_visibility(&mut eval, true, "Toggle OFF check");
88}28fn main() {
29 let args: Vec<String> = std::env::args().collect();
30 let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic");
31
32 let mut scenario_filter: Option<String> = None;
33 let mut enable_comparison = false;
34 let mut show_timing = false;
35 let mut i = 1;
36
37 // Parse arguments
38 while i < args.len() {
39 let arg = &args[i];
40
41 if arg == "-h" || arg == "--help" {
42 print_help(program_name);
43 return;
44 } else if arg == "--compare" {
45 enable_comparison = true;
46 } else if arg == "--timing" {
47 show_timing = true;
48 } else if !arg.starts_with('-') {
49 scenario_filter = Some(arg.clone());
50 } else {
51 eprintln!("Error: unknown option '{}'", arg);
52 print_help(program_name);
53 return;
54 }
55
56 i += 1;
57 }
58
59 println!("\n🚀 JSON Evaluation - Basic Example (JSON Schema)\n");
60
61 if enable_comparison {
62 println!("🔍 Comparison: enabled");
63 }
64 if show_timing {
65 println!("⏱️ Internal timing: enabled");
66 }
67 if enable_comparison || show_timing {
68 println!();
69 }
70
71 let samples_dir = Path::new("samples");
72 let mut scenarios = common::discover_scenarios(samples_dir);
73
74 // Filter out MessagePack scenarios - only use JSON
75 scenarios.retain(|s| !s.is_msgpack);
76
77 // Filter scenarios if a filter is provided
78 if let Some(ref filter) = scenario_filter {
79 scenarios.retain(|s| s.name.contains(filter));
80 println!("📋 Filtering scenarios matching: '{}'\n", filter);
81 }
82
83 if scenarios.is_empty() {
84 if let Some(filter) = scenario_filter {
85 println!(
86 "ℹ️ No scenarios found matching '{}' in `{}`.",
87 filter,
88 samples_dir.display()
89 );
90 } else {
91 println!(
92 "ℹ️ No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
93 samples_dir.display()
94 );
95 }
96 return;
97 }
98
99 println!("📊 Found {} scenario(s)\n", scenarios.len());
100
101 let mut total_parse_time = std::time::Duration::ZERO;
102 let mut total_eval_time = std::time::Duration::ZERO;
103 let mut successful_scenarios = 0;
104 let mut comparison_failures = 0;
105
106 for scenario in &scenarios {
107 println!("==============================");
108 println!("Scenario: {}", scenario.name);
109 println!("Schema: {} ({})",
110 scenario.schema_path.display(),
111 if scenario.is_msgpack { "MessagePack" } else { "JSON" }
112 );
113 println!("Data: {}\n", scenario.data_path.display());
114
115 // Clear timing data from previous scenarios
116 if show_timing {
117 json_eval_rs::enable_timing();
118 json_eval_rs::clear_timing_data();
119 }
120
121 let data_str = fs::read_to_string(&scenario.data_path)
122 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
123
124 // Step 1: Parse schema (JSONEval::new)
125 let parse_start = Instant::now();
126
127 let schema_str = fs::read_to_string(&scenario.schema_path)
128 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
129
130 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str))
131 .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e));
132
133 let parse_time = parse_start.elapsed();
134 println!(" 📝 Parse (new): {:?}", parse_time);
135
136 // Step 2: Evaluate
137 let eval_start = Instant::now();
138
139 eval.evaluate(&data_str, Some("{}"), None, None)
140 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
141
142 // Step 3: Validate
143 let validation_start = Instant::now();
144 let validation_result = eval.validate(&data_str, None, None, None)
145 .unwrap_or_else(|e| panic!("validation failed: {}", e));
146 let validation_time = validation_start.elapsed();
147 println!(" 🛡️ Validate: {:?}", validation_time);
148
149 // Legacy behavior: get_evaluated_schema takes skip_layout: bool
150 // We pass false to ensure layout IS resolved
151 let evaluated_schema = eval.get_evaluated_schema(false);
152 let schema_value = eval.get_schema_value();
153 let eval_time = eval_start.elapsed();
154
155 println!(" ⚡ Eval: {:?}", eval_time);
156 println!(" ⏱️ Total: {:?}\n", parse_time + eval_time);
157
158 // Print detailed timing breakdown if --timing flag is set
159 if show_timing {
160 json_eval_rs::print_timing_summary();
161 }
162
163 total_parse_time += parse_time;
164 total_eval_time += eval_time;
165 successful_scenarios += 1;
166
167 // Save results
168 let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
169 let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
170 let value_path = samples_dir.join(format!("{}-schema-value.json", scenario.name));
171 let validation_path = samples_dir.join(format!("{}-validation.json", scenario.name));
172
173 fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
174 .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
175
176 let mut metadata_obj = Map::new();
177 metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
178 metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
179 metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
180
181 fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
182 .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
183
184 fs::write(&value_path, common::pretty_json(&schema_value))
185 .unwrap_or_else(|e| panic!("failed to write {}: {}", value_path.display(), e));
186
187 let validation_value = serde_json::to_value(&validation_result)
188 .unwrap_or_else(|e| panic!("failed to serialize validation result: {}", e));
189 fs::write(&validation_path, common::pretty_json(&validation_value))
190 .unwrap_or_else(|e| panic!("failed to write {}: {}", validation_path.display(), e));
191
192 println!("✅ Results saved:");
193 println!(" - {}", evaluated_path.display());
194 println!(" - {}", parsed_path.display());
195 println!(" - {}", value_path.display());
196 println!(" - {}\n", validation_path.display());
197
198 // Optional comparison
199 if enable_comparison {
200 if let Some(comp_path) = &scenario.comparison_path {
201 if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
202 comparison_failures += 1;
203 }
204 println!();
205 }
206 }
207 }
208
209 // Print summary
210 println!("{}", "=".repeat(50));
211 println!("📊 Summary");
212 println!("{}", "=".repeat(50));
213 println!("Total scenarios run: {}", successful_scenarios);
214 println!("Total parse time: {:?}", total_parse_time);
215 println!("Total eval time: {:?}", total_eval_time);
216 println!("Total time: {:?}", total_parse_time + total_eval_time);
217
218 if successful_scenarios > 1 {
219 println!("\nAverage per scenario:");
220 println!(" Parse: {:?}", total_parse_time / successful_scenarios as u32);
221 println!(" Eval: {:?}", total_eval_time / successful_scenarios as u32);
222 }
223
224 if enable_comparison {
225 println!("Comparison failures: {}", comparison_failures);
226 }
227
228 println!("\n✅ All scenarios completed!\n");
229}32fn main() {
33 let args: Vec<String> = std::env::args().collect();
34 let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("benchmark");
35
36 let mut iterations = 1usize;
37 let mut scenario_filter: Option<String> = None;
38 let mut show_cpu_info = false;
39 let mut use_parsed_schema = false;
40 let mut use_cache = false;
41 let mut concurrent_count: Option<usize> = None;
42 let mut enable_comparison = false;
43 let mut show_timing = false;
44 let mut i = 1;
45
46 // Parse arguments
47 while i < args.len() {
48 let arg = &args[i];
49
50 if arg == "-h" || arg == "--help" {
51 print_help(program_name);
52 return;
53 } else if arg == "--cpu-info" {
54 show_cpu_info = true;
55 } else if arg == "--parsed" {
56 use_parsed_schema = true;
57 } else if arg == "--cache" {
58 use_cache = true;
59 } else if arg == "--compare" {
60 enable_comparison = true;
61 } else if arg == "--timing" {
62 show_timing = true;
63 } else if arg == "--concurrent" {
64 if i + 1 >= args.len() {
65 eprintln!("Error: {} requires a value", arg);
66 print_help(program_name);
67 return;
68 }
69 i += 1;
70 match args[i].parse::<usize>() {
71 Ok(n) if n > 0 => concurrent_count = Some(n),
72 _ => {
73 eprintln!("Error: concurrent count must be a positive integer, got '{}'", args[i]);
74 return;
75 }
76 }
77 } else if arg == "-i" || arg == "--iterations" {
78 if i + 1 >= args.len() {
79 eprintln!("Error: {} requires a value", arg);
80 print_help(program_name);
81 return;
82 }
83 i += 1;
84 match args[i].parse::<usize>() {
85 Ok(n) if n > 0 => iterations = n,
86 _ => {
87 eprintln!("Error: iterations must be a positive integer, got '{}'", args[i]);
88 return;
89 }
90 }
91 } else if !arg.starts_with('-') {
92 scenario_filter = Some(arg.clone());
93 } else {
94 eprintln!("Error: unknown option '{}'", arg);
95 print_help(program_name);
96 return;
97 }
98
99 i += 1;
100 }
101
102 println!("\n🚀 JSON Evaluation - Benchmark\n");
103
104 // Show CPU info if requested or if running benchmarks
105 if show_cpu_info || iterations > 1 || concurrent_count.is_some() {
106 common::print_cpu_info();
107 }
108
109 if use_parsed_schema {
110 println!("📦 Mode: ParsedSchema (parse once, reuse for all iterations)\n");
111 }
112
113 if use_cache {
114 println!("♻️ Mode: Cache (reuse JSONEval instance across iterations)\n");
115 }
116
117 if let Some(count) = concurrent_count {
118 println!("🔀 Concurrent evaluations: {} threads\n", count);
119 } else if iterations > 1 {
120 println!("🔄 Iterations per scenario: {}\n", iterations);
121 }
122
123 if enable_comparison {
124 println!("🔍 Comparison: enabled");
125 }
126 if show_timing {
127 println!("⏱️ Internal timing: enabled");
128 }
129 if enable_comparison || show_timing {
130 println!();
131 }
132
133 let samples_dir = Path::new("samples");
134 let mut scenarios = common::discover_scenarios(samples_dir);
135
136 // Filter scenarios if a filter is provided
137 if let Some(ref filter) = scenario_filter {
138 scenarios.retain(|s| s.name.contains(filter));
139 println!("📋 Filtering scenarios matching: '{}'\n", filter);
140 }
141
142 if scenarios.is_empty() {
143 if let Some(filter) = scenario_filter {
144 println!(
145 "ℹ️ No scenarios found matching '{}' in `{}`.",
146 filter,
147 samples_dir.display()
148 );
149 } else {
150 println!(
151 "ℹ️ No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
152 samples_dir.display()
153 );
154 }
155 return;
156 }
157
158 println!("📊 Found {} scenario(s)\n", scenarios.len());
159
160 let mut total_parse_time = std::time::Duration::ZERO;
161 let mut total_eval_time = std::time::Duration::ZERO;
162 let mut successful_scenarios = 0;
163 let mut comparison_failures = 0;
164
165 for scenario in &scenarios {
166 println!("==============================");
167 println!("Scenario: {}", scenario.name);
168 println!("Schema: {} ({})",
169 scenario.schema_path.display(),
170 if scenario.is_msgpack { "MessagePack" } else { "JSON" }
171 );
172 println!("Data: {}\n", scenario.data_path.display());
173
174 // Clear timing data from previous scenarios
175 if show_timing {
176 json_eval_rs::enable_timing();
177 json_eval_rs::clear_timing_data();
178 }
179
180 let data_str = fs::read_to_string(&scenario.data_path)
181 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
182
183 println!("Running evaluation...\n");
184
185 let (parse_time, eval_time, evaluated_schema, eval, iteration_times) = if use_parsed_schema {
186 // ParsedSchema mode: parse once, reuse for all iterations/threads
187 let start_time = Instant::now();
188
189 let parsed_schema = if scenario.is_msgpack {
190 let schema_msgpack = fs::read(&scenario.schema_path)
191 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
192 println!(" 📦 MessagePack schema size: {} bytes", schema_msgpack.len());
193 Arc::new(ParsedSchema::parse_msgpack(&schema_msgpack)
194 .unwrap_or_else(|e| panic!("failed to parse MessagePack schema: {}", e)))
195 } else {
196 let schema_str = fs::read_to_string(&scenario.schema_path)
197 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
198 Arc::new(ParsedSchema::parse(&schema_str)
199 .unwrap_or_else(|e| panic!("failed to parse schema: {}", e)))
200 };
201
202 let parse_time = start_time.elapsed();
203 println!(" Schema parsing & compilation: {:?}", parse_time);
204
205 // Concurrent mode with ParsedSchema
206 if let Some(thread_count) = concurrent_count {
207 use std::thread;
208
209 let eval_start = Instant::now();
210 let mut handles = vec![];
211
212 for thread_id in 0..thread_count {
213 let parsed_clone = parsed_schema.clone();
214 let data_str_clone = data_str.clone();
215 let iter_count = iterations;
216 let thread_use_cache = use_cache;
217
218 let handle = thread::spawn(move || {
219 let mut thread_times = Vec::with_capacity(iter_count);
220 let mut last_schema = Value::Null;
221
222 let mut eval_instance = JSONEval::with_parsed_schema(
223 parsed_clone.clone(),
224 Some("{}"),
225 Some(&data_str_clone)
226 ).unwrap();
227
228 for iter in 0..iter_count {
229 let iter_start = Instant::now();
230
231 if !thread_use_cache && iter > 0 {
232 eval_instance = JSONEval::with_parsed_schema(
233 parsed_clone.clone(),
234 Some("{}"),
235 Some(&data_str_clone)
236 ).unwrap();
237 }
238
239 eval_instance.evaluate(&data_str_clone, Some("{}"), None, None).unwrap();
240 last_schema = eval_instance.get_evaluated_schema(false);
241 thread_times.push(iter_start.elapsed());
242 }
243
244 (thread_times, last_schema, thread_id)
245 });
246 handles.push(handle);
247 }
248
249 let mut all_iteration_times = Vec::new();
250 let mut evaluated_schema = Value::Null;
251
252 for handle in handles {
253 let (thread_times, thread_schema, thread_id) = handle.join().unwrap();
254 println!(" Thread {} completed {} iterations", thread_id, thread_times.len());
255 all_iteration_times.extend(thread_times);
256 evaluated_schema = thread_schema; // Use last thread's result
257 }
258
259 let eval_time = eval_start.elapsed();
260
261 // Create a temp eval for metadata export
262 let temp_eval = JSONEval::with_parsed_schema(
263 parsed_schema.clone(),
264 Some("{}"),
265 Some(&data_str)
266 ).unwrap();
267
268 (parse_time, eval_time, evaluated_schema, temp_eval, all_iteration_times)
269 } else {
270 // Sequential iterations with ParsedSchema
271 let eval_start = Instant::now();
272 let mut evaluated_schema = Value::Null;
273 let mut iteration_times = Vec::with_capacity(iterations);
274 let mut eval_instance = JSONEval::with_parsed_schema(
275 parsed_schema.clone(),
276 Some("{}"),
277 Some(&data_str)
278 ).unwrap();
279
280 for iter in 0..iterations {
281 let iter_start = Instant::now();
282
283 if !use_cache && iter > 0 {
284 eval_instance = JSONEval::with_parsed_schema(
285 parsed_schema.clone(),
286 Some("{}"),
287 Some(&data_str)
288 ).unwrap();
289 }
290
291 eval_instance.evaluate(&data_str, Some("{}"), None, None)
292 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
293 evaluated_schema = eval_instance.get_evaluated_schema(false);
294 iteration_times.push(iter_start.elapsed());
295
296 if iterations > 1 && (iter + 1) % 10 == 0 {
297 print!(".");
298 if (iter + 1) % 50 == 0 {
299 println!(" {}/{}", iter + 1, iterations);
300 }
301 }
302 }
303
304 if iterations > 1 && iterations % 50 != 0 {
305 println!(" {}/{}", iterations, iterations);
306 }
307
308 let eval_time = eval_start.elapsed();
309 (parse_time, eval_time, evaluated_schema, eval_instance, iteration_times)
310 }
311 } else {
312 // Traditional mode: parse and create JSONEval each time
313 let schema_msgpack = if scenario.is_msgpack {
314 let bytes = fs::read(&scenario.schema_path)
315 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
316 println!(" 📦 MessagePack schema size: {} bytes", bytes.len());
317 Some(bytes)
318 } else {
319 None
320 };
321
322 let schema_str = if !scenario.is_msgpack {
323 Some(fs::read_to_string(&scenario.schema_path)
324 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e)))
325 } else {
326 None
327 };
328
329 let start_time = Instant::now();
330 let mut eval = if scenario.is_msgpack {
331 JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
332 .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
333 } else {
334 JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
335 .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
336 };
337 let parse_time = start_time.elapsed();
338 println!(" Schema parsing & compilation: {:?}", parse_time);
339
340 let eval_start = Instant::now();
341 let mut evaluated_schema = Value::Null;
342 let mut iteration_times = Vec::with_capacity(iterations);
343
344 for iter in 0..iterations {
345 let iter_start = Instant::now();
346
347 if !use_cache && iter > 0 {
348 eval = if scenario.is_msgpack {
349 JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
350 .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
351 } else {
352 JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
353 .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
354 };
355 }
356
357 eval.evaluate(&data_str, Some("{}"), None, None)
358 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
359 evaluated_schema = eval.get_evaluated_schema(false);
360 iteration_times.push(iter_start.elapsed());
361
362 if iterations > 1 && (iter + 1) % 10 == 0 {
363 print!(".");
364 if (iter + 1) % 50 == 0 {
365 println!(" {}/{}", iter + 1, iterations);
366 }
367 }
368 }
369
370 if iterations > 1 && iterations % 50 != 0 {
371 println!(" {}/{}", iterations, iterations);
372 }
373
374 let eval_time = eval_start.elapsed();
375 (parse_time, eval_time, evaluated_schema, eval, iteration_times)
376 };
377
378 // Calculate statistics
379 let total_iterations = iteration_times.len();
380 if total_iterations == 1 {
381 println!(" Evaluation: {:?}", eval_time);
382 } else {
383 let avg_time = eval_time / total_iterations as u32;
384 let min_time = iteration_times.iter().min().unwrap();
385 let max_time = iteration_times.iter().max().unwrap();
386
387 println!(" Total evaluation time: {:?}", eval_time);
388 println!(" Total iterations: {}", total_iterations);
389 println!(" Average per iteration: {:?}", avg_time);
390 println!(" Min: {:?} | Max: {:?}", min_time, max_time);
391
392 // Show cache statistics
393 let cache_stats = eval.cache_stats();
394 println!(" Cache: {} entries, {} hits, {} misses ({:.1}% hit rate)",
395 cache_stats.entries,
396 cache_stats.hits,
397 cache_stats.misses,
398 cache_stats.hit_rate * 100.0
399 );
400 }
401
402 let total_time = parse_time + eval_time;
403 println!("⏱️ Execution time: {:?}\n", total_time);
404
405 // Print detailed timing breakdown if --timing flag is set
406 if show_timing {
407 json_eval_rs::print_timing_summary();
408 }
409
410 // Track statistics
411 total_parse_time += parse_time;
412 total_eval_time += eval_time;
413 successful_scenarios += 1;
414
415 let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
416 let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
417
418 fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
419 .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
420
421 let mut metadata_obj = Map::new();
422 metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
423 metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
424
425 fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
426 .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
427
428 println!("✅ Results saved:");
429 println!(" - {}", evaluated_path.display());
430 println!(" - {}\n", parsed_path.display());
431
432 // Optional comparison
433 if enable_comparison {
434 if let Some(comp_path) = &scenario.comparison_path {
435 if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
436 comparison_failures += 1;
437 }
438 println!();
439 }
440 }
441 }
442
443 // Print summary statistics
444 if successful_scenarios > 0 {
445 println!("\n{}", "=".repeat(50));
446 println!("📊 Summary Statistics");
447 println!("{}", "=".repeat(50));
448 println!("Total scenarios run: {}", successful_scenarios);
449 println!("Total parsing time: {:?}", total_parse_time);
450 println!("Total evaluation time: {:?}", total_eval_time);
451 println!("Total time: {:?}", total_parse_time + total_eval_time);
452
453 if successful_scenarios > 1 {
454 println!("\nAverage per scenario:");
455 println!(" Parsing: {:?}", total_parse_time / successful_scenarios as u32);
456 println!(" Evaluation: {:?}", total_eval_time / successful_scenarios as u32);
457 }
458
459 if enable_comparison {
460 println!("\nComparison failures: {}", comparison_failures);
461 }
462
463 println!("\n✅ All scenarios completed successfully!\n");
464 }
465}Sourcepub fn new_from_msgpack(
schema_msgpack: &[u8],
context: Option<&str>,
data: Option<&str>,
) -> Result<Self, String>
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 bytescontext- Optional JSON context stringdata- Optional JSON data string
§Returns
A Result containing the JSONEval instance or an error
Examples found in repository?
28fn main() {
29 let args: Vec<String> = std::env::args().collect();
30 let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic_msgpack");
31
32 let mut scenario_filter: Option<String> = None;
33 let mut enable_comparison = false;
34 let mut show_timing = false;
35 let mut i = 1;
36
37 // Parse arguments
38 while i < args.len() {
39 let arg = &args[i];
40
41 if arg == "-h" || arg == "--help" {
42 print_help(program_name);
43 return;
44 } else if arg == "--compare" {
45 enable_comparison = true;
46 } else if arg == "--timing" {
47 show_timing = true;
48 } else if !arg.starts_with('-') {
49 scenario_filter = Some(arg.clone());
50 } else {
51 eprintln!("Error: unknown option '{}'", arg);
52 print_help(program_name);
53 return;
54 }
55
56 i += 1;
57 }
58
59 println!("\n🚀 JSON Evaluation - Basic Example (MessagePack Schema)\n");
60
61 if enable_comparison {
62 println!("🔍 Comparison: enabled");
63 }
64 if show_timing {
65 println!("⏱️ Internal timing: enabled");
66 }
67 if enable_comparison || show_timing {
68 println!();
69 }
70
71 let samples_dir = Path::new("samples");
72 let mut scenarios = common::discover_scenarios(samples_dir);
73
74 // Filter to only MessagePack scenarios
75 scenarios.retain(|s| s.is_msgpack);
76
77 // Filter scenarios if a filter is provided
78 if let Some(ref filter) = scenario_filter {
79 scenarios.retain(|s| s.name.contains(filter));
80 println!("📋 Filtering scenarios matching: '{}'\n", filter);
81 }
82
83 if scenarios.is_empty() {
84 if let Some(filter) = scenario_filter {
85 println!(
86 "ℹ️ No MessagePack scenarios found matching '{}' in `{}`.",
87 filter,
88 samples_dir.display()
89 );
90 } else {
91 println!(
92 "ℹ️ No MessagePack scenarios discovered in `{}`. Add files like `name.bform` and `name-data.json`.",
93 samples_dir.display()
94 );
95 }
96 return;
97 }
98
99 println!("📊 Found {} MessagePack scenario(s)\n", scenarios.len());
100
101 let mut total_parse_time = std::time::Duration::ZERO;
102 let mut total_eval_time = std::time::Duration::ZERO;
103 let mut successful_scenarios = 0;
104 let mut comparison_failures = 0;
105
106 for scenario in &scenarios {
107 println!("==============================");
108 println!("Scenario: {}", scenario.name);
109 println!("Schema: {} (MessagePack)", scenario.schema_path.display());
110 println!("Data: {}\n", scenario.data_path.display());
111
112 // Clear timing data from previous scenarios
113 if show_timing {
114 json_eval_rs::enable_timing();
115 json_eval_rs::clear_timing_data();
116 }
117
118 let data_str = fs::read_to_string(&scenario.data_path)
119 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
120
121 // Step 1: Parse schema (new_from_msgpack)
122 let parse_start = Instant::now();
123
124 let schema_msgpack = fs::read(&scenario.schema_path)
125 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
126
127 println!(" 📦 MessagePack schema size: {} bytes", schema_msgpack.len());
128
129 let mut eval = JSONEval::new_from_msgpack(&schema_msgpack, None, Some(&data_str))
130 .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e));
131
132 let parse_time = parse_start.elapsed();
133 println!(" 📝 Parse (msgpack): {:?}", parse_time);
134
135 // Step 2: Evaluate
136 let eval_start = Instant::now();
137
138 eval.evaluate(&data_str, Some("{}"), None, None)
139 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
140
141 let evaluated_schema = eval.get_evaluated_schema(false);
142 let eval_time = eval_start.elapsed();
143
144 println!(" ⚡ Eval: {:?}", eval_time);
145 println!(" ⏱️ Total: {:?}\n", parse_time + eval_time);
146
147 // Print detailed timing breakdown if --timing flag is set
148 if show_timing {
149 json_eval_rs::print_timing_summary();
150 }
151
152 total_parse_time += parse_time;
153 total_eval_time += eval_time;
154 successful_scenarios += 1;
155
156 // Save results
157 let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
158 let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
159
160 fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
161 .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
162
163 let mut metadata_obj = Map::new();
164 metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
165 metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
166 metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
167
168 fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
169 .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
170
171 println!("✅ Results saved:");
172 println!(" - {}", evaluated_path.display());
173 println!(" - {}\n", parsed_path.display());
174
175 // Optional comparison
176 if enable_comparison {
177 if let Some(comp_path) = &scenario.comparison_path {
178 if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
179 comparison_failures += 1;
180 }
181 println!();
182 }
183 }
184 }
185
186 // Print summary
187 println!("{}", "=".repeat(50));
188 println!("📊 Summary");
189 println!("{}", "=".repeat(50));
190 println!("Total scenarios run: {}", successful_scenarios);
191 println!("Total parse time: {:?}", total_parse_time);
192 println!("Total eval time: {:?}", total_eval_time);
193 println!("Total time: {:?}", total_parse_time + total_eval_time);
194
195 if successful_scenarios > 1 {
196 println!("\nAverage per scenario:");
197 println!(" Parse: {:?}", total_parse_time / successful_scenarios as u32);
198 println!(" Eval: {:?}", total_eval_time / successful_scenarios as u32);
199 }
200
201 if enable_comparison {
202 println!("Comparison failures: {}", comparison_failures);
203 }
204
205 println!("\n✅ All scenarios completed!\n");
206}More examples
32fn main() {
33 let args: Vec<String> = std::env::args().collect();
34 let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("benchmark");
35
36 let mut iterations = 1usize;
37 let mut scenario_filter: Option<String> = None;
38 let mut show_cpu_info = false;
39 let mut use_parsed_schema = false;
40 let mut use_cache = false;
41 let mut concurrent_count: Option<usize> = None;
42 let mut enable_comparison = false;
43 let mut show_timing = false;
44 let mut i = 1;
45
46 // Parse arguments
47 while i < args.len() {
48 let arg = &args[i];
49
50 if arg == "-h" || arg == "--help" {
51 print_help(program_name);
52 return;
53 } else if arg == "--cpu-info" {
54 show_cpu_info = true;
55 } else if arg == "--parsed" {
56 use_parsed_schema = true;
57 } else if arg == "--cache" {
58 use_cache = true;
59 } else if arg == "--compare" {
60 enable_comparison = true;
61 } else if arg == "--timing" {
62 show_timing = true;
63 } else if arg == "--concurrent" {
64 if i + 1 >= args.len() {
65 eprintln!("Error: {} requires a value", arg);
66 print_help(program_name);
67 return;
68 }
69 i += 1;
70 match args[i].parse::<usize>() {
71 Ok(n) if n > 0 => concurrent_count = Some(n),
72 _ => {
73 eprintln!("Error: concurrent count must be a positive integer, got '{}'", args[i]);
74 return;
75 }
76 }
77 } else if arg == "-i" || arg == "--iterations" {
78 if i + 1 >= args.len() {
79 eprintln!("Error: {} requires a value", arg);
80 print_help(program_name);
81 return;
82 }
83 i += 1;
84 match args[i].parse::<usize>() {
85 Ok(n) if n > 0 => iterations = n,
86 _ => {
87 eprintln!("Error: iterations must be a positive integer, got '{}'", args[i]);
88 return;
89 }
90 }
91 } else if !arg.starts_with('-') {
92 scenario_filter = Some(arg.clone());
93 } else {
94 eprintln!("Error: unknown option '{}'", arg);
95 print_help(program_name);
96 return;
97 }
98
99 i += 1;
100 }
101
102 println!("\n🚀 JSON Evaluation - Benchmark\n");
103
104 // Show CPU info if requested or if running benchmarks
105 if show_cpu_info || iterations > 1 || concurrent_count.is_some() {
106 common::print_cpu_info();
107 }
108
109 if use_parsed_schema {
110 println!("📦 Mode: ParsedSchema (parse once, reuse for all iterations)\n");
111 }
112
113 if use_cache {
114 println!("♻️ Mode: Cache (reuse JSONEval instance across iterations)\n");
115 }
116
117 if let Some(count) = concurrent_count {
118 println!("🔀 Concurrent evaluations: {} threads\n", count);
119 } else if iterations > 1 {
120 println!("🔄 Iterations per scenario: {}\n", iterations);
121 }
122
123 if enable_comparison {
124 println!("🔍 Comparison: enabled");
125 }
126 if show_timing {
127 println!("⏱️ Internal timing: enabled");
128 }
129 if enable_comparison || show_timing {
130 println!();
131 }
132
133 let samples_dir = Path::new("samples");
134 let mut scenarios = common::discover_scenarios(samples_dir);
135
136 // Filter scenarios if a filter is provided
137 if let Some(ref filter) = scenario_filter {
138 scenarios.retain(|s| s.name.contains(filter));
139 println!("📋 Filtering scenarios matching: '{}'\n", filter);
140 }
141
142 if scenarios.is_empty() {
143 if let Some(filter) = scenario_filter {
144 println!(
145 "ℹ️ No scenarios found matching '{}' in `{}`.",
146 filter,
147 samples_dir.display()
148 );
149 } else {
150 println!(
151 "ℹ️ No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
152 samples_dir.display()
153 );
154 }
155 return;
156 }
157
158 println!("📊 Found {} scenario(s)\n", scenarios.len());
159
160 let mut total_parse_time = std::time::Duration::ZERO;
161 let mut total_eval_time = std::time::Duration::ZERO;
162 let mut successful_scenarios = 0;
163 let mut comparison_failures = 0;
164
165 for scenario in &scenarios {
166 println!("==============================");
167 println!("Scenario: {}", scenario.name);
168 println!("Schema: {} ({})",
169 scenario.schema_path.display(),
170 if scenario.is_msgpack { "MessagePack" } else { "JSON" }
171 );
172 println!("Data: {}\n", scenario.data_path.display());
173
174 // Clear timing data from previous scenarios
175 if show_timing {
176 json_eval_rs::enable_timing();
177 json_eval_rs::clear_timing_data();
178 }
179
180 let data_str = fs::read_to_string(&scenario.data_path)
181 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
182
183 println!("Running evaluation...\n");
184
185 let (parse_time, eval_time, evaluated_schema, eval, iteration_times) = if use_parsed_schema {
186 // ParsedSchema mode: parse once, reuse for all iterations/threads
187 let start_time = Instant::now();
188
189 let parsed_schema = if scenario.is_msgpack {
190 let schema_msgpack = fs::read(&scenario.schema_path)
191 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
192 println!(" 📦 MessagePack schema size: {} bytes", schema_msgpack.len());
193 Arc::new(ParsedSchema::parse_msgpack(&schema_msgpack)
194 .unwrap_or_else(|e| panic!("failed to parse MessagePack schema: {}", e)))
195 } else {
196 let schema_str = fs::read_to_string(&scenario.schema_path)
197 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
198 Arc::new(ParsedSchema::parse(&schema_str)
199 .unwrap_or_else(|e| panic!("failed to parse schema: {}", e)))
200 };
201
202 let parse_time = start_time.elapsed();
203 println!(" Schema parsing & compilation: {:?}", parse_time);
204
205 // Concurrent mode with ParsedSchema
206 if let Some(thread_count) = concurrent_count {
207 use std::thread;
208
209 let eval_start = Instant::now();
210 let mut handles = vec![];
211
212 for thread_id in 0..thread_count {
213 let parsed_clone = parsed_schema.clone();
214 let data_str_clone = data_str.clone();
215 let iter_count = iterations;
216 let thread_use_cache = use_cache;
217
218 let handle = thread::spawn(move || {
219 let mut thread_times = Vec::with_capacity(iter_count);
220 let mut last_schema = Value::Null;
221
222 let mut eval_instance = JSONEval::with_parsed_schema(
223 parsed_clone.clone(),
224 Some("{}"),
225 Some(&data_str_clone)
226 ).unwrap();
227
228 for iter in 0..iter_count {
229 let iter_start = Instant::now();
230
231 if !thread_use_cache && iter > 0 {
232 eval_instance = JSONEval::with_parsed_schema(
233 parsed_clone.clone(),
234 Some("{}"),
235 Some(&data_str_clone)
236 ).unwrap();
237 }
238
239 eval_instance.evaluate(&data_str_clone, Some("{}"), None, None).unwrap();
240 last_schema = eval_instance.get_evaluated_schema(false);
241 thread_times.push(iter_start.elapsed());
242 }
243
244 (thread_times, last_schema, thread_id)
245 });
246 handles.push(handle);
247 }
248
249 let mut all_iteration_times = Vec::new();
250 let mut evaluated_schema = Value::Null;
251
252 for handle in handles {
253 let (thread_times, thread_schema, thread_id) = handle.join().unwrap();
254 println!(" Thread {} completed {} iterations", thread_id, thread_times.len());
255 all_iteration_times.extend(thread_times);
256 evaluated_schema = thread_schema; // Use last thread's result
257 }
258
259 let eval_time = eval_start.elapsed();
260
261 // Create a temp eval for metadata export
262 let temp_eval = JSONEval::with_parsed_schema(
263 parsed_schema.clone(),
264 Some("{}"),
265 Some(&data_str)
266 ).unwrap();
267
268 (parse_time, eval_time, evaluated_schema, temp_eval, all_iteration_times)
269 } else {
270 // Sequential iterations with ParsedSchema
271 let eval_start = Instant::now();
272 let mut evaluated_schema = Value::Null;
273 let mut iteration_times = Vec::with_capacity(iterations);
274 let mut eval_instance = JSONEval::with_parsed_schema(
275 parsed_schema.clone(),
276 Some("{}"),
277 Some(&data_str)
278 ).unwrap();
279
280 for iter in 0..iterations {
281 let iter_start = Instant::now();
282
283 if !use_cache && iter > 0 {
284 eval_instance = JSONEval::with_parsed_schema(
285 parsed_schema.clone(),
286 Some("{}"),
287 Some(&data_str)
288 ).unwrap();
289 }
290
291 eval_instance.evaluate(&data_str, Some("{}"), None, None)
292 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
293 evaluated_schema = eval_instance.get_evaluated_schema(false);
294 iteration_times.push(iter_start.elapsed());
295
296 if iterations > 1 && (iter + 1) % 10 == 0 {
297 print!(".");
298 if (iter + 1) % 50 == 0 {
299 println!(" {}/{}", iter + 1, iterations);
300 }
301 }
302 }
303
304 if iterations > 1 && iterations % 50 != 0 {
305 println!(" {}/{}", iterations, iterations);
306 }
307
308 let eval_time = eval_start.elapsed();
309 (parse_time, eval_time, evaluated_schema, eval_instance, iteration_times)
310 }
311 } else {
312 // Traditional mode: parse and create JSONEval each time
313 let schema_msgpack = if scenario.is_msgpack {
314 let bytes = fs::read(&scenario.schema_path)
315 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
316 println!(" 📦 MessagePack schema size: {} bytes", bytes.len());
317 Some(bytes)
318 } else {
319 None
320 };
321
322 let schema_str = if !scenario.is_msgpack {
323 Some(fs::read_to_string(&scenario.schema_path)
324 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e)))
325 } else {
326 None
327 };
328
329 let start_time = Instant::now();
330 let mut eval = if scenario.is_msgpack {
331 JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
332 .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
333 } else {
334 JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
335 .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
336 };
337 let parse_time = start_time.elapsed();
338 println!(" Schema parsing & compilation: {:?}", parse_time);
339
340 let eval_start = Instant::now();
341 let mut evaluated_schema = Value::Null;
342 let mut iteration_times = Vec::with_capacity(iterations);
343
344 for iter in 0..iterations {
345 let iter_start = Instant::now();
346
347 if !use_cache && iter > 0 {
348 eval = if scenario.is_msgpack {
349 JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
350 .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
351 } else {
352 JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
353 .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
354 };
355 }
356
357 eval.evaluate(&data_str, Some("{}"), None, None)
358 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
359 evaluated_schema = eval.get_evaluated_schema(false);
360 iteration_times.push(iter_start.elapsed());
361
362 if iterations > 1 && (iter + 1) % 10 == 0 {
363 print!(".");
364 if (iter + 1) % 50 == 0 {
365 println!(" {}/{}", iter + 1, iterations);
366 }
367 }
368 }
369
370 if iterations > 1 && iterations % 50 != 0 {
371 println!(" {}/{}", iterations, iterations);
372 }
373
374 let eval_time = eval_start.elapsed();
375 (parse_time, eval_time, evaluated_schema, eval, iteration_times)
376 };
377
378 // Calculate statistics
379 let total_iterations = iteration_times.len();
380 if total_iterations == 1 {
381 println!(" Evaluation: {:?}", eval_time);
382 } else {
383 let avg_time = eval_time / total_iterations as u32;
384 let min_time = iteration_times.iter().min().unwrap();
385 let max_time = iteration_times.iter().max().unwrap();
386
387 println!(" Total evaluation time: {:?}", eval_time);
388 println!(" Total iterations: {}", total_iterations);
389 println!(" Average per iteration: {:?}", avg_time);
390 println!(" Min: {:?} | Max: {:?}", min_time, max_time);
391
392 // Show cache statistics
393 let cache_stats = eval.cache_stats();
394 println!(" Cache: {} entries, {} hits, {} misses ({:.1}% hit rate)",
395 cache_stats.entries,
396 cache_stats.hits,
397 cache_stats.misses,
398 cache_stats.hit_rate * 100.0
399 );
400 }
401
402 let total_time = parse_time + eval_time;
403 println!("⏱️ Execution time: {:?}\n", total_time);
404
405 // Print detailed timing breakdown if --timing flag is set
406 if show_timing {
407 json_eval_rs::print_timing_summary();
408 }
409
410 // Track statistics
411 total_parse_time += parse_time;
412 total_eval_time += eval_time;
413 successful_scenarios += 1;
414
415 let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
416 let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
417
418 fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
419 .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
420
421 let mut metadata_obj = Map::new();
422 metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
423 metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
424
425 fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
426 .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
427
428 println!("✅ Results saved:");
429 println!(" - {}", evaluated_path.display());
430 println!(" - {}\n", parsed_path.display());
431
432 // Optional comparison
433 if enable_comparison {
434 if let Some(comp_path) = &scenario.comparison_path {
435 if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
436 comparison_failures += 1;
437 }
438 println!();
439 }
440 }
441 }
442
443 // Print summary statistics
444 if successful_scenarios > 0 {
445 println!("\n{}", "=".repeat(50));
446 println!("📊 Summary Statistics");
447 println!("{}", "=".repeat(50));
448 println!("Total scenarios run: {}", successful_scenarios);
449 println!("Total parsing time: {:?}", total_parse_time);
450 println!("Total evaluation time: {:?}", total_eval_time);
451 println!("Total time: {:?}", total_parse_time + total_eval_time);
452
453 if successful_scenarios > 1 {
454 println!("\nAverage per scenario:");
455 println!(" Parsing: {:?}", total_parse_time / successful_scenarios as u32);
456 println!(" Evaluation: {:?}", total_eval_time / successful_scenarios as u32);
457 }
458
459 if enable_comparison {
460 println!("\nComparison failures: {}", comparison_failures);
461 }
462
463 println!("\n✅ All scenarios completed successfully!\n");
464 }
465}Sourcepub fn with_parsed_schema(
parsed: Arc<ParsedSchema>,
context: Option<&str>,
data: Option<&str>,
) -> Result<Self, String>
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 stringdata- 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?
38fn demo_local_cache() -> Result<(), Box<dyn std::error::Error>> {
39 println!("📦 Example 1: Local Cache Instance");
40 println!("Creating a dedicated cache for this application...\n");
41
42 let cache = ParsedSchemaCache::new();
43
44 // Simple schema
45 let schema_json = r#"{
46 "$params": {
47 "rate": { "type": "number" }
48 },
49 "result": {
50 "type": "number",
51 "title": "Calculated Result",
52 "$evaluation": {
53 "logic": { "*": [{"var": "$rate"}, 100] }
54 }
55 }
56 }"#;
57
58 // Parse and cache with a custom key
59 println!("📝 Parsing schema and caching with key 'calculation-v1'...");
60 let parsed = ParsedSchema::parse(schema_json)?;
61 cache.insert("calculation-v1".to_string(), Arc::new(parsed));
62
63 println!("✅ Schema cached successfully");
64 println!(" Cache size: {} entries", cache.len());
65 println!(" Keys: {:?}\n", cache.keys());
66
67 // Retrieve and use cached schema
68 println!("🔍 Retrieving cached schema...");
69 if let Some(cached_schema) = cache.get("calculation-v1") {
70 println!("✅ Retrieved from cache");
71
72 // Create JSONEval from cached ParsedSchema
73 let mut eval = JSONEval::with_parsed_schema(cached_schema, Some(r#"{"rate": 1.5}"#), None)?;
74 eval.evaluate("{}", None, None, None)?;
75
76 let evaluated = eval.get_evaluated_schema(false);
77 let result = evaluated.pointer("/result")
78 .and_then(|v| v.as_f64())
79 .unwrap_or(0.0);
80 println!(" Evaluation result: {}\n", result);
81 }
82
83 // Check cache stats
84 let stats = cache.stats();
85 println!("📊 Cache Statistics: {}", stats);
86
87 // Remove entry
88 println!("\n🗑️ Removing 'calculation-v1' from cache...");
89 cache.remove("calculation-v1");
90 println!(" Cache size after removal: {}", cache.len());
91
92 Ok(())
93}
94
95fn demo_global_cache() -> Result<(), Box<dyn std::error::Error>> {
96 println!("🌍 Example 2: Global Cache Instance");
97 println!("Using the built-in PARSED_SCHEMA_CACHE...\n");
98
99 let schema_json = r#"{
100 "$params": {
101 "x": { "type": "number" },
102 "y": { "type": "number" }
103 },
104 "sum": {
105 "type": "number",
106 "$evaluation": { "+": [{"var": "$x"}, {"var": "$y"}] }
107 }
108 }"#;
109
110 // Use global cache
111 println!("📝 Caching schema globally with key 'math-operations'...");
112 let parsed = ParsedSchema::parse(schema_json)?;
113 PARSED_SCHEMA_CACHE.insert("math-operations".to_string(), Arc::new(parsed));
114
115 println!("✅ Schema cached globally");
116 println!(" Global cache size: {}\n", PARSED_SCHEMA_CACHE.len());
117
118 // Access from anywhere in the application
119 simulate_another_function()?;
120
121 // Clean up
122 println!("\n🧹 Clearing global cache...");
123 PARSED_SCHEMA_CACHE.clear();
124 println!(" Global cache size: {}", PARSED_SCHEMA_CACHE.len());
125
126 Ok(())
127}
128
129fn simulate_another_function() -> Result<(), Box<dyn std::error::Error>> {
130 println!("🔄 In another function, accessing global cache...");
131
132 if let Some(cached) = PARSED_SCHEMA_CACHE.get("math-operations") {
133 println!("✅ Retrieved schema from global cache");
134
135 let mut eval = JSONEval::with_parsed_schema(cached, Some(r#"{"x": 10, "y": 20}"#), None)?;
136 eval.evaluate("{}", None, None, None)?;
137
138 let evaluated = eval.get_evaluated_schema(false);
139 let sum = evaluated.pointer("/sum")
140 .and_then(|v| v.as_f64())
141 .unwrap_or(0.0);
142 println!(" Result: {}", sum);
143 }
144
145 Ok(())
146}
147
148fn demo_performance_comparison() -> Result<(), Box<dyn std::error::Error>> {
149 println!("⚡ Example 3: Performance Comparison");
150 println!("Comparing cached vs non-cached schema usage...\n");
151
152 let schema_json = r#"{
153 "$params": {
154 "value": { "type": "number" }
155 },
156 "doubled": {
157 "type": "number",
158 "$evaluation": { "*": [{"var": "$value"}, 2] }
159 },
160 "tripled": {
161 "type": "number",
162 "$evaluation": { "*": [{"var": "$value"}, 3] }
163 }
164 }"#;
165
166 let iterations = 100;
167
168 // WITHOUT CACHE: Parse schema every time
169 println!("🐌 Without cache (parse + evaluate each time):");
170 let start = Instant::now();
171 for i in 0..iterations {
172 let context = format!(r#"{{"value": {}}}"#, i);
173 let mut eval = JSONEval::new(schema_json, Some(&context), None)?;
174 eval.evaluate("{}", None, None, None)?;
175 }
176 let without_cache = start.elapsed();
177 println!(" Time: {:?}", without_cache);
178 println!(" Avg per iteration: {:?}\n", without_cache / iterations);
179
180 // WITH CACHE: Parse once, evaluate many times
181 println!("🚀 With cache (parse once, reuse for all evaluations):");
182 let cache = ParsedSchemaCache::new();
183
184 // Parse once
185 let parse_start = Instant::now();
186 let parsed = ParsedSchema::parse(schema_json)?;
187 cache.insert("perf-test".to_string(), Arc::new(parsed));
188 let parse_time = parse_start.elapsed();
189
190 // Evaluate many times
191 let eval_start = Instant::now();
192 for i in 0..iterations {
193 if let Some(cached) = cache.get("perf-test") {
194 let context = format!(r#"{{"value": {}}}"#, i);
195 let mut eval = JSONEval::with_parsed_schema(cached.clone(), Some(&context), None)?;
196 eval.evaluate("{}", None, None, None)?;
197 }
198 }
199 let eval_time = eval_start.elapsed();
200 let with_cache = parse_time + eval_time;
201
202 println!(" Parse time: {:?}", parse_time);
203 println!(" Eval time: {:?}", eval_time);
204 println!(" Total time: {:?}", with_cache);
205 println!(" Avg per iteration: {:?}\n", eval_time / iterations);
206
207 let speedup = without_cache.as_secs_f64() / with_cache.as_secs_f64();
208 println!("📈 Speedup: {:.2}x faster", speedup);
209
210 Ok(())
211}More examples
30fn main() {
31 let args: Vec<String> = std::env::args().collect();
32 let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic_parsed");
33
34 let mut scenario_filter: Option<String> = None;
35 let mut enable_comparison = false;
36 let mut show_timing = false;
37 let mut i = 1;
38
39 // Parse arguments
40 while i < args.len() {
41 let arg = &args[i];
42
43 if arg == "-h" || arg == "--help" {
44 print_help(program_name);
45 return;
46 } else if arg == "--compare" {
47 enable_comparison = true;
48 } else if arg == "--timing" {
49 show_timing = true;
50 } else if !arg.starts_with('-') {
51 scenario_filter = Some(arg.clone());
52 } else {
53 eprintln!("Error: unknown option '{}'", arg);
54 print_help(program_name);
55 return;
56 }
57
58 i += 1;
59 }
60
61 println!("\n🚀 JSON Evaluation - Basic Example (ParsedSchema)\n");
62 println!("📦 Using Arc<ParsedSchema> for efficient caching\n");
63
64 if enable_comparison {
65 println!("🔍 Comparison: enabled");
66 }
67 if show_timing {
68 println!("⏱️ Internal timing: enabled");
69 }
70 if enable_comparison || show_timing {
71 println!();
72 }
73
74 let samples_dir = Path::new("samples");
75 let mut scenarios = common::discover_scenarios(samples_dir);
76
77 // Filter scenarios if a filter is provided
78 if let Some(ref filter) = scenario_filter {
79 scenarios.retain(|s| s.name.contains(filter));
80 println!("📋 Filtering scenarios matching: '{}'\n", filter);
81 }
82
83 if scenarios.is_empty() {
84 if let Some(filter) = scenario_filter {
85 println!(
86 "ℹ️ No scenarios found matching '{}' in `{}`.",
87 filter,
88 samples_dir.display()
89 );
90 } else {
91 println!(
92 "ℹ️ No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
93 samples_dir.display()
94 );
95 }
96 return;
97 }
98
99 println!("📊 Found {} scenario(s)\n", scenarios.len());
100
101 let mut total_parse_time = std::time::Duration::ZERO;
102 let mut total_eval_time = std::time::Duration::ZERO;
103 let mut successful_scenarios = 0;
104 let mut comparison_failures = 0;
105
106 for scenario in &scenarios {
107 println!("==============================");
108 println!("Scenario: {}", scenario.name);
109 println!("Schema: {} ({})",
110 scenario.schema_path.display(),
111 if scenario.is_msgpack { "MessagePack" } else { "JSON" }
112 );
113 println!("Data: {}\n", scenario.data_path.display());
114
115 // Clear timing data from previous scenarios
116 if show_timing {
117 json_eval_rs::enable_timing();
118 json_eval_rs::clear_timing_data();
119 }
120
121 let data_str = fs::read_to_string(&scenario.data_path)
122 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
123
124 // Step 1: Parse schema once
125 let parse_start = Instant::now();
126 let parsed_schema = if scenario.is_msgpack {
127 let schema_msgpack = fs::read(&scenario.schema_path)
128 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
129 println!(" 📦 MessagePack schema size: {} bytes", schema_msgpack.len());
130 Arc::new(ParsedSchema::parse_msgpack(&schema_msgpack)
131 .unwrap_or_else(|e| panic!("failed to parse MessagePack schema: {}", e)))
132 } else {
133 let schema_str = fs::read_to_string(&scenario.schema_path)
134 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
135 Arc::new(ParsedSchema::parse(&schema_str)
136 .unwrap_or_else(|e| panic!("failed to parse schema: {}", e)))
137 };
138 let parse_time = parse_start.elapsed();
139 println!(" 📝 Schema parsing: {:?}", parse_time);
140
141 // Step 2: Create JSONEval from ParsedSchema (reuses compiled logic)
142 let eval_start = Instant::now();
143 let mut eval = JSONEval::with_parsed_schema(
144 parsed_schema.clone(), // Arc::clone is cheap!
145 Some("{}"),
146 Some(&data_str)
147 ).unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e));
148
149 eval.evaluate(&data_str, Some("{}"), None, None)
150 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
151
152 let evaluated_schema = eval.get_evaluated_schema(false);
153 let eval_time = eval_start.elapsed();
154
155 println!(" ⚡ Eval: {:?}", eval_time);
156 println!(" ⏱️ Total: {:?}\n", parse_time + eval_time);
157
158 // Print detailed timing breakdown if --timing flag is set
159 if show_timing {
160 json_eval_rs::print_timing_summary();
161 }
162
163 total_parse_time += parse_time;
164 total_eval_time += eval_time;
165 successful_scenarios += 1;
166
167 // Save results
168 let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
169 let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
170
171 fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
172 .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
173
174 let mut metadata_obj = Map::new();
175 metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
176 metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
177 metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
178
179 fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
180 .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
181
182 println!("✅ Results saved:");
183 println!(" - {}", evaluated_path.display());
184 println!(" - {}\n", parsed_path.display());
185
186 // Optional comparison
187 if enable_comparison {
188 if let Some(comp_path) = &scenario.comparison_path {
189 if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
190 comparison_failures += 1;
191 }
192 println!();
193 }
194 }
195 }
196
197 // Print summary
198 println!("{}", "=".repeat(50));
199 println!("📊 Summary");
200 println!("{}", "=".repeat(50));
201 println!("Total scenarios run: {}", successful_scenarios);
202 println!("Total parsing time: {:?}", total_parse_time);
203 println!("Total evaluation time: {:?}", total_eval_time);
204 println!("Total time: {:?}", total_parse_time + total_eval_time);
205
206 if successful_scenarios > 1 {
207 println!("\nAverage per scenario:");
208 println!(" Parsing: {:?}", total_parse_time / successful_scenarios as u32);
209 println!(" Evaluation: {:?}", total_eval_time / successful_scenarios as u32);
210 }
211
212 if enable_comparison {
213 println!("\nComparison failures: {}", comparison_failures);
214 }
215
216 println!("\n✅ All scenarios completed!\n");
217}32fn main() {
33 let args: Vec<String> = std::env::args().collect();
34 let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("benchmark");
35
36 let mut iterations = 1usize;
37 let mut scenario_filter: Option<String> = None;
38 let mut show_cpu_info = false;
39 let mut use_parsed_schema = false;
40 let mut use_cache = false;
41 let mut concurrent_count: Option<usize> = None;
42 let mut enable_comparison = false;
43 let mut show_timing = false;
44 let mut i = 1;
45
46 // Parse arguments
47 while i < args.len() {
48 let arg = &args[i];
49
50 if arg == "-h" || arg == "--help" {
51 print_help(program_name);
52 return;
53 } else if arg == "--cpu-info" {
54 show_cpu_info = true;
55 } else if arg == "--parsed" {
56 use_parsed_schema = true;
57 } else if arg == "--cache" {
58 use_cache = true;
59 } else if arg == "--compare" {
60 enable_comparison = true;
61 } else if arg == "--timing" {
62 show_timing = true;
63 } else if arg == "--concurrent" {
64 if i + 1 >= args.len() {
65 eprintln!("Error: {} requires a value", arg);
66 print_help(program_name);
67 return;
68 }
69 i += 1;
70 match args[i].parse::<usize>() {
71 Ok(n) if n > 0 => concurrent_count = Some(n),
72 _ => {
73 eprintln!("Error: concurrent count must be a positive integer, got '{}'", args[i]);
74 return;
75 }
76 }
77 } else if arg == "-i" || arg == "--iterations" {
78 if i + 1 >= args.len() {
79 eprintln!("Error: {} requires a value", arg);
80 print_help(program_name);
81 return;
82 }
83 i += 1;
84 match args[i].parse::<usize>() {
85 Ok(n) if n > 0 => iterations = n,
86 _ => {
87 eprintln!("Error: iterations must be a positive integer, got '{}'", args[i]);
88 return;
89 }
90 }
91 } else if !arg.starts_with('-') {
92 scenario_filter = Some(arg.clone());
93 } else {
94 eprintln!("Error: unknown option '{}'", arg);
95 print_help(program_name);
96 return;
97 }
98
99 i += 1;
100 }
101
102 println!("\n🚀 JSON Evaluation - Benchmark\n");
103
104 // Show CPU info if requested or if running benchmarks
105 if show_cpu_info || iterations > 1 || concurrent_count.is_some() {
106 common::print_cpu_info();
107 }
108
109 if use_parsed_schema {
110 println!("📦 Mode: ParsedSchema (parse once, reuse for all iterations)\n");
111 }
112
113 if use_cache {
114 println!("♻️ Mode: Cache (reuse JSONEval instance across iterations)\n");
115 }
116
117 if let Some(count) = concurrent_count {
118 println!("🔀 Concurrent evaluations: {} threads\n", count);
119 } else if iterations > 1 {
120 println!("🔄 Iterations per scenario: {}\n", iterations);
121 }
122
123 if enable_comparison {
124 println!("🔍 Comparison: enabled");
125 }
126 if show_timing {
127 println!("⏱️ Internal timing: enabled");
128 }
129 if enable_comparison || show_timing {
130 println!();
131 }
132
133 let samples_dir = Path::new("samples");
134 let mut scenarios = common::discover_scenarios(samples_dir);
135
136 // Filter scenarios if a filter is provided
137 if let Some(ref filter) = scenario_filter {
138 scenarios.retain(|s| s.name.contains(filter));
139 println!("📋 Filtering scenarios matching: '{}'\n", filter);
140 }
141
142 if scenarios.is_empty() {
143 if let Some(filter) = scenario_filter {
144 println!(
145 "ℹ️ No scenarios found matching '{}' in `{}`.",
146 filter,
147 samples_dir.display()
148 );
149 } else {
150 println!(
151 "ℹ️ No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
152 samples_dir.display()
153 );
154 }
155 return;
156 }
157
158 println!("📊 Found {} scenario(s)\n", scenarios.len());
159
160 let mut total_parse_time = std::time::Duration::ZERO;
161 let mut total_eval_time = std::time::Duration::ZERO;
162 let mut successful_scenarios = 0;
163 let mut comparison_failures = 0;
164
165 for scenario in &scenarios {
166 println!("==============================");
167 println!("Scenario: {}", scenario.name);
168 println!("Schema: {} ({})",
169 scenario.schema_path.display(),
170 if scenario.is_msgpack { "MessagePack" } else { "JSON" }
171 );
172 println!("Data: {}\n", scenario.data_path.display());
173
174 // Clear timing data from previous scenarios
175 if show_timing {
176 json_eval_rs::enable_timing();
177 json_eval_rs::clear_timing_data();
178 }
179
180 let data_str = fs::read_to_string(&scenario.data_path)
181 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
182
183 println!("Running evaluation...\n");
184
185 let (parse_time, eval_time, evaluated_schema, eval, iteration_times) = if use_parsed_schema {
186 // ParsedSchema mode: parse once, reuse for all iterations/threads
187 let start_time = Instant::now();
188
189 let parsed_schema = if scenario.is_msgpack {
190 let schema_msgpack = fs::read(&scenario.schema_path)
191 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
192 println!(" 📦 MessagePack schema size: {} bytes", schema_msgpack.len());
193 Arc::new(ParsedSchema::parse_msgpack(&schema_msgpack)
194 .unwrap_or_else(|e| panic!("failed to parse MessagePack schema: {}", e)))
195 } else {
196 let schema_str = fs::read_to_string(&scenario.schema_path)
197 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
198 Arc::new(ParsedSchema::parse(&schema_str)
199 .unwrap_or_else(|e| panic!("failed to parse schema: {}", e)))
200 };
201
202 let parse_time = start_time.elapsed();
203 println!(" Schema parsing & compilation: {:?}", parse_time);
204
205 // Concurrent mode with ParsedSchema
206 if let Some(thread_count) = concurrent_count {
207 use std::thread;
208
209 let eval_start = Instant::now();
210 let mut handles = vec![];
211
212 for thread_id in 0..thread_count {
213 let parsed_clone = parsed_schema.clone();
214 let data_str_clone = data_str.clone();
215 let iter_count = iterations;
216 let thread_use_cache = use_cache;
217
218 let handle = thread::spawn(move || {
219 let mut thread_times = Vec::with_capacity(iter_count);
220 let mut last_schema = Value::Null;
221
222 let mut eval_instance = JSONEval::with_parsed_schema(
223 parsed_clone.clone(),
224 Some("{}"),
225 Some(&data_str_clone)
226 ).unwrap();
227
228 for iter in 0..iter_count {
229 let iter_start = Instant::now();
230
231 if !thread_use_cache && iter > 0 {
232 eval_instance = JSONEval::with_parsed_schema(
233 parsed_clone.clone(),
234 Some("{}"),
235 Some(&data_str_clone)
236 ).unwrap();
237 }
238
239 eval_instance.evaluate(&data_str_clone, Some("{}"), None, None).unwrap();
240 last_schema = eval_instance.get_evaluated_schema(false);
241 thread_times.push(iter_start.elapsed());
242 }
243
244 (thread_times, last_schema, thread_id)
245 });
246 handles.push(handle);
247 }
248
249 let mut all_iteration_times = Vec::new();
250 let mut evaluated_schema = Value::Null;
251
252 for handle in handles {
253 let (thread_times, thread_schema, thread_id) = handle.join().unwrap();
254 println!(" Thread {} completed {} iterations", thread_id, thread_times.len());
255 all_iteration_times.extend(thread_times);
256 evaluated_schema = thread_schema; // Use last thread's result
257 }
258
259 let eval_time = eval_start.elapsed();
260
261 // Create a temp eval for metadata export
262 let temp_eval = JSONEval::with_parsed_schema(
263 parsed_schema.clone(),
264 Some("{}"),
265 Some(&data_str)
266 ).unwrap();
267
268 (parse_time, eval_time, evaluated_schema, temp_eval, all_iteration_times)
269 } else {
270 // Sequential iterations with ParsedSchema
271 let eval_start = Instant::now();
272 let mut evaluated_schema = Value::Null;
273 let mut iteration_times = Vec::with_capacity(iterations);
274 let mut eval_instance = JSONEval::with_parsed_schema(
275 parsed_schema.clone(),
276 Some("{}"),
277 Some(&data_str)
278 ).unwrap();
279
280 for iter in 0..iterations {
281 let iter_start = Instant::now();
282
283 if !use_cache && iter > 0 {
284 eval_instance = JSONEval::with_parsed_schema(
285 parsed_schema.clone(),
286 Some("{}"),
287 Some(&data_str)
288 ).unwrap();
289 }
290
291 eval_instance.evaluate(&data_str, Some("{}"), None, None)
292 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
293 evaluated_schema = eval_instance.get_evaluated_schema(false);
294 iteration_times.push(iter_start.elapsed());
295
296 if iterations > 1 && (iter + 1) % 10 == 0 {
297 print!(".");
298 if (iter + 1) % 50 == 0 {
299 println!(" {}/{}", iter + 1, iterations);
300 }
301 }
302 }
303
304 if iterations > 1 && iterations % 50 != 0 {
305 println!(" {}/{}", iterations, iterations);
306 }
307
308 let eval_time = eval_start.elapsed();
309 (parse_time, eval_time, evaluated_schema, eval_instance, iteration_times)
310 }
311 } else {
312 // Traditional mode: parse and create JSONEval each time
313 let schema_msgpack = if scenario.is_msgpack {
314 let bytes = fs::read(&scenario.schema_path)
315 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
316 println!(" 📦 MessagePack schema size: {} bytes", bytes.len());
317 Some(bytes)
318 } else {
319 None
320 };
321
322 let schema_str = if !scenario.is_msgpack {
323 Some(fs::read_to_string(&scenario.schema_path)
324 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e)))
325 } else {
326 None
327 };
328
329 let start_time = Instant::now();
330 let mut eval = if scenario.is_msgpack {
331 JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
332 .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
333 } else {
334 JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
335 .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
336 };
337 let parse_time = start_time.elapsed();
338 println!(" Schema parsing & compilation: {:?}", parse_time);
339
340 let eval_start = Instant::now();
341 let mut evaluated_schema = Value::Null;
342 let mut iteration_times = Vec::with_capacity(iterations);
343
344 for iter in 0..iterations {
345 let iter_start = Instant::now();
346
347 if !use_cache && iter > 0 {
348 eval = if scenario.is_msgpack {
349 JSONEval::new_from_msgpack(schema_msgpack.as_ref().unwrap(), None, Some(&data_str))
350 .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
351 } else {
352 JSONEval::new(schema_str.as_ref().unwrap(), None, Some(&data_str))
353 .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
354 };
355 }
356
357 eval.evaluate(&data_str, Some("{}"), None, None)
358 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
359 evaluated_schema = eval.get_evaluated_schema(false);
360 iteration_times.push(iter_start.elapsed());
361
362 if iterations > 1 && (iter + 1) % 10 == 0 {
363 print!(".");
364 if (iter + 1) % 50 == 0 {
365 println!(" {}/{}", iter + 1, iterations);
366 }
367 }
368 }
369
370 if iterations > 1 && iterations % 50 != 0 {
371 println!(" {}/{}", iterations, iterations);
372 }
373
374 let eval_time = eval_start.elapsed();
375 (parse_time, eval_time, evaluated_schema, eval, iteration_times)
376 };
377
378 // Calculate statistics
379 let total_iterations = iteration_times.len();
380 if total_iterations == 1 {
381 println!(" Evaluation: {:?}", eval_time);
382 } else {
383 let avg_time = eval_time / total_iterations as u32;
384 let min_time = iteration_times.iter().min().unwrap();
385 let max_time = iteration_times.iter().max().unwrap();
386
387 println!(" Total evaluation time: {:?}", eval_time);
388 println!(" Total iterations: {}", total_iterations);
389 println!(" Average per iteration: {:?}", avg_time);
390 println!(" Min: {:?} | Max: {:?}", min_time, max_time);
391
392 // Show cache statistics
393 let cache_stats = eval.cache_stats();
394 println!(" Cache: {} entries, {} hits, {} misses ({:.1}% hit rate)",
395 cache_stats.entries,
396 cache_stats.hits,
397 cache_stats.misses,
398 cache_stats.hit_rate * 100.0
399 );
400 }
401
402 let total_time = parse_time + eval_time;
403 println!("⏱️ Execution time: {:?}\n", total_time);
404
405 // Print detailed timing breakdown if --timing flag is set
406 if show_timing {
407 json_eval_rs::print_timing_summary();
408 }
409
410 // Track statistics
411 total_parse_time += parse_time;
412 total_eval_time += eval_time;
413 successful_scenarios += 1;
414
415 let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
416 let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
417
418 fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
419 .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
420
421 let mut metadata_obj = Map::new();
422 metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
423 metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
424
425 fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
426 .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
427
428 println!("✅ Results saved:");
429 println!(" - {}", evaluated_path.display());
430 println!(" - {}\n", parsed_path.display());
431
432 // Optional comparison
433 if enable_comparison {
434 if let Some(comp_path) = &scenario.comparison_path {
435 if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
436 comparison_failures += 1;
437 }
438 println!();
439 }
440 }
441 }
442
443 // Print summary statistics
444 if successful_scenarios > 0 {
445 println!("\n{}", "=".repeat(50));
446 println!("📊 Summary Statistics");
447 println!("{}", "=".repeat(50));
448 println!("Total scenarios run: {}", successful_scenarios);
449 println!("Total parsing time: {:?}", total_parse_time);
450 println!("Total evaluation time: {:?}", total_eval_time);
451 println!("Total time: {:?}", total_parse_time + total_eval_time);
452
453 if successful_scenarios > 1 {
454 println!("\nAverage per scenario:");
455 println!(" Parsing: {:?}", total_parse_time / successful_scenarios as u32);
456 println!(" Evaluation: {:?}", total_eval_time / successful_scenarios as u32);
457 }
458
459 if enable_comparison {
460 println!("\nComparison failures: {}", comparison_failures);
461 }
462
463 println!("\n✅ All scenarios completed successfully!\n");
464 }
465}pub fn reload_schema( &mut self, schema: &str, context: Option<&str>, data: Option<&str>, ) -> Result<(), String>
Sourcepub fn set_timezone_offset(&mut self, offset_minutes: Option<i32>)
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) PassNoneto 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);Sourcepub fn reload_schema_msgpack(
&mut self,
schema_msgpack: &[u8],
context: Option<&str>,
data: Option<&str>,
) -> Result<(), String>
pub fn reload_schema_msgpack( &mut self, schema_msgpack: &[u8], context: Option<&str>, data: Option<&str>, ) -> Result<(), String>
Sourcepub fn reload_schema_parsed(
&mut self,
parsed: Arc<ParsedSchema>,
context: Option<&str>,
data: Option<&str>,
) -> Result<(), String>
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 ParsedSchemacontext- Optional context data JSON stringdata- Optional initial data JSON string
§Returns
A Result indicating success or an error message
Sourcepub fn reload_schema_from_cache(
&mut self,
cache_key: &str,
context: Option<&str>,
data: Option<&str>,
) -> Result<(), String>
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 ParsedSchemaCachecontext- Optional context data JSON stringdata- Optional initial data JSON string
§Returns
A Result indicating success or an error message
Source§impl JSONEval
impl JSONEval
Sourcepub fn run_logic(
&mut self,
logic_id: CompiledLogicId,
data: Option<&Value>,
context: Option<&Value>,
) -> Result<Value, String>
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
Sourcepub fn compile_logic(&self, logic_str: &str) -> Result<CompiledLogicId, String>
pub fn compile_logic(&self, logic_str: &str) -> Result<CompiledLogicId, String>
Compile a logic expression from a JSON string and store it globally
Sourcepub fn compile_logic_value(
&self,
logic: &Value,
) -> Result<CompiledLogicId, String>
pub fn compile_logic_value( &self, logic: &Value, ) -> Result<CompiledLogicId, String>
Compile a logic expression from a Value and store it globally
Source§impl JSONEval
impl JSONEval
Sourcepub fn evaluate_subform(
&mut self,
subform_path: &str,
data: &str,
context: Option<&str>,
paths: Option<&[String]>,
token: Option<&CancellationToken>,
) -> Result<(), String>
pub fn evaluate_subform( &mut self, subform_path: &str, data: &str, context: Option<&str>, paths: Option<&[String]>, token: Option<&CancellationToken>, ) -> Result<(), String>
Evaluate a subform with data Evaluate a subform with data and optional selective paths
Sourcepub fn validate_subform(
&mut self,
subform_path: &str,
data: &str,
context: Option<&str>,
paths: Option<&[String]>,
token: Option<&CancellationToken>,
) -> Result<ValidationResult, String>
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
Sourcepub fn evaluate_dependents_subform(
&mut self,
subform_path: &str,
changed_paths: &[String],
data: Option<&str>,
context: Option<&str>,
re_evaluate: bool,
token: Option<&CancellationToken>,
canceled_paths: Option<&mut Vec<String>>,
) -> Result<Value, String>
pub fn evaluate_dependents_subform( &mut self, subform_path: &str, changed_paths: &[String], data: Option<&str>, context: Option<&str>, re_evaluate: bool, token: Option<&CancellationToken>, canceled_paths: Option<&mut Vec<String>>, ) -> Result<Value, String>
Evaluate dependents in subform when a field changes
Sourcepub fn resolve_layout_subform(
&mut self,
subform_path: &str,
evaluate: bool,
) -> Result<(), String>
pub fn resolve_layout_subform( &mut self, subform_path: &str, evaluate: bool, ) -> Result<(), String>
Resolve layout for subform
Sourcepub fn get_evaluated_schema_subform(
&mut self,
subform_path: &str,
resolve_layout: bool,
) -> Value
pub fn get_evaluated_schema_subform( &mut self, subform_path: &str, resolve_layout: bool, ) -> Value
Get evaluated schema from subform
Sourcepub fn get_schema_value_subform(&mut self, subform_path: &str) -> Value
pub fn get_schema_value_subform(&mut self, subform_path: &str) -> Value
Get schema value from subform in nested object format (all .value fields).
Sourcepub fn get_schema_value_array_subform(&self, subform_path: &str) -> Value
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.
Sourcepub fn get_schema_value_object_subform(&self, subform_path: &str) -> Value
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.
Sourcepub fn get_evaluated_schema_without_params_subform(
&mut self,
subform_path: &str,
resolve_layout: bool,
) -> Value
pub fn get_evaluated_schema_without_params_subform( &mut self, subform_path: &str, resolve_layout: bool, ) -> Value
Get evaluated schema without $params from subform
Sourcepub fn get_evaluated_schema_by_path_subform(
&mut self,
subform_path: &str,
schema_path: &str,
skip_layout: bool,
) -> Option<Value>
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
Sourcepub fn get_evaluated_schema_by_paths_subform(
&mut self,
subform_path: &str,
schema_paths: &[String],
skip_layout: bool,
format: Option<ReturnFormat>,
) -> Value
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
Sourcepub fn get_schema_by_path_subform(
&self,
subform_path: &str,
schema_path: &str,
) -> Option<Value>
pub fn get_schema_by_path_subform( &self, subform_path: &str, schema_path: &str, ) -> Option<Value>
Get schema by specific path from subform
Sourcepub fn get_schema_by_paths_subform(
&self,
subform_path: &str,
schema_paths: &[String],
format: Option<ReturnFormat>,
) -> Value
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
Sourcepub fn get_subform_paths(&self) -> Vec<String>
pub fn get_subform_paths(&self) -> Vec<String>
Get list of available subform paths
Sourcepub fn has_subform(&self, subform_path: &str) -> bool
pub fn has_subform(&self, subform_path: &str) -> bool
Check if a subform exists at the given path
Source§impl JSONEval
impl JSONEval
Sourcepub fn validate(
&mut self,
data: &str,
context: Option<&str>,
paths: Option<&[String]>,
token: Option<&CancellationToken>,
) -> Result<ValidationResult, String>
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?
28fn main() {
29 let args: Vec<String> = std::env::args().collect();
30 let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic");
31
32 let mut scenario_filter: Option<String> = None;
33 let mut enable_comparison = false;
34 let mut show_timing = false;
35 let mut i = 1;
36
37 // Parse arguments
38 while i < args.len() {
39 let arg = &args[i];
40
41 if arg == "-h" || arg == "--help" {
42 print_help(program_name);
43 return;
44 } else if arg == "--compare" {
45 enable_comparison = true;
46 } else if arg == "--timing" {
47 show_timing = true;
48 } else if !arg.starts_with('-') {
49 scenario_filter = Some(arg.clone());
50 } else {
51 eprintln!("Error: unknown option '{}'", arg);
52 print_help(program_name);
53 return;
54 }
55
56 i += 1;
57 }
58
59 println!("\n🚀 JSON Evaluation - Basic Example (JSON Schema)\n");
60
61 if enable_comparison {
62 println!("🔍 Comparison: enabled");
63 }
64 if show_timing {
65 println!("⏱️ Internal timing: enabled");
66 }
67 if enable_comparison || show_timing {
68 println!();
69 }
70
71 let samples_dir = Path::new("samples");
72 let mut scenarios = common::discover_scenarios(samples_dir);
73
74 // Filter out MessagePack scenarios - only use JSON
75 scenarios.retain(|s| !s.is_msgpack);
76
77 // Filter scenarios if a filter is provided
78 if let Some(ref filter) = scenario_filter {
79 scenarios.retain(|s| s.name.contains(filter));
80 println!("📋 Filtering scenarios matching: '{}'\n", filter);
81 }
82
83 if scenarios.is_empty() {
84 if let Some(filter) = scenario_filter {
85 println!(
86 "ℹ️ No scenarios found matching '{}' in `{}`.",
87 filter,
88 samples_dir.display()
89 );
90 } else {
91 println!(
92 "ℹ️ No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
93 samples_dir.display()
94 );
95 }
96 return;
97 }
98
99 println!("📊 Found {} scenario(s)\n", scenarios.len());
100
101 let mut total_parse_time = std::time::Duration::ZERO;
102 let mut total_eval_time = std::time::Duration::ZERO;
103 let mut successful_scenarios = 0;
104 let mut comparison_failures = 0;
105
106 for scenario in &scenarios {
107 println!("==============================");
108 println!("Scenario: {}", scenario.name);
109 println!("Schema: {} ({})",
110 scenario.schema_path.display(),
111 if scenario.is_msgpack { "MessagePack" } else { "JSON" }
112 );
113 println!("Data: {}\n", scenario.data_path.display());
114
115 // Clear timing data from previous scenarios
116 if show_timing {
117 json_eval_rs::enable_timing();
118 json_eval_rs::clear_timing_data();
119 }
120
121 let data_str = fs::read_to_string(&scenario.data_path)
122 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
123
124 // Step 1: Parse schema (JSONEval::new)
125 let parse_start = Instant::now();
126
127 let schema_str = fs::read_to_string(&scenario.schema_path)
128 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.schema_path.display(), e));
129
130 let mut eval = JSONEval::new(&schema_str, None, Some(&data_str))
131 .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e));
132
133 let parse_time = parse_start.elapsed();
134 println!(" 📝 Parse (new): {:?}", parse_time);
135
136 // Step 2: Evaluate
137 let eval_start = Instant::now();
138
139 eval.evaluate(&data_str, Some("{}"), None, None)
140 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
141
142 // Step 3: Validate
143 let validation_start = Instant::now();
144 let validation_result = eval.validate(&data_str, None, None, None)
145 .unwrap_or_else(|e| panic!("validation failed: {}", e));
146 let validation_time = validation_start.elapsed();
147 println!(" 🛡️ Validate: {:?}", validation_time);
148
149 // Legacy behavior: get_evaluated_schema takes skip_layout: bool
150 // We pass false to ensure layout IS resolved
151 let evaluated_schema = eval.get_evaluated_schema(false);
152 let schema_value = eval.get_schema_value();
153 let eval_time = eval_start.elapsed();
154
155 println!(" ⚡ Eval: {:?}", eval_time);
156 println!(" ⏱️ Total: {:?}\n", parse_time + eval_time);
157
158 // Print detailed timing breakdown if --timing flag is set
159 if show_timing {
160 json_eval_rs::print_timing_summary();
161 }
162
163 total_parse_time += parse_time;
164 total_eval_time += eval_time;
165 successful_scenarios += 1;
166
167 // Save results
168 let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
169 let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
170 let value_path = samples_dir.join(format!("{}-schema-value.json", scenario.name));
171 let validation_path = samples_dir.join(format!("{}-validation.json", scenario.name));
172
173 fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
174 .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
175
176 let mut metadata_obj = Map::new();
177 metadata_obj.insert("dependencies".to_string(), serde_json::to_value(&*eval.dependencies).unwrap());
178 metadata_obj.insert("evaluations".to_string(), serde_json::to_value(&*eval.evaluations).unwrap());
179 metadata_obj.insert("sorted_evaluations".to_string(), serde_json::to_value(&*eval.sorted_evaluations).unwrap());
180
181 fs::write(&parsed_path, common::pretty_json(&Value::Object(metadata_obj)))
182 .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
183
184 fs::write(&value_path, common::pretty_json(&schema_value))
185 .unwrap_or_else(|e| panic!("failed to write {}: {}", value_path.display(), e));
186
187 let validation_value = serde_json::to_value(&validation_result)
188 .unwrap_or_else(|e| panic!("failed to serialize validation result: {}", e));
189 fs::write(&validation_path, common::pretty_json(&validation_value))
190 .unwrap_or_else(|e| panic!("failed to write {}: {}", validation_path.display(), e));
191
192 println!("✅ Results saved:");
193 println!(" - {}", evaluated_path.display());
194 println!(" - {}", parsed_path.display());
195 println!(" - {}", value_path.display());
196 println!(" - {}\n", validation_path.display());
197
198 // Optional comparison
199 if enable_comparison {
200 if let Some(comp_path) = &scenario.comparison_path {
201 if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
202 comparison_failures += 1;
203 }
204 println!();
205 }
206 }
207 }
208
209 // Print summary
210 println!("{}", "=".repeat(50));
211 println!("📊 Summary");
212 println!("{}", "=".repeat(50));
213 println!("Total scenarios run: {}", successful_scenarios);
214 println!("Total parse time: {:?}", total_parse_time);
215 println!("Total eval time: {:?}", total_eval_time);
216 println!("Total time: {:?}", total_parse_time + total_eval_time);
217
218 if successful_scenarios > 1 {
219 println!("\nAverage per scenario:");
220 println!(" Parse: {:?}", total_parse_time / successful_scenarios as u32);
221 println!(" Eval: {:?}", total_eval_time / successful_scenarios as u32);
222 }
223
224 if enable_comparison {
225 println!("Comparison failures: {}", comparison_failures);
226 }
227
228 println!("\n✅ All scenarios completed!\n");
229}