Skip to main content

basic_parsed/
basic_parsed.rs

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