use std::sync::Arc;
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
use rhai::{Dynamic, Engine};
use tempfile::TempDir;
use linesmith::data_context::DataContext;
use linesmith::plugins::{build_engine, CompiledPlugin, PluginRegistry, RhaiSegment};
use linesmith::segments::{Segment, BUILT_IN_SEGMENT_IDS};
const MINIMAL_PLUGIN: &str = include_str!("../tests/fixtures/plugins/minimal.rhai");
const MINIMAL_PAYLOAD: &[u8] = include_bytes!("../tests/fixtures/claude_minimal.json");
fn fresh_registry(src: &str) -> (PluginRegistry, Arc<Engine>, TempDir) {
let tmp = TempDir::new().expect("tempdir");
std::fs::write(tmp.path().join("minimal.rhai"), src).expect("write fixture");
let engine = build_engine();
let registry = PluginRegistry::load_with_xdg(
&[tmp.path().to_path_buf()],
None,
&engine,
BUILT_IN_SEGMENT_IDS,
);
assert!(
registry.load_errors().is_empty(),
"bench fixture must compile cleanly: {:?}",
registry.load_errors()
);
(registry, engine, tmp)
}
fn build_segment(registry: PluginRegistry, engine: Arc<Engine>) -> RhaiSegment {
let plugin: CompiledPlugin = registry
.into_plugins()
.into_iter()
.next()
.expect("compiled plugin");
RhaiSegment::from_compiled(plugin, engine, Dynamic::UNIT)
}
fn data_context() -> DataContext {
DataContext::new(linesmith::input::parse(MINIMAL_PAYLOAD).expect("payload parses"))
}
fn bench_engine_init(c: &mut Criterion) {
c.bench_function("plugin_engine_init", |b| {
b.iter(|| {
let engine = build_engine();
criterion::black_box(engine);
});
});
}
fn bench_compile(c: &mut Criterion) {
c.bench_function("plugin_compile", |b| {
b.iter_batched(
build_engine,
|engine| {
let ast = engine.compile(MINIMAL_PLUGIN).expect("compile ok");
criterion::black_box(ast);
},
BatchSize::PerIteration,
);
});
}
fn bench_load(c: &mut Criterion) {
c.bench_function("plugin_load", |b| {
b.iter_batched(
|| {
let tmp = TempDir::new().expect("tempdir");
std::fs::write(tmp.path().join("minimal.rhai"), MINIMAL_PLUGIN)
.expect("write fixture");
tmp
},
|tmp| {
let engine = build_engine();
let registry = PluginRegistry::load_with_xdg(
&[tmp.path().to_path_buf()],
None,
&engine,
BUILT_IN_SEGMENT_IDS,
);
assert!(registry.load_errors().is_empty());
criterion::black_box(registry);
},
BatchSize::PerIteration,
);
});
}
fn bench_render_cold(c: &mut Criterion) {
c.bench_function("plugin_render_cold", |b| {
b.iter_batched(
|| {
let (registry, engine, tmp) = fresh_registry(MINIMAL_PLUGIN);
(build_segment(registry, engine), data_context(), tmp)
},
|(seg, ctx, _tmp)| {
let rendered = seg
.render(&ctx)
.expect("render ok")
.expect("visible rendered segment");
criterion::black_box(rendered.text());
},
BatchSize::PerIteration,
);
});
}
fn bench_render_warm(c: &mut Criterion) {
let (registry, engine, _tmp) = fresh_registry(MINIMAL_PLUGIN);
let seg = build_segment(registry, engine);
let ctx = data_context();
c.bench_function("plugin_render_warm", |b| {
b.iter(|| {
let rendered = seg
.render(&ctx)
.expect("render ok")
.expect("visible rendered segment");
criterion::black_box(rendered.text());
});
});
}
criterion_group!(
plugins,
bench_engine_init,
bench_compile,
bench_load,
bench_render_cold,
bench_render_warm,
);
criterion_main!(plugins);