1mod common;
2
3use json_eval_rs::JSONEval;
4use serde_json::{Map, Value};
5use std::fs;
6use std::path::Path;
7use std::time::Instant;
8
9fn print_help(program_name: &str) {
10 println!("\nš JSON Evaluation - Basic Example (JSON/MsgPack Schema)\n");
11 println!("USAGE:");
12 println!(" {} [OPTIONS] [FILTER]\n", program_name);
13 println!("OPTIONS:");
14 println!(" -h, --help Show this help message");
15 println!(" --compare Enable comparison with expected results");
16 println!(" --timing Show detailed internal timing breakdown\n");
17 println!("ARGUMENTS:");
18 println!(" [FILTER] Optional filter to match scenario names\n");
19 println!("DESCRIPTION:");
20 println!(" Evaluates schemas using JSONEval::new() or JSONEval::new_from_msgpack().\n");
21 println!("EXAMPLES:");
22 println!(" {} # Run all scenarios", program_name);
23 println!(
24 " {} zcc # Run scenarios matching 'zcc'",
25 program_name
26 );
27 println!(
28 " {} --compare # Run with comparison enabled",
29 program_name
30 );
31 println!(
32 " {} zcc --timing # Run with detailed timing breakdown",
33 program_name
34 );
35}
36
37fn main() {
38 let args: Vec<String> = std::env::args().collect();
39 let program_name = args.get(0).map(|s| s.as_str()).unwrap_or("basic");
40
41 let mut scenario_filter: Option<String> = None;
42 let mut enable_comparison = false;
43 let mut show_timing = false;
44 let mut i = 1;
45
46 while i < args.len() {
48 let arg = &args[i];
49
50 if arg == "-h" || arg == "--help" {
51 print_help(program_name);
52 return;
53 } else if arg == "--compare" {
54 enable_comparison = true;
55 } else if arg == "--timing" {
56 show_timing = true;
57 } else if !arg.starts_with('-') {
58 scenario_filter = Some(arg.clone());
59 } else {
60 eprintln!("Error: unknown option '{}'", arg);
61 print_help(program_name);
62 return;
63 }
64
65 i += 1;
66 }
67
68 println!("\nš JSON Evaluation - Basic Example (JSON/MsgPack Schema)\n");
69
70 if enable_comparison {
71 println!("š Comparison: enabled");
72 }
73 if show_timing {
74 println!("ā±ļø Internal timing: enabled");
75 }
76 if enable_comparison || show_timing {
77 println!();
78 }
79
80 let samples_dir = Path::new("samples");
81 let mut scenarios = common::discover_scenarios(samples_dir);
82
83 if let Some(ref filter) = scenario_filter {
85 scenarios.retain(|s| s.name.contains(filter));
86 println!("š Filtering scenarios matching: '{}'\n", filter);
87 }
88
89 if scenarios.is_empty() {
90 if let Some(filter) = scenario_filter {
91 println!(
92 "ā¹ļø No scenarios found matching '{}' in `{}`.",
93 filter,
94 samples_dir.display()
95 );
96 } else {
97 println!(
98 "ā¹ļø No scenarios discovered in `{}`. Add files like `name.json` and `name-data.json`.",
99 samples_dir.display()
100 );
101 }
102 return;
103 }
104
105 println!("š Found {} scenario(s)\n", scenarios.len());
106
107 let mut total_parse_time = std::time::Duration::ZERO;
108 let mut total_eval_time = std::time::Duration::ZERO;
109 let mut successful_scenarios = 0;
110 let mut comparison_failures = 0;
111
112 for scenario in &scenarios {
113 println!("==============================");
114 println!("Scenario: {}", scenario.name);
115 println!(
116 "Schema: {} ({})",
117 scenario.schema_path.display(),
118 if scenario.is_msgpack {
119 "MessagePack"
120 } else {
121 "JSON"
122 }
123 );
124 println!("Data: {}\n", scenario.data_path.display());
125
126 if show_timing {
128 json_eval_rs::enable_timing();
129 json_eval_rs::clear_timing_data();
130 }
131
132 let data_str = fs::read_to_string(&scenario.data_path)
133 .unwrap_or_else(|e| panic!("failed to read {}: {}", scenario.data_path.display(), e));
134
135 let parse_start = Instant::now();
137
138 let mut eval = if scenario.is_msgpack {
139 let schema_msgpack = fs::read(&scenario.schema_path).unwrap_or_else(|e| {
140 panic!("failed to read {}: {}", scenario.schema_path.display(), e)
141 });
142 println!(
143 " š¦ MessagePack schema size: {} bytes",
144 schema_msgpack.len()
145 );
146 JSONEval::new_from_msgpack(&schema_msgpack, None, Some(&data_str))
147 .unwrap_or_else(|e| panic!("failed to create JSONEval from MessagePack: {}", e))
148 } else {
149 let schema_str = fs::read_to_string(&scenario.schema_path).unwrap_or_else(|e| {
150 panic!("failed to read {}: {}", scenario.schema_path.display(), e)
151 });
152 JSONEval::new(&schema_str, None, Some(&data_str))
153 .unwrap_or_else(|e| panic!("failed to create JSONEval: {}", e))
154 };
155
156 let parse_time = parse_start.elapsed();
157 println!(" š Parse (new): {:?}", parse_time);
158
159 let eval_start = Instant::now();
161
162 eval.evaluate(&data_str, Some("{}"), None, None)
163 .unwrap_or_else(|e| panic!("evaluation failed: {}", e));
164
165 let validation_start = Instant::now();
167 let validation_result = eval
168 .validate(&data_str, None, None, None)
169 .unwrap_or_else(|e| panic!("validation failed: {}", e));
170 let validation_time = validation_start.elapsed();
171 println!(" š”ļø Validate: {:?}", validation_time);
172
173 let evaluated_schema = eval.get_evaluated_schema(false);
176 let schema_value = eval.get_schema_value();
177 let eval_time = eval_start.elapsed();
178
179 println!(" ā” Eval: {:?}", eval_time);
180 println!(" ā±ļø Total: {:?}\n", parse_time + eval_time);
181
182 if show_timing {
184 json_eval_rs::print_timing_summary();
185 }
186
187 total_parse_time += parse_time;
188 total_eval_time += eval_time;
189 successful_scenarios += 1;
190
191 let evaluated_path = samples_dir.join(format!("{}-evaluated-schema.json", scenario.name));
193 let parsed_path = samples_dir.join(format!("{}-parsed-schema.json", scenario.name));
194 let value_path = samples_dir.join(format!("{}-schema-value.json", scenario.name));
195 let validation_path = samples_dir.join(format!("{}-validation.json", scenario.name));
196
197 fs::write(&evaluated_path, common::pretty_json(&evaluated_schema))
198 .unwrap_or_else(|e| panic!("failed to write {}: {}", evaluated_path.display(), e));
199
200 let mut metadata_obj = Map::new();
201 metadata_obj.insert(
202 "dependencies".to_string(),
203 serde_json::to_value(&*eval.dependencies).unwrap(),
204 );
205 metadata_obj.insert(
206 "evaluations".to_string(),
207 serde_json::to_value(&*eval.evaluations).unwrap(),
208 );
209 metadata_obj.insert(
210 "sorted_evaluations".to_string(),
211 serde_json::to_value(&*eval.sorted_evaluations).unwrap(),
212 );
213
214 fs::write(
215 &parsed_path,
216 common::pretty_json(&Value::Object(metadata_obj)),
217 )
218 .unwrap_or_else(|e| panic!("failed to write {}: {}", parsed_path.display(), e));
219
220 fs::write(&value_path, common::pretty_json(&schema_value))
221 .unwrap_or_else(|e| panic!("failed to write {}: {}", value_path.display(), e));
222
223 let validation_value = serde_json::to_value(&validation_result)
224 .unwrap_or_else(|e| panic!("failed to serialize validation result: {}", e));
225 fs::write(&validation_path, common::pretty_json(&validation_value))
226 .unwrap_or_else(|e| panic!("failed to write {}: {}", validation_path.display(), e));
227
228 println!("ā
Results saved:");
229 println!(" - {}", evaluated_path.display());
230 println!(" - {}", parsed_path.display());
231 println!(" - {}", value_path.display());
232 println!(" - {}\n", validation_path.display());
233
234 if enable_comparison {
236 if let Some(comp_path) = &scenario.comparison_path {
237 if common::compare_with_expected(&evaluated_schema, comp_path).is_err() {
238 comparison_failures += 1;
239 }
240 println!();
241 }
242 }
243 }
244
245 println!("{}", "=".repeat(50));
247 println!("š Summary");
248 println!("{}", "=".repeat(50));
249 println!("Total scenarios run: {}", successful_scenarios);
250 println!("Total parse time: {:?}", total_parse_time);
251 println!("Total eval time: {:?}", total_eval_time);
252 println!("Total time: {:?}", total_parse_time + total_eval_time);
253
254 if successful_scenarios > 1 {
255 println!("\nAverage per scenario:");
256 println!(
257 " Parse: {:?}",
258 total_parse_time / successful_scenarios as u32
259 );
260 println!(
261 " Eval: {:?}",
262 total_eval_time / successful_scenarios as u32
263 );
264 }
265
266 if enable_comparison {
267 println!("Comparison failures: {}", comparison_failures);
268 }
269
270 println!("\nā
All scenarios completed!\n");
271}