use criterion::{black_box, criterion_group, criterion_main, Criterion};
use std::fs;
use std::io::{self, Write};
use std::path::Path;
use toggle::core::{
find_and_toggle_section, toggle_comments, toggle_comments_multi, CommentStyle, LineRange,
};
fn generate_fixture(num_lines: usize) -> String {
let mut buf = String::new();
for i in 0..num_lines {
if i % 2 == 0 {
buf.push_str(&format!("# This is a commented line {}\n", i + 1));
} else {
buf.push_str(&format!("print('This is an uncommented line {}')\n", i + 1));
}
}
buf
}
fn create_fixture_file(path: &Path, num_lines: usize) -> io::Result<()> {
let mut file = fs::File::create(path)?;
for i in 0..num_lines {
if i % 2 == 0 {
writeln!(file, "# This is a commented line {}", i + 1)?;
} else {
writeln!(file, "print('This is an uncommented line {}')", i + 1)?;
}
}
Ok(())
}
fn generate_section_content(num_sections: usize, lines_per_section: usize) -> String {
let mut buf = String::new();
for s in 0..num_sections {
buf.push_str(&format!("# toggle:start ID=section{}\n", s));
for l in 0..lines_per_section {
buf.push_str(&format!("print('section {} line {}')\n", s, l + 1));
}
buf.push_str(&format!("# toggle:end ID=section{}\n", s));
}
buf
}
fn bench_toggle_by_size(c: &mut Criterion) {
let small = generate_fixture(50);
let medium = generate_fixture(500);
let large = generate_fixture(5000);
c.bench_function("toggle_small_50", |b| {
b.iter(|| {
let ranges = vec![LineRange::new(1, 50)];
toggle_comments(black_box(&small), black_box(&ranges), None)
})
});
c.bench_function("toggle_medium_500", |b| {
b.iter(|| {
let ranges = vec![LineRange::new(1, 500)];
toggle_comments(black_box(&medium), black_box(&ranges), None)
})
});
c.bench_function("toggle_large_5000", |b| {
b.iter(|| {
let ranges = vec![LineRange::new(1, 5000)];
toggle_comments(black_box(&large), black_box(&ranges), None)
})
});
}
fn bench_toggle_comments(c: &mut Criterion) {
let content = generate_fixture(1000);
c.bench_function("toggle_all_1000", |b| {
b.iter(|| {
let ranges = vec![LineRange::new(1, 1000)];
toggle_comments(black_box(&content), black_box(&ranges), None)
})
});
c.bench_function("toggle_range_100", |b| {
b.iter(|| {
let ranges = vec![LineRange::new(1, 100)];
toggle_comments(black_box(&content), black_box(&ranges), None)
})
});
}
fn bench_toggle_multi(c: &mut Criterion) {
let mut content = String::new();
for i in 0..1000 {
content.push_str(&format!("console.log('line {}');\n", i + 1));
}
c.bench_function("toggle_multi_1000", |b| {
b.iter(|| {
let ranges = vec![LineRange::new(1, 1000)];
toggle_comments_multi(black_box(&content), black_box(&ranges), None, "/*", "*/")
})
});
}
fn bench_section_toggle(c: &mut Criterion) {
let content = generate_section_content(20, 10);
let style = CommentStyle {
single_line: "#".to_string(),
multi_line_start: None,
multi_line_end: None,
};
c.bench_function("section_toggle_single", |b| {
b.iter(|| {
let mut lines: Vec<String> = content.lines().map(String::from).collect();
find_and_toggle_section(black_box(&mut lines), black_box("section10"), &None, &style)
.unwrap()
})
});
c.bench_function("section_toggle_all_20", |b| {
b.iter(|| {
let mut lines: Vec<String> = content.lines().map(String::from).collect();
for i in 0..20 {
let id = format!("section{}", i);
find_and_toggle_section(black_box(&mut lines), &id, &None, &style).unwrap();
}
})
});
}
fn bench_cli_100_files(c: &mut Criterion) {
let dir = tempfile::tempdir().expect("Failed to create temp dir");
let mut paths: Vec<String> = Vec::new();
for i in 0..100 {
let file_path = dir.path().join(format!("file_{}.py", i));
create_fixture_file(&file_path, 50).expect("Failed to create fixture file");
paths.push(file_path.to_str().unwrap().to_string());
}
let binary = env!("CARGO_BIN_EXE_toggle");
c.bench_function("cli_100_files", |b| {
b.iter_batched(
|| {
for (i, p) in paths.iter().enumerate() {
create_fixture_file(Path::new(p), 50)
.unwrap_or_else(|_| panic!("Failed to recreate file {}", i));
}
},
|()| {
let mut cmd = std::process::Command::new(binary);
cmd.args(&paths).args(["-l", "1:10"]);
let output = cmd.output().expect("Failed to run toggle");
assert!(output.status.success(), "toggle exited with error");
},
criterion::BatchSize::SmallInput,
)
});
}
criterion_group!(
benches,
bench_toggle_by_size,
bench_toggle_comments,
bench_toggle_multi,
bench_section_toggle,
bench_cli_100_files,
);
criterion_main!(benches);