//! Performance benchmarks for core operations. //! //! Run with: `cargo bench` //! First run establishes baseline. Subsequent runs report regressions. use std::fs; use std::hint::black_box; use std::path::PathBuf; use criterion::{Criterion, criterion_group, criterion_main}; use swagger_cli::core::indexer::build_index; use swagger_cli::core::search::{SearchEngine, SearchOptions}; use swagger_cli::core::spec::SpecIndex; fn fixture_path(name: &str) -> PathBuf { let manifest_dir = env!("CARGO_MANIFEST_DIR"); PathBuf::from(manifest_dir) .join("tests") .join("fixtures") .join(name) } fn load_petstore_json() -> serde_json::Value { let path = fixture_path("petstore.json"); let bytes = fs::read(&path).expect("failed to read petstore.json fixture"); serde_json::from_slice(&bytes).expect("failed to parse petstore.json") } fn load_petstore_index() -> SpecIndex { let raw_json = load_petstore_json(); build_index(&raw_json, "sha256:bench", 1).expect("failed to build index") } fn bench_json_parse(c: &mut Criterion) { let path = fixture_path("petstore.json"); let bytes = fs::read(&path).expect("failed to read petstore.json fixture"); c.bench_function("parse_petstore_json", |b| { b.iter(|| { let v: serde_json::Value = serde_json::from_slice(black_box(&bytes)).expect("parse failed"); black_box(v); }); }); } fn bench_build_index_real(c: &mut Criterion) { let raw_json = load_petstore_json(); c.bench_function("build_index_real", |b| { b.iter(|| { let index = build_index(black_box(&raw_json), "sha256:bench", 1).expect("build_index failed"); black_box(index); }); }); } fn bench_hash_computation(c: &mut Criterion) { let path = fixture_path("petstore.json"); let bytes = fs::read(&path).expect("failed to read petstore.json"); c.bench_function("sha256_petstore", |b| { b.iter(|| { use sha2::{Digest, Sha256}; let mut hasher = Sha256::new(); hasher.update(black_box(&bytes)); let result = format!("sha256:{:x}", hasher.finalize()); black_box(result); }); }); } fn bench_json_pointer_resolution(c: &mut Criterion) { let raw_json = load_petstore_json(); c.bench_function("json_pointer_resolution", |b| { b.iter(|| { let a = raw_json .pointer("/paths/~1pets/get/summary") .map(|v| v.as_str()); let b_val = raw_json .pointer("/components/schemas/Pet") .map(|v| v.is_object()); let c_val = raw_json.pointer("/info/title").map(|v| v.as_str()); black_box((a, b_val, c_val)); }); }); } fn bench_search_real(c: &mut Criterion) { let index = load_petstore_index(); let engine = SearchEngine::new(&index); let opts = SearchOptions::default(); c.bench_function("search_real_pet", |b| { b.iter(|| { let results = engine.search(black_box("pet"), &opts); black_box(results); }); }); } fn bench_search_multi_term(c: &mut Criterion) { let index = load_petstore_index(); let engine = SearchEngine::new(&index); let opts = SearchOptions::default(); c.bench_function("search_real_multi_term", |b| { b.iter(|| { let results = engine.search(black_box("list all pets"), &opts); black_box(results); }); }); } fn bench_search_case_insensitive(c: &mut Criterion) { let index = load_petstore_index(); let engine = SearchEngine::new(&index); let opts = SearchOptions { case_sensitive: false, ..SearchOptions::default() }; c.bench_function("search_real_case_insensitive", |b| { b.iter(|| { let results = engine.search(black_box("PET"), &opts); black_box(results); }); }); } fn bench_normalize_json(c: &mut Criterion) { let path = fixture_path("petstore.json"); let bytes = fs::read(&path).expect("failed to read petstore.json fixture"); c.bench_function("normalize_json_input", |b| { b.iter(|| { let result = swagger_cli::core::indexer::normalize_to_json( black_box(&bytes), swagger_cli::core::indexer::Format::Json, ) .expect("normalize failed"); black_box(result); }); }); } fn bench_normalize_and_parse_pipeline(c: &mut Criterion) { let path = fixture_path("petstore.json"); let bytes = fs::read(&path).expect("failed to read petstore.json fixture"); // New way: normalize returns both bytes and value (one parse) c.bench_function("pipeline_normalize_new", |b| { b.iter(|| { let (json_bytes, value) = swagger_cli::core::indexer::normalize_to_json( black_box(&bytes), swagger_cli::core::indexer::Format::Json, ) .expect("normalize failed"); black_box((&json_bytes, &value)); }); }); // Old way simulation: parse to validate + copy, then parse again c.bench_function("pipeline_normalize_old", |b| { b.iter(|| { // Step 1: old normalize_to_json did: parse(validate), copy bytes let _: serde_json::Value = serde_json::from_slice(black_box(&bytes)).expect("validate failed"); let json_bytes = bytes.to_vec(); // Step 2: caller would parse again let value: serde_json::Value = serde_json::from_slice(&json_bytes).expect("parse failed"); black_box((&json_bytes, &value)); }); }); } fn bench_detect_format_json_no_hints(c: &mut Criterion) { let path = fixture_path("petstore.json"); let bytes = fs::read(&path).expect("failed to read petstore.json fixture"); c.bench_function("detect_format_json_no_hints", |b| { b.iter(|| { let fmt = swagger_cli::core::indexer::detect_format(black_box(&bytes), None, None); black_box(fmt); }); }); } fn bench_detect_format_with_hint(c: &mut Criterion) { let path = fixture_path("petstore.json"); let bytes = fs::read(&path).expect("failed to read petstore.json fixture"); c.bench_function("detect_format_with_content_type", |b| { b.iter(|| { let fmt = swagger_cli::core::indexer::detect_format( black_box(&bytes), None, Some("application/json"), ); black_box(fmt); }); }); } criterion_group!( benches, bench_json_parse, bench_build_index_real, bench_hash_computation, bench_json_pointer_resolution, bench_search_real, bench_search_multi_term, bench_search_case_insensitive, bench_normalize_json, bench_normalize_and_parse_pipeline, bench_detect_format_json_no_hints, bench_detect_format_with_hint, ); criterion_main!(benches);