Skip to main content

JSONEval

Struct JSONEval 

Source
pub struct JSONEval {
Show 22 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, /* private fields */
}

Fields§

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

Implementations§

Source§

impl JSONEval

Source

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

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

Source§

impl JSONEval

Source

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

Evaluate the schema with the given data and context.

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

A Result indicating success or an error message.

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

impl JSONEval

Source

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

Get the evaluated schema with optional layout resolution.

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

The evaluated schema as a JSON value.

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

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

Get specific schema value by path

Source

pub fn get_schema_value(&mut self) -> Value

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

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

pub fn get_schema_value_array(&self) -> Value

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

§Returns

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

Source

pub fn get_schema_value_object(&self) -> Value

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

§Returns

Flat object with dotted notation paths as keys and evaluated values

Source

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

Get evaluated schema without $params

Source

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

Get evaluated schema as MessagePack bytes

Source

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

Get value from evaluated schema by path

Source

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

Get evaluated schema parts by multiple paths

Source

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

Get original (unevaluated) schema by path

Source

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

Get original schema by multiple paths

Source

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

Flatten a nested object key-value pair to dotted keys

Source

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

Source§

impl JSONEval

Source

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

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

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

Create a new JSONEval instance from MessagePack-encoded schema

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

A Result containing the JSONEval instance or an error

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

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

Create a new JSONEval instance from a pre-parsed ParsedSchema

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

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

A Result containing the JSONEval instance or an error

§Example
use std::sync::Arc;

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

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

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

Source

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

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

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

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

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

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

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

Reload schema from MessagePack-encoded bytes

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

A Result indicating success or an error message

Source

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

Reload schema from a cached ParsedSchema

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

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

A Result indicating success or an error message

Source

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

Reload schema from ParsedSchemaCache using a cache key

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

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

A Result indicating success or an error message

Source§

impl JSONEval

Source

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

Resolve layout references with optional evaluation

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

A Result indicating success or an error message.

Source§

impl JSONEval

Source

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

Run pre-compiled logic against current data

Source

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

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

Source

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

Compile a logic expression from a Value and store it globally

Source

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

Compile and run logic in one go (convenience method)

Source§

impl JSONEval

Source

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

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

Source

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

Validate subform data against its schema rules

Source

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

Evaluate dependents in subform when a field changes

Source

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

Resolve layout for subform

Source

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

Get evaluated schema from subform

Source

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

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

Source

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

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

Source

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

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

Source

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

Get evaluated schema without $params from subform

Source

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

Get evaluated schema by specific path from subform

Source

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

Get evaluated schema by multiple paths from subform

Source

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

Get schema by specific path from subform

Source

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

Get schema by multiple paths from subform

Source

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

Get list of available subform paths

Source

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

Check if a subform exists at the given path

Source§

impl JSONEval

Source

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

Validate data against schema rules

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

Trait Implementations§

Source§

impl Clone for JSONEval

Source§

fn clone(&self) -> Self

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

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

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

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

Source§

fn into(self) -> U

Calls U::from(self).

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

Source§

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

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

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

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

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

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.