Skip to main content

basic/
basic.rs

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