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