nordisk/edda
crates/nornir-docgen/src/sections.rs
//! Section-level docs assembly.
//!
//! Looks for paired markers in a Markdown file:
//!
//! ```text
//! <!-- nornir:gen:start:bench -->
//! ... auto-generated ...
//! <!-- nornir:gen:end:bench -->
//! ```
//!
//! Each `start` marker carries a section name and optional `key=value` args:
//!
//! ```text
//! <!-- nornir:gen:start:symbols binary=target/debug/foo limit=20 -->
//! ```
//!
//! Renderers (keyed by section name):
//! - `bench` — flat table from the latest [`BenchRun`] (back-compat).
//! - `benches` — pivot table from the latest [`BenchRun`]. `winner=col`
//! (default) bolds the best cell per row across same-unit rival
//! columns (ours-vs-legacy); `winner=row` bolds the best cell
//! down each column across rival rows (which system wins each
//! metric — for cross-system comparisons). `mode=compact`
//! collapses all rivals to one synthetic "best-of" row (absurd-
//! best value per column); `top=N` keeps the N strongest rows
//! by `rank=METRIC` — the tight top-of-README mashup.
//! - `mashup` — ZERO-CONFIG top-of-README comparison: auto-discovers *ours*
//! + every rival system from the latest [`BenchRun`] and renders
//! the N-way `compare`/`winner=row` table itself (no `compare=`
//! to hand-list), plus a biggest-diff headline. All args optional.
//! - `bench_chart` — horizontal bar chart for one `metric=` across the matching
//! workloads, winner highlighted; an SVG asset (`export`) or
//! a unicode-bar text fallback. The "featured mashup" visual.
//! - `bench_status` — the run's **provenance + honesty header**, and the one
//! place a repo with NO numbers still renders a statement
//! instead of a silent gap: version · host · cores · date, arm
//! and test counts, a PREVIEW banner when the harness declared
//! its own run unpublishable, and the arms screened out as not
//! credible as real work. Takes no arguments — with no run it
//! probes the repo for a `nornir-bench` entrypoint / criterion
//! `[[bench]]` targets and renders "not benched" with the
//! reason. See [`super::bench_status`].
//! - `tests` — unit/integration/doc test inventory (via `syn`).
//! - `depgraph` — workspace crate-dependency graph. With feature
//! `export`: rendered as static SVG (typst engine)
//! under `<repo_root>/.nornir/assets/depgraph.svg` (a
//! tracked path, so the link resolves on git hosts) and
//! referenced via a Markdown image link. Without the
//! feature: falls back to a self-contained inline SVG
//! (no JS, no build step — never Mermaid).
//! - `symbols` — top public symbols extracted from a binary (DWARF).
//! - `callgraph` — most-called callees from a binary (DWARF inline edges).
//! - `capabilities` — a ✓/✗ **static-capability matrix** built from a
//! [`BenchSource::Static`](nornir_bench::BenchSource) row's
//! `cap.<slug>.<rival>` numeric keys (the seam any repo emits
//! via [`BenchResult::static_capabilities`](nornir_bench::BenchResult)).
//! Rows are capabilities, columns are `ours` + rivals, the
//! `ours` column is bold: `source=<bench-name>` names the row,
//! `ours=<key>` picks our column (default: `source` minus a
//! trailing `.capabilities`).
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use anyhow::{Context, Result, anyhow, bail};
use nornir_bench::BenchRun;
use nornir_introspect::{artifact, callgraph_dwarf, depgraph};
/// Outcome of an assemble run on one file.
#[derive(Debug, Default)]
pub struct FileReport {
pub path: PathBuf,
pub sections: Vec<String>,
pub changed: bool,
}
/// Context passed to section renderers.
pub struct Ctx<'a> {
pub repo_root: &'a Path,
pub workspace_root: &'a Path,
pub run: Option<&'a BenchRun>,
/// Bench-run history (newest-first not assumed; the renderer sorts), used by
/// the `bench_history` section. Pre-fetched by the caller — renderers must not
/// open the warehouse themselves (the server already holds its single lock).
/// Empty by default; populate via [`with_history`](Self::with_history).
pub history: &'a [BenchRun],
/// Directory of the doc currently being rendered, when known. Used to emit
/// **doc-relative** asset links (e.g. the `depgraph` SVG) so an image
/// embedded in `.nornir/design.md` points at `assets/depgraph.svg`, while
/// the same section in the repo-root `README.md` points at
/// `.nornir/assets/depgraph.svg`. Git hosts resolve image paths relative to
/// the containing file, so this must track it. `None` ⇒ repo-root-relative
/// (the historical default, correct for README).
pub doc_dir: Option<&'a Path>,
/// Pre-fetched source-coverage rows for the `coverage` section (feature N —
/// `nornir:gen:coverage`). Pre-fetched by the caller (renderers must not open
/// the warehouse). Empty by default; populate via [`with_coverage`](Self::with_coverage).
pub coverage: &'a [nornir_coverage::CoverageRow],
/// Markdown-only mode: never (re)write the heavy/non-deterministic media
/// assets (`depgraph.svg`, bench-chart SVGs) to `.nornir/assets/`. The
/// markdown still emits the image link (the asset is committed from a prior
/// full render), so the README/CHANGELOG text stays deterministic. This is
/// what the **pre-commit hook** uses so an ordinary commit never regenerates
/// the noisy SVG/PDF — only `docs render` / the release backstop do.
/// `false` ⇒ historical behaviour (assets are (re)written on render).
pub no_assets: bool,
/// Manual (thin-book) mode: when assembling the whole-doc book, include only
/// the authoritative **manual** classes — README/CHANGELOG front matter plus
/// `design` / `guide` / benchmark chapters — and EXCLUDE the non-manual
/// back-matter (`*-reasoning.md`, `*-history.md`, `*-unsorted.md`,
/// `*-for-idiots.md` / `*-for-dummies.md`, `docs-generation.md`, and the
/// `Tankegångar` catch-all). This is the doctrine's thin manual — it keeps the
/// shipped `docs/book.pdf` small (Codeberg space) without deleting any source;
/// the reasoning/history still live in `.nornir/`. `false` ⇒ full assembly.
pub manual: bool,
/// The **network half** of path-dep prep for the `depgraph` section, injected
/// by the caller so this crate stays free of any network/git-clone coupling:
/// given `(repo_root, scan_root)` it clones any missing sibling repo into
/// `scan_root` and returns how many it fetched (the same seam
/// [`nornir_introspect::depgraph::extract_with_path_deps`] takes). nornir's
/// CLI/server inject `security::clone_missing_path_dep_siblings`; `None`
/// (the default) behaves as `|_, _| 0` — pure symlink materialization only,
/// exactly what a unit test or an offline caller wants. Populate via
/// [`with_path_dep_cloner`](Self::with_path_dep_cloner).
pub path_dep_cloner: Option<&'a (dyn Fn(&Path, &Path) -> usize + Sync)>,
/// The **kernel evidence reader** for the AI context pack (kernel tooling
/// fix 16), injected by the caller so this leaf never links the build
/// engine: given the repo root it returns the generated "Kernel evidence"
/// Markdown section, `None` when the repo has no kernel evidence. nornir
/// injects edda `nornir_build_thing::kernel_facts::ai_section`. `None` (the
/// default) ⇒ no section. Populate via [`with_kernel_facts`](Self::with_kernel_facts).
pub kernel_facts: Option<&'a (dyn Fn(&Path) -> anyhow::Result<Option<String>> + Sync)>,
}
impl<'a> Ctx<'a> {
pub fn new(repo_root: &'a Path, workspace_root: &'a Path, run: Option<&'a BenchRun>) -> Self {
Ctx {
repo_root,
workspace_root,
run,
history: &[],
doc_dir: None,
coverage: &[],
no_assets: false,
manual: false,
path_dep_cloner: None,
kernel_facts: None,
}
}
/// Manual (thin-book) mode — exclude non-manual back-matter from the assembled
/// book. See [`Ctx::manual`].
pub fn manual(mut self) -> Self {
self.manual = true;
self
}
/// Markdown-only render: skip writing the heavy/non-deterministic SVG/PDF
/// assets (see [`Ctx::no_assets`]). Used by the pre-commit hook.
pub fn markdown_only(mut self) -> Self {
self.no_assets = true;
self
}
/// Attach pre-fetched bench-run history for the `bench_history` section.
pub fn with_history(mut self, history: &'a [BenchRun]) -> Self {
self.history = history;
self
}
/// Attach pre-fetched source-coverage rows for the `coverage` section
/// (feature N). The caller reads the latest `coverage` run from the warehouse
/// and passes its rows; the renderer turns them into the maven-style table.
pub fn with_coverage(
mut self,
coverage: &'a [nornir_coverage::CoverageRow],
) -> Self {
self.coverage = coverage;
self
}
/// Set the directory of the doc being rendered, so asset links are emitted
/// relative to it. See [`Ctx::doc_dir`].
pub fn with_doc_dir(mut self, doc_dir: &'a Path) -> Self {
self.doc_dir = Some(doc_dir);
self
}
/// Inject the network half of path-dep prep for the `depgraph` section
/// (clone missing sibling repos into the scan root). See
/// [`Ctx::path_dep_cloner`]. Absent ⇒ no cloning, symlink materialization only.
pub fn with_path_dep_cloner(
mut self,
cloner: &'a (dyn Fn(&Path, &Path) -> usize + Sync),
) -> Self {
self.path_dep_cloner = Some(cloner);
self
}
/// Inject the kernel evidence reader for the AI context pack. See
/// [`Ctx::kernel_facts`].
pub fn with_kernel_facts(
mut self,
reader: &'a (dyn Fn(&Path) -> anyhow::Result<Option<String>> + Sync),
) -> Self {
self.kernel_facts = Some(reader);
self
}
}
#[derive(Debug)]
struct Marker {
line_start: usize,
line_end: usize,
name: String,
args: HashMap<String, String>,
}
const START: &str = "<!-- nornir:gen:start:";
const END: &str = "<!-- nornir:gen:end:";
/// Split a marker body into tokens on whitespace, but keep a double-quoted span
/// together so an arg value may contain spaces (`title="catalog reads (ops/s)"`).
/// The surrounding quotes are stripped from the token.
fn tokenize_marker(s: &str) -> Vec<String> {
let mut out = Vec::new();
let mut cur = String::new();
let mut in_q = false;
let mut started = false;
for c in s.chars() {
match c {
'"' => {
in_q = !in_q;
started = true;
}
c if c.is_whitespace() && !in_q => {
if started {
out.push(std::mem::take(&mut cur));
started = false;
}
}
c => {
cur.push(c);
started = true;
}
}
}
if started {
out.push(cur);
}
out
}
fn parse_markers(text: &str) -> Result<Vec<(Marker, usize)>> {
let lines: Vec<&str> = text.split_inclusive('\n').collect();
let mut open: Vec<(usize, String, HashMap<String, String>)> = Vec::new();
let mut out: Vec<(Marker, usize)> = Vec::new();
let mut in_fence = false;
for (i, line) in lines.iter().enumerate() {
let t = line.trim();
// Toggle fenced-code-block state. We track both ``` and ~~~ fences;
// any marker lines *inside* a fence are documentation examples and
// must be left alone.
if t.starts_with("```") || t.starts_with("~~~") {
in_fence = !in_fence;
continue;
}
if in_fence {
continue;
}
if let Some(rest) = t.strip_prefix(START) {
let body = rest
.strip_suffix("-->")
.ok_or_else(|| anyhow!("malformed start marker at line {}: {t}", i + 1))?
.trim();
let mut parts = tokenize_marker(body).into_iter();
let name = parts
.next()
.ok_or_else(|| anyhow!("missing section name at line {}", i + 1))?;
let mut args = HashMap::new();
for kv in parts {
if let Some((k, v)) = kv.split_once('=') {
args.insert(k.to_string(), v.to_string());
}
}
open.push((i, name, args));
} else if let Some(rest) = t.strip_prefix(END) {
let name = rest
.strip_suffix("-->")
.ok_or_else(|| anyhow!("malformed end marker at line {}: {t}", i + 1))?
.trim()
.to_string();
let (start_i, start_name, args) = open
.pop()
.ok_or_else(|| anyhow!("unmatched end marker `{name}` at line {}", i + 1))?;
if start_name != name {
bail!(
"marker mismatch: start `{start_name}` at line {} vs end `{name}` at line {}",
start_i + 1,
i + 1
);
}
out.push((
Marker {
line_start: start_i,
line_end: i,
name,
args,
},
0,
));
}
}
if let Some((i, name, _)) = open.pop() {
bail!("unclosed marker `{name}` opened at line {}", i + 1);
}
Ok(out)
}
/// The "I have no fresh data" bodies a renderer emits when its source is empty:
/// no bench run at all (warehouse unreachable, never benched), no result matching
/// the marker's `name=`/`exclude=` filter in *this* run, or no capability row.
///
/// These are **absence markers, not content**. Splicing one over a section that
/// already holds a real rendered table destroys hard-won benchmark numbers that
/// simply have no live source right now — a bench that wasn't re-run, a rival arm
/// dropped from the latest sweep, a warehouse that was down when the pre-commit
/// hook fired. Docs generation must never be destructive: see
/// [`is_no_data_placeholder`] and its use in [`render`].
///
/// Author-error bodies (`_(bench_hero needs `fast=` …)_`) are deliberately NOT in
/// this list — a malformed marker should stay visible so it gets fixed.
/// ⚠ THE SIXTH ONE WAS MISSING, and it cost real numbers (measured 2026-08-01 on
/// nornir's own README — see [`super::bench_status::NOT_BENCHED_PREFIX`]). The
/// `bench_status` card's absence body opens `**Not benched.**`, not `_(no …`, so it
/// was classified as CONTENT and spliced over a real measured card while the
/// `benches` table two lines above it — whose placeholder IS listed — was correctly
/// preserved. A half-protected doc reads as a protected one.
///
/// The entry is the renderer's OWN constant, not a copy of its wording, so the
/// prose can be reworded without silently falling out of this list.
const NO_DATA_PREFIXES: [&str; 6] = [
"_(no bench history yet",
"_(no bench results",
"_(no capability matrix yet",
"_(no `",
"_(no coverage recorded",
super::bench_status::NOT_BENCHED_PREFIX,
];
/// Is `body` a renderer's "no fresh data" placeholder rather than real content?
///
/// Matches on the leading placeholder run so a multi-line body that *starts* with
/// content is never mistaken for absence.
pub(crate) fn is_no_data_placeholder(body: &str) -> bool {
let t = body.trim();
if t.is_empty() {
return true;
}
NO_DATA_PREFIXES.iter().any(|p| t.starts_with(p))
}
/// Render one section. `prev_body` is the section's existing content (everything
/// strictly between its start and end markers), used to make generation
/// **non-destructive**: when the renderer has no fresh data but the section already
/// holds a real table, the existing body is kept instead of being blanked.
fn render(ctx: &Ctx, m: &Marker, prev_body: &str) -> Result<String> {
let body = match m.name.as_str() {
"bench" => render_bench(ctx, &m.args)?,
"bench_history" => render_bench_history(ctx, &m.args)?,
"benches" => render_benches(ctx, &m.args)?,
"bench_chart" => render_bench_chart(ctx, &m.args)?,
"bench_hero" => render_bench_hero(ctx, &m.args)?,
"bench_status" => super::bench_status::render(ctx, &m.args)?,
"mashup" => render_mashup(ctx, &m.args)?,
"tests" => render_tests(ctx, &m.args)?,
"coverage" => render_coverage(ctx, &m.args)?,
"capabilities" => render_capabilities(ctx, &m.args)?,
"depgraph" => render_depgraph(ctx)?,
"symbols" => render_symbols(ctx, &m.args)?,
"callgraph" => render_callgraph(ctx, &m.args)?,
other => bail!("unknown section `{other}`"),
};
// NON-DESTRUCTIVE GENERATION: a renderer with no fresh data must never
// overwrite a section that already has a body. Keep what is committed; the next
// render with live bench data replaces it normally.
//
// This deliberately also keeps an existing *placeholder* rather than swapping it
// for a differently-worded one. Absence is absence — rewording it changes no
// fact, but it does leave the file dirty after every render, and a doc that is
// perpetually dirty is one people stage blindly. That habit is how the real
// tables got committed away in the first place.
let body = if is_no_data_placeholder(&body) && !prev_body.trim().is_empty() {
prev_body.to_string()
} else {
body
};
let mut out = String::new();
out.push_str("<!-- nornir:gen:start:");
out.push_str(&m.name);
for (k, v) in args_sorted(&m.args) {
out.push(' ');
out.push_str(&k);
out.push('=');
// Re-quote values that carry spaces so the round-trip re-parses (see
// `tokenize_marker`).
if v.chars().any(char::is_whitespace) {
out.push('"');
out.push_str(&v);
out.push('"');
} else {
out.push_str(&v);
}
}
out.push_str(" -->\n");
out.push_str(body.trim_end());
out.push('\n');
out.push_str("<!-- nornir:gen:end:");
out.push_str(&m.name);
out.push_str(" -->\n");
Ok(out)
}
fn args_sorted(a: &HashMap<String, String>) -> Vec<(String, String)> {
let mut v: Vec<(String, String)> = a.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
v.sort_by(|a, b| a.0.cmp(&b.0));
v
}
/// Rewrite `path` in place by replacing every section between paired markers.
pub fn assemble_file(path: &Path, ctx: &Ctx) -> Result<FileReport> {
let text = std::fs::read_to_string(path).with_context(|| format!("read {}", path.display()))?;
// Scope the context to this doc's directory so asset links (depgraph SVG)
// are emitted relative to where the file lives.
let parent = path.parent().map(|p| p.to_path_buf());
let scoped = match parent.as_deref() {
Some(dir) => Ctx {
doc_dir: Some(dir),
..*ctx
},
None => Ctx { ..*ctx },
};
let (new_text, sections) = rewrite(&text, &scoped)?;
let changed = new_text != text;
if changed {
std::fs::write(path, &new_text).with_context(|| format!("write {}", path.display()))?;
}
Ok(FileReport {
path: path.to_path_buf(),
sections,
changed,
})
}
/// Like [`assemble_file`] but errors out (non-zero exit) if rewriting would
/// change the file. Use in CI / release gates.
pub fn check_file(path: &Path, ctx: &Ctx) -> Result<FileReport> {
let text = std::fs::read_to_string(path).with_context(|| format!("read {}", path.display()))?;
let (new_text, sections) = rewrite(&text, ctx)?;
if new_text != text {
bail!(
"{} is out of date — run `nornir docs assemble` to regenerate",
path.display()
);
}
Ok(FileReport {
path: path.to_path_buf(),
sections,
changed: false,
})
}
/// Like [`assemble_file`] / [`check_file`], but in-memory: returns the
/// rewritten text and the names of sections that were filled.
pub fn rewrite_str(text: &str, ctx: &Ctx) -> Result<(String, Vec<String>)> {
rewrite_with_fallback(text, ctx, None)
}
/// Like [`rewrite_str`], but with the PREVIOUSLY RENDERED OUTPUT as the fallback
/// source of existing section bodies.
///
/// A MANAGED doc (`README.md`, `CHANGELOG.md`) is generated: `.nornir/README.md` is
/// a template whose `nornir:gen` markers are *intentionally empty*, and the filled
/// result is written to `<repo>/README.md`. So for those docs the rendered tables
/// live ONLY in the output — which means the non-destructive rule in [`render`] had
/// nothing to protect: `prev_body` came from the template and was always empty, the
/// no-data placeholder always won, and the output's real numbers were overwritten.
///
/// Observed 2026-07-22 on znippy-zoomies: with no warehouse rows reachable,
/// `nornir docs render` replaced **58 lines** of committed, measured bench tables
/// with 9 `_(no bench history yet …)_` placeholders. That is the same data loss the
/// in-place guard already prevents, arriving through the generated-output door — and
/// it had destroyed skade's whole benchmark set once before.
///
/// Sections are matched between output and template by their START MARKER LINE,
/// which carries the marker's full argument list (`compare=`, `cols=`, …) and so
/// distinguishes the many sibling `benches` blocks a README carries.
pub fn rewrite_str_against_output(
text: &str,
ctx: &Ctx,
prev_output: &str,
) -> Result<(String, Vec<String>)> {
rewrite_with_fallback(text, ctx, Some(prev_output))
}
/// Fill markers using only `text`'s own bodies. Correct for an IN-PLACE source —
/// a `.nornir/*.md` that is itself the published file, so its markers already hold
/// whatever was last rendered and there is no separate output to consult.
fn rewrite(text: &str, ctx: &Ctx) -> Result<(String, Vec<String>)> {
rewrite_with_fallback(text, ctx, None)
}
/// A marker's identity: its name plus its arguments in a canonical order.
///
/// NOT the raw marker line. The renderer re-emits arguments SORTED, so the same
/// section reads `compare=… winner=… cols=…` in the hand-written source and
/// `cols=… compare=… winner=…` in the generated output. Keying on the raw text
/// therefore never matches across that boundary — which is exactly how the first
/// version of this fallback silently did nothing while looking correct.
fn marker_key(m: &Marker) -> String {
let mut args: Vec<(&String, &String)> = m.args.iter().collect();
args.sort();
let mut k = m.name.clone();
for (a, v) in args {
k.push('\u{1}');
k.push_str(a);
k.push('=');
k.push_str(v);
}
k
}
/// Existing section bodies in `text`, keyed by [`marker_key`]. Best effort: an
/// unparseable previous output yields no fallbacks, so a corrupt README can never
/// block a render.
fn bodies_by_marker(text: &str) -> HashMap<String, String> {
let mut map = HashMap::new();
let Ok(markers) = parse_markers(text) else {
return map;
};
let lines: Vec<&str> = text.split_inclusive('\n').collect();
for (m, _) in &markers {
let body: String = lines[m.line_start + 1..m.line_end].concat();
map.insert(marker_key(m), body);
}
map
}
fn rewrite_with_fallback(
text: &str,
ctx: &Ctx,
prev_output: Option<&str>,
) -> Result<(String, Vec<String>)> {
let markers = parse_markers(text)?;
if markers.is_empty() {
return Ok((text.to_string(), Vec::new()));
}
let fallback = prev_output.map(bodies_by_marker).unwrap_or_default();
let lines: Vec<&str> = text.split_inclusive('\n').collect();
let mut out = String::new();
let mut cursor = 0usize;
let mut sections = Vec::new();
for (m, _) in &markers {
for l in &lines[cursor..m.line_start] {
out.push_str(l);
}
// The section's existing body — everything strictly between the paired
// markers. Handed to `render` so a no-fresh-data render preserves it
// instead of blanking real benchmark tables.
//
// For a MANAGED doc the template's own body is empty BY DESIGN, so fall back
// to the same section in the previously rendered OUTPUT — that is where the
// numbers actually are.
let mut prev_body: String = lines[m.line_start + 1..m.line_end].concat();
if prev_body.trim().is_empty()
&& let Some(prev) = fallback.get(&marker_key(m))
{
prev_body = prev.clone();
}
out.push_str(&render(ctx, m, &prev_body)?);
sections.push(m.name.clone());
cursor = m.line_end + 1;
}
for l in &lines[cursor..] {
out.push_str(l);
}
Ok((out, sections))
}
// ----- renderers -------------------------------------------------------------
/// Legacy flat renderer: one row per (result, metric). Kept so existing
/// `bench` markers keep working; new docs should prefer `benches`.
/// The bolded run-context line shown above a bench table: version · machine ·
/// cores · date. `machine` is recorded from `NORNIR_MACHINE`; omitted when empty.
fn bench_header(run: &BenchRun) -> String {
let mut parts = vec![format!("v{}", run.version)];
if !run.machine.is_empty() {
parts.push(run.machine.clone());
}
parts.push(format!("{} cores", run.cores));
if !run.date.is_empty() {
parts.push(run.date.clone());
}
format!("**{}**\n\n", parts.join(" · "))
}
/// The run a bench section is ALLOWED to render.
///
/// A **preview** run — one whose harness stamped [`super::bench_status::PREVIEW_TOKEN`]
/// into `machine` because its input was under the heavy floor it requires, and so
/// refused to emit a publishable row — is INVISIBLE to an ordinary bench marker.
/// It reads as "no fresh data", which the non-destructive guard in [`render`] turns
/// into "keep the committed table". That is the whole point: a preview pass on a
/// bigger box must never quietly replace measured, published numbers taken on
/// another host. znippy-zoomies' README carries real 12-core `loki` tables; an
/// under-floor 32-core `oden` preview would otherwise have overwritten every one of
/// them with numbers the harness had explicitly declined to publish.
///
/// A section that WANTS the preview (a clearly-labelled preview block) opts in with
/// `preview=yes`. `bench_status` is exempt — stating the refusal is its job.
fn usable_run<'a>(ctx: &'a Ctx, args: &HashMap<String, String>) -> Option<&'a BenchRun> {
let run = ctx.run?;
let opted_in = matches!(
args.get("preview").map(|s| s.as_str()),
Some("yes" | "only" | "allow" | "true")
);
if super::bench_status::is_preview(run) && !opted_in {
return None;
}
Some(run)
}
fn render_bench(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
let Some(run) = usable_run(ctx, args) else {
return Ok(
"_(no bench history yet — run a benchmark to populate this section)_\n".to_string(),
);
};
let mut out = String::new();
out.push_str(&bench_header(run));
if run.results.is_empty() {
out.push_str("_(no bench results)_\n");
return Ok(out);
}
out.push_str("| name | metric | value |\n|------|--------|-------|\n");
let mut rows: Vec<(String, String, f64)> = Vec::new();
for r in &run.results {
for (k, v) in &r.metrics {
if let Some(f) = v.as_f64() {
rows.push((r.name.clone(), k.clone(), f));
}
}
}
rows.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)));
for (n, k, v) in rows {
out.push_str(&format!("| {n} | {k} | {v:.2} |\n"));
}
Ok(out)
}
/// `bench_history` renderer: one row per past bench run (newest first) from the
/// warehouse — `| Version | Date | Machine | <metric> |`. Reads the pre-fetched
/// [`Ctx::history`] (the caller fetches via `query_bench_runs`; renderers must not
/// open the warehouse). Makes the hand-maintained "Benchmark history" tables in
/// the holger/znippy READMEs obsolete.
///
/// Args:
/// - `limit=N` — keep only the N most recent runs (default 20).
/// - `metric=NAME` — show this metric's value per run (matched against each
/// result's metric key or result name); default is the run's max numeric metric.
fn render_bench_history(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
if ctx.history.is_empty() {
return Ok(
"_(no bench history yet — run benchmarks to populate this section)_\n".to_string(),
);
}
let limit: usize = args.get("limit").and_then(|s| s.parse().ok()).unwrap_or(20);
let metric = args.get("metric").map(|s| s.as_str());
let mut runs: Vec<&BenchRun> = ctx.history.iter().collect();
// Newest first by timestamp when present, else by the `date` string.
runs.sort_by(|a, b| {
let ka = a.timestamp.as_deref().unwrap_or(&a.date);
let kb = b.timestamp.as_deref().unwrap_or(&b.date);
kb.cmp(ka)
});
let metric_label = metric.unwrap_or("best metric");
let mut out = String::new();
out.push_str(&format!(
"| Version | Date | Machine | {metric_label} |\n|---|---|---|---|\n"
));
for run in runs.into_iter().take(limit) {
let date = if run.date.is_empty() {
"-"
} else {
run.date.as_str()
};
let machine = if run.machine.is_empty() {
"-"
} else {
run.machine.as_str()
};
let val = best_metric(run, metric)
.map(|v| format!("{v:.2}"))
.unwrap_or_else(|| "-".to_string());
out.push_str(&format!(
"| v{} | {} | {} | {} |\n",
run.version, date, machine, val
));
}
Ok(out)
}
/// Pick a representative metric value from a run: the named `metric` (matched on a
/// result's metric key or the result name) when given, else the max numeric metric.
fn best_metric(run: &BenchRun, metric: Option<&str>) -> Option<f64> {
let mut best: Option<f64> = None;
for r in &run.results {
for (k, v) in &r.metrics {
let Some(f) = v.as_f64() else { continue };
let take = match metric {
Some(name) => k == name || r.name == name,
None => true,
};
if take {
best = Some(best.map_or(f, |b| b.max(f)));
}
}
}
best
}
/// `benches` renderer: a pivot table (one row per result/workload, one column
/// per metric) that **bolds the winning cell** in each row. Cells that share a
/// unit (e.g. `ljar_mbs` + `unzip_mbs`, both `mbs`) form a comparison group;
/// within a group of ≥2 the best value (highest for `High`, lowest for `Low`)
/// is bolded. Direction is inferred from each metric's unit
/// (`nornir_bench::unit_of`); the `best=` marker arg overrides the odd cases.
/// This is what turns paired ours-vs-legacy metrics into a "how much faster"
/// table.
///
/// Optional args:
/// - `name=PAT` — keep only results whose name contains `PAT`.
/// - `exclude=PAT` — drop results whose name contains `PAT` (e.g. `_st`
/// single-core rows from a multi-core headline).
/// - `best=metric:low,other:high` — per-metric direction overrides (for units
/// the suffix convention can't classify, e.g. a `_pct` that's higher-better).
/// - `bold=false` — disable winner-bolding (for tables whose same-unit columns
/// aren't rivals, e.g. archive `compress_mbs` + `decompress_mbs`).
///
/// Compact "best-of mashup" modes (collapse many variants to a few standout
/// rows — for the tight 10-second-readable block at the top of a README):
/// - `mode=compact` — collapse **all** matched rows to a SINGLE synthetic
/// "best-of" row that takes the absurd-best value per column across every
/// variant (highest for `High` metrics, lowest for `Low`; `Neutral` columns
/// are dropped — there's no "best" count to cherry-pick). The row label is
/// `best-of` unless `label=` overrides it. Honours `name=`/`exclude=`/`compare=`
/// first, so `mode=compact compare=skade_s3,nessie,polaris` distils exactly
/// those rivals into one champion line. Combine with `winner=row` to also bold
/// the (single) cell — though with one row every populated cell is the winner.
/// - `top=N` — keep only the N strongest rows, ranked by `rank=METRIC` (defaults
/// to the first throughput-like column, e.g. `ops_sec`/`*_per_sec`; direction
/// inferred from the unit, so latency ranks ascend). Use it to show "the 3
/// fastest backends" without hand-picking. `top=` applies after `compare=`.
///
/// Meaty-first column ranking for the crisp `compare=` mashup: throughput rates
/// lead, then headline latency (p50 → p90 → p99 → p999), then mean/min/max, with
/// everything else in the middle. Lower sorts further left. Ties break
/// alphabetically (see the caller).
fn col_rank(col: &str) -> u8 {
if col == "ops_sec" || col.ends_with("_per_sec") {
0
} else if col.starts_with("p50") {
10
} else if col.starts_with("p90") {
11
} else if col.starts_with("p999") {
13
} else if col.starts_with("p99") {
12
} else if col.starts_with("mean") {
20
} else if col.starts_with("min") {
21
} else if col.starts_with("max") {
22
} else {
15
}
}
/// Does bench-arm `name` match a `compare=`/`self=` directive `token`? A token
/// matches ONLY when it equals the whole arm id OR appears as a WORD-BOUNDED segment
/// of it — delimited on both sides by a non-alphanumeric separator (`_`/`-`) or a
/// string edge — NEVER as a bare letter-adjacent substring.
///
/// This is the H3 fix: the rival compressor names are plain substrings of our arm
/// ids (`bzip2` ⊂ `lbzip2` ⊂ `zoomies-lbzip2`, `jar` ⊂ `zoomies-jar`, `zip` ⊂
/// `zoomies-zip`), so a plain `.contains()` let `compare=bzip2` mis-map the rival
/// row onto ours (the `bzip2` row printed `zoomies-lbzip2`'s number). Word-bounded
/// matching rejects `bzip2` inside `lbzip2` (the `l` is letter-adjacent — no
/// boundary) while still folding a variant family like `skade_s3` →
/// `data_pipe_skade_s3` (the `_` before `skade` IS a boundary), so the skade/holger
/// mashup tables that rely on that folding keep working. No arm is renamed.
fn token_matches(name: &str, token: &str) -> bool {
if token.is_empty() {
return false;
}
if name == token {
return true;
}
let nb = name.as_bytes();
let tb = token.as_bytes();
if tb.len() > nb.len() {
return false;
}
let boundary = |b: Option<&u8>| b.is_none_or(|c| !c.is_ascii_alphanumeric());
let mut i = 0;
while i + tb.len() <= nb.len() {
if &nb[i..i + tb.len()] == tb
&& boundary(i.checked_sub(1).map(|p| &nb[p]))
&& boundary(nb.get(i + tb.len()))
{
return true;
}
i += 1;
}
false
}
fn render_benches(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
use nornir_bench::{MetricDirection, direction_of, unit_of};
let Some(run) = usable_run(ctx, args) else {
return Ok(
"_(no bench history yet — run a benchmark to populate this section)_\n".to_string(),
);
};
let overrides = parse_best_overrides(args.get("best").map(|s| s.as_str()));
// `bold=false` disables winner-bolding — for tables whose same-unit columns
// aren't rivals (e.g. archive `compress_mbs` + `decompress_mbs`).
let bold_enabled = args.get("bold").map(|v| v != "false").unwrap_or(true);
// `winner=` picks the comparison axis:
// `col` (default) — rows are workloads, columns are rivals sharing a unit;
// bold the best cell *within each row* (ours-vs-legacy).
// `row` — rows are rivals (systems/backends) for one workload,
// columns are distinct metrics; bold the best cell *down
// each column* by that metric's direction (which system
// wins each metric). Use for cross-system comparisons.
let winner_by_row = matches!(args.get("winner").map(|s| s.as_str()), Some("row" | "rows"));
// `self=TOK[,TOK...]` — which system(s) are *ours*. Only used to break winner
// ties in ours' favour (see the `winner=row` block); absent ⇒ no preference.
let ours_tokens: Vec<String> = args
.get("self")
.map(|s| {
s.split(',')
.map(str::trim)
.filter(|t| !t.is_empty())
.map(str::to_string)
.collect()
})
.unwrap_or_default();
let filter = args.get("name").map(|s| s.as_str());
let exclude = args.get("exclude").map(|s| s.as_str());
// `suspect=` — what to do with arms [`bench_status::suspect_reasons`] judges
// NOT CREDIBLE as real work (a rate divided by a sub-timing-floor interval; a
// parallel speedup above the core count). `flag` (default) keeps the row and
// marks the name ⚠ with a rendered footnote; `exclude` withholds it entirely
// and says so under the table; `off` restores the unscreened behaviour.
//
// The screen is DERIVED, never a hand-listed `exclude=` of arm names — a hand
// list is a copy of the data and goes stale the next time the bench moves.
let suspect_mode = args.get("suspect").map(|s| s.as_str()).unwrap_or("flag");
let flagged: std::collections::HashMap<&str, Vec<String>> = if suspect_mode == "off" {
std::collections::HashMap::new()
} else {
super::bench_status::suspects(run)
.into_iter()
.map(|(r, w)| (r.name.as_str(), w))
.collect()
};
let mut results: Vec<&nornir_bench::BenchResult> = run
.results
.iter()
.filter(|r| filter.is_none_or(|p| r.name.contains(p)))
.filter(|r| exclude.is_none_or(|p| !r.name.contains(p)))
.filter(|r| suspect_mode != "exclude" || !flagged.contains_key(r.name.as_str()))
.collect();
// What the `exclude` mode actually withheld from THIS table (after `name=` /
// `exclude=` narrowing), so the caveat under the table can never name a row the
// table would not have shown anyway.
let withheld: Vec<(&str, &Vec<String>)> = if suspect_mode == "exclude" {
run.results
.iter()
.filter(|r| filter.is_none_or(|p| r.name.contains(p)))
.filter(|r| exclude.is_none_or(|p| !r.name.contains(p)))
.filter_map(|r| flagged.get(r.name.as_str()).map(|w| (r.name.as_str(), w)))
.collect()
} else {
Vec::new()
};
if results.is_empty() {
return Ok("_(no bench results)_\n".to_string());
}
// `compare=a,b,c` collapses the matches to exactly one row per named backend
// token (first hit, in the given order) — the crisp N-way "mashup" table.
// Use it to fold nornir's nvme/ram/s3 variants down to a single rival row
// for a head-to-head vs Nessie / Polaris.
if let Some(spec) = args.get("compare") {
// Each token claims a DISTINCT row: an EXACT arm-id match first, then a
// WORD-BOUNDED match ([`token_matches`]) — NEVER a bare substring. Exact-first
// lets `compare=zoomies-jar,jar` give each token its own row; the word-bounded
// fallback still folds a variant family (`skade_s3` → `data_pipe_skade_s3`)
// WITHOUT letting `bzip2` steal `lbzip2`/`zoomies-lbzip2` (letter-adjacent, no
// boundary — the H3 mis-map). Put the more specific token first to let it
// claim its row.
let mut used = vec![false; results.len()];
let mut picked: Vec<&nornir_bench::BenchResult> = Vec::new();
for t in spec.split(',').map(str::trim).filter(|t| !t.is_empty()) {
let hit = (0..results.len())
.find(|&i| !used[i] && results[i].name == t)
.or_else(|| {
(0..results.len()).find(|&i| !used[i] && token_matches(&results[i].name, t))
});
if let Some(i) = hit {
used[i] = true;
picked.push(results[i]);
}
}
if !picked.is_empty() {
results = picked;
}
}
// `top=N` — rank the matched rows by a chosen metric (direction-aware) and
// keep only the strongest N. `rank=METRIC` selects the axis; default is the
// first throughput-like column present (`ops_sec` / `*_per_sec`), falling
// back to the first numeric column. Latency metrics (`Low`) rank ascending.
if let Some(n) = args
.get("top")
.and_then(|v| v.parse::<usize>().ok())
.filter(|n| *n > 0)
{
let rank_col = args
.get("rank")
.cloned()
.or_else(|| {
results.iter().find_map(|r| {
r.metrics
.keys()
.find(|k| {
r.metrics[*k].as_f64().is_some()
&& (k.as_str() == "ops_sec" || k.ends_with("_per_sec"))
})
.cloned()
})
})
.or_else(|| {
results.iter().find_map(|r| {
r.metrics
.keys()
.find(|k| r.metrics[*k].as_f64().is_some())
.cloned()
})
});
if let Some(col) = rank_col {
let dir = direction_of(&overrides, &col);
let key = |r: &&nornir_bench::BenchResult| r.metrics.get(&col).and_then(|v| v.as_f64());
results.sort_by(|a, b| {
let (av, bv) = (key(a), key(b));
match (av, bv) {
(Some(x), Some(y)) => match dir {
// Best first: High → descending, else ascending.
MetricDirection::High => y.total_cmp(&x),
_ => x.total_cmp(&y),
},
(Some(_), None) => std::cmp::Ordering::Less,
(None, Some(_)) => std::cmp::Ordering::Greater,
(None, None) => std::cmp::Ordering::Equal,
}
});
results.truncate(n);
}
}
// `mode=compact` — collapse every matched row to ONE synthetic "best-of" row
// that, per column, takes the absurd-best value across all variants (max for
// `High`, min for `Low`; `Neutral` columns are skipped — there's no winning
// count). Built for the tight top-of-README mashup. The owned row is held in
// `compact_holder` so `results` can keep its `&` shape downstream.
let compact_holder: Vec<nornir_bench::BenchResult>;
if args.get("mode").map(|s| s.as_str()) == Some("compact") && !results.is_empty() {
let mut cols: Vec<String> = Vec::new();
for r in &results {
for k in r.metrics.keys() {
if r.metrics[k].as_f64().is_some() && !cols.contains(k) {
cols.push(k.clone());
}
}
}
let mut best = serde_json::Map::new();
for c in &cols {
let dir = direction_of(&overrides, c);
if dir == MetricDirection::Neutral {
continue; // no "best" count to cherry-pick
}
let vals: Vec<f64> = results
.iter()
.filter_map(|r| r.metrics.get(c).and_then(|v| v.as_f64()))
.collect();
let pick = match dir {
MetricDirection::High => vals.iter().copied().reduce(f64::max),
MetricDirection::Low => vals.iter().copied().reduce(f64::min),
MetricDirection::Neutral => None,
};
if let Some(v) = pick {
best.insert(c.clone(), serde_json::Value::from(v));
}
}
let label = args
.get("label")
.cloned()
.unwrap_or_else(|| "best-of".to_string());
compact_holder = vec![nornir_bench::BenchResult {
name: label,
metrics: best,
..Default::default()
}];
results = compact_holder.iter().collect();
}
// Column order: first-seen across results (stable, deterministic).
let mut columns: Vec<String> = Vec::new();
for r in &results {
for k in r.metrics.keys() {
if r.metrics[k].as_f64().is_some() && !columns.contains(k) {
columns.push(k.clone());
}
}
}
// `cols=a,b,c` — explicit ordered whitelist (meaty-first); keep only these,
// in this order. Otherwise, for the crisp `compare=` mashup we (1) drop
// columns that carry no signal — identical across every compared row, e.g. a
// constant `encode_workers` that just echoes the core count already in the
// header — and (2) float the meaty metrics (throughput, then latency) to the
// left. Plain (non-`compare`) tables keep the full alphabetical breakdown.
if let Some(spec) = args.get("cols") {
let want: Vec<&str> = spec
.split(',')
.map(str::trim)
.filter(|t| !t.is_empty())
.collect();
columns.retain(|c| want.iter().any(|w| *w == c));
columns.sort_by_key(|c| want.iter().position(|w| *w == c).unwrap_or(usize::MAX));
} else if args.contains_key("compare") && results.len() >= 2 {
columns.retain(|c| {
let vals: Vec<f64> = results
.iter()
.filter_map(|r| r.metrics.get(c).and_then(|v| v.as_f64()))
.collect();
// Keep unless it's present in every row and constant across them.
!(vals.len() == results.len() && vals.windows(2).all(|w| w[0] == w[1]))
});
columns.sort_by(|a, b| col_rank(a).cmp(&col_rank(b)).then_with(|| a.cmp(b)));
} else {
columns.sort();
}
// `drop=a,b` — remove named columns after selection (data-driven, generic).
// Lets a mashup strip FORMAT-level columns that don't belong in the story,
// e.g. `drop=compress_mbs,ratio` on a *decompressor* mashup keeps only the
// decode metrics. Applied last so it composes with `cols=`/`compare=`.
if let Some(spec) = args.get("drop") {
let unwanted: Vec<&str> = spec
.split(',')
.map(str::trim)
.filter(|t| !t.is_empty())
.collect();
columns.retain(|c| !unwanted.iter().any(|w| *w == c));
}
let mut out = String::new();
out.push_str(&bench_header(run));
// Header.
out.push_str("| workload |");
for c in &columns {
out.push_str(&format!(" {c} |"));
}
out.push('\n');
out.push_str("|---|");
for _ in &columns {
out.push_str("---|");
}
out.push('\n');
// Precompute the set of cells `(row_index, column)` to bold.
let pick_best = |cells: &[(usize, f64)], dir: MetricDirection| -> Option<usize> {
match dir {
MetricDirection::High => cells.iter().max_by(|a, b| a.1.total_cmp(&b.1)),
MetricDirection::Low => cells.iter().min_by(|a, b| a.1.total_cmp(&b.1)),
MetricDirection::Neutral => None,
}
.map(|(i, _)| *i)
};
let mut bold: std::collections::HashSet<(usize, &String)> = std::collections::HashSet::new();
if bold_enabled && winner_by_row {
// Down each column: rivals (systems) compete on one metric. Bold the best
// cell per column by that metric's direction; needs ≥2 rivals to compare.
for c in &columns {
let dir = direction_of(&overrides, c);
if dir == MetricDirection::Neutral {
continue;
}
let cells: Vec<(usize, f64)> = results
.iter()
.enumerate()
.filter_map(|(i, r)| r.metrics.get(c).and_then(|v| v.as_f64()).map(|f| (i, f)))
.collect();
if cells.len() < 2 {
continue;
}
if let Some(default_i) = pick_best(&cells, dir) {
// On a TIE for the best value, favour OURS: if any row tied for the
// winning value is ours, bold that one instead of the arbitrary
// rival `pick_best` (max_by/min_by) happened to land on. With no
// `self=`, or no ours in the tied-best set, keep the default cell.
let best_val = cells
.iter()
.find(|(i, _)| *i == default_i)
.map(|(_, f)| *f)
.unwrap_or(f64::NAN);
let chosen = cells
.iter()
.filter(|(_, f)| f.total_cmp(&best_val) == std::cmp::Ordering::Equal)
.map(|(i, _)| *i)
.find(|i| row_is_ours(&results[*i].name, &ours_tokens))
.unwrap_or(default_i);
bold.insert((chosen, c));
}
}
} else if bold_enabled {
// Across each row: group same-unit columns (rivals); bold the best per group.
for (i, r) in results.iter().enumerate() {
let mut groups: HashMap<&'static str, Vec<(usize, f64)>> = HashMap::new();
let mut col_of: HashMap<usize, &String> = HashMap::new();
for (ci, c) in columns.iter().enumerate() {
if let Some(f) = r.metrics.get(c).and_then(|v| v.as_f64()) {
if let Some(u) = unit_of(c) {
groups.entry(u.name).or_default().push((ci, f));
col_of.insert(ci, c);
}
}
}
for cells in groups.values() {
if cells.len() < 2 {
continue;
}
let dir = direction_of(&overrides, col_of[&cells[0].0]);
if let Some(ci) = pick_best(cells, dir) {
bold.insert((i, col_of[&ci]));
}
}
}
}
// Per-column uniform magnitude: scan each column's max |value| once so every
// cell in the column shares one unit suffix (M/B/T) — no mixing 2.12M with
// 3930. Below a million the column keeps plain, un-separated values (L3/L8).
let col_scale: Vec<Option<(f64, &'static str, usize)>> = columns
.iter()
.map(|c| {
let vals: Vec<f64> = results
.iter()
.filter_map(|r| r.metrics.get(c).and_then(|v| v.as_f64()))
.collect();
column_format(&vals)
})
.collect();
let trophies = hard_broken_trophies(ctx, &results, &ours_tokens);
for (i, r) in results.iter().enumerate() {
let name_cell = if trophies.contains(r.name.as_str()) {
format!("{} 🏆", r.name)
} else if flagged.contains_key(r.name.as_str()) {
// `suspect=flag`: the row stays, but the reader is told before reading it.
format!("{} ⚠", r.name)
} else {
r.name.clone()
};
out.push_str(&format!("| {} |", name_cell));
for (ci, c) in columns.iter().enumerate() {
match r.metrics.get(c).and_then(|v| v.as_f64()) {
Some(f) => {
let cell = fmt_metric_scaled(f, col_scale[ci]);
if bold.contains(&(i, c)) {
out.push_str(&format!(" **{cell}** |"));
} else {
out.push_str(&format!(" {cell} |"));
}
}
None => out.push_str(" |"),
}
}
out.push('\n');
}
// The caveat is rendered from the SAME screen that shaped the table, so a
// published number and its warning can never drift apart.
let shown_flags: Vec<(&str, &Vec<String>)> = results
.iter()
.filter_map(|r| flagged.get(r.name.as_str()).map(|w| (r.name.as_str(), w)))
.collect();
if !shown_flags.is_empty() {
out.push_str(
"\n⚠ **not credible as real work — read as an artefact, not a measurement:**\n\n",
);
for (name, why) in shown_flags {
out.push_str(&format!("- `{name}` — {}\n", why.join("; ")));
}
}
if !withheld.is_empty() {
out.push_str("\n⚠ **withheld from this table — not credible as real work:**\n\n");
for (name, why) in withheld {
out.push_str(&format!("- `{name}` — {}\n", why.join("; ")));
}
}
Ok(out)
}
/// `bench_hero name=PAT fast=TOK slow=TOK [unit=_s] [label=query]` — the "most
/// dramatic" callout. Among the per-item metrics the two backends share (keys
/// like `q01_s`..`q22_s` — i.e. `q<digits><unit>`, lower-better), find the item
/// where `fast` beats `slow` by the biggest ratio and render a one-line headline
/// + a tiny 1-row table. Built for per-query TPC-H across nornir vs a competitor.
fn render_bench_hero(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
let Some(run) = usable_run(ctx, args) else {
return Ok(
"_(no bench history yet — run a benchmark to populate this section)_\n".to_string(),
);
};
let name = args.get("name").map(|s| s.as_str()).unwrap_or("");
let (Some(fast_tok), Some(slow_tok)) = (args.get("fast"), args.get("slow")) else {
return Ok("_(bench_hero needs `fast=` and `slow=` backend tokens)_\n".to_string());
};
let unit = args.get("unit").map(|s| s.as_str()).unwrap_or("_s");
let find = |tok: &str| {
run.results
.iter()
.find(|r| r.name.contains(name) && r.name.contains(tok))
};
let (Some(fast), Some(slow)) = (find(fast_tok), find(slow_tok)) else {
return Ok("_(bench_hero: backend rows not found for this run)_\n".to_string());
};
// (item-key, ratio = slow/fast, fast_seconds, slow_seconds)
let mut best: Option<(String, f64, f64, f64)> = None;
for (k, v) in fast.metrics.iter() {
let Some(stem) = k.strip_suffix(unit) else {
continue;
};
let is_item = stem.len() > 1
&& stem.starts_with('q')
&& stem[1..].bytes().all(|b| b.is_ascii_digit());
if !is_item {
continue;
}
let (Some(fv), Some(sv)) = (v.as_f64(), slow.metrics.get(k).and_then(|x| x.as_f64()))
else {
continue;
};
if fv <= 0.0 {
continue;
}
let ratio = sv / fv;
if best.as_ref().is_none_or(|b| ratio > b.1) {
best = Some((stem.to_string(), ratio, fv, sv));
}
}
let Some((item, ratio, fv, sv)) = best else {
return Ok("_(bench_hero: the two backends share no per-item metrics)_\n".to_string());
};
let label = args.get("label").map(|s| s.as_str()).unwrap_or("query");
let mut out = String::new();
out.push_str(&bench_header(run));
out.push_str(&format!(
"> **Most dramatic {label} — `{item}`: nornir is {ratio:.1}× faster** \
({:.1} ms vs {:.1} ms).\n\n",
fv * 1000.0,
sv * 1000.0
));
out.push_str(&format!(
"| {label} | {} | {} | speedup |\n|---|---|---|---|\n| `{item}` | **{:.1} ms** | {:.1} ms | **{:.1}×** |\n",
fast.name,
slow.name,
fv * 1000.0,
sv * 1000.0,
ratio
));
Ok(out)
}
/// The "system" token of a bench result: the first `_`-delimited segment of its
/// `name`. Results carry no dedicated system field — the identity is embedded in
/// the name (`holger_get_blob`, `nexus_get_blob`, `nornir_embedded_table_exists`),
/// so the leading segment is the system (`holger` / `nexus` / `nornir`). Variants
/// of one system (`nornir_ram`, `nornir_nvme`) collapse to a single token, which
/// is exactly what the mashup wants — one row per system.
fn system_token(name: &str) -> &str {
name.split('_').next().unwrap_or(name)
}
/// The system token honoring the single-thread VARIANT dimension. With
/// `keep_st` `false` this is exactly [`system_token`] — an `_st` variant
/// collapses into its base system (`zoomies-lgz_st` → `zoomies-lgz`), the
/// historical one-row-per-system behaviour. With `keep_st` `true` a
/// single-thread (`_st`) row keeps a DISTINCT token (`zoomies-lgz` vs
/// `zoomies-lgz_st`), so the mashup can render a base system and its
/// single-core variant as TWO rows. The `_st` notion is the SAME one the
/// core-saturation gate exempts — both go through [`nornir_bench`]
/// ([`nornir_bench::is_single_thread`] / [`nornir_bench::strip_single_thread`])
/// so the two subsystems can never drift.
fn system_token_variant(name: &str, keep_st: bool) -> String {
if keep_st && nornir_bench::is_single_thread(name) {
let base = system_token(nornir_bench::strip_single_thread(name));
format!("{base}{}", nornir_bench::SINGLE_THREAD_SUFFIX)
} else {
system_token(name).to_string()
}
}
/// Does this result row belong to *ours* (for winner-tie preference)? Matches when
/// the row's system token equals any `self` token, or shares its leading `-`-family
/// segment — so a single `self=zoomies-lbz2` claims BOTH `zoomies-lbz2` and its
/// sibling `zoomies-lgz` (family `zoomies`). `ours` may be a comma-list of tokens.
/// Empty `ours` ⇒ never ours (preserves the no-`self=` behaviour untouched).
fn row_is_ours(name: &str, ours: &[String]) -> bool {
if ours.is_empty() {
return false;
}
let tok = system_token(name).to_ascii_lowercase();
let fam = tok.split('-').next().unwrap_or(tok.as_str());
ours.iter().any(|o| {
let ol = o.to_ascii_lowercase();
let of = ol.split('-').next().unwrap_or(ol.as_str());
tok == ol || (!of.is_empty() && of == fam)
})
}
/// Every distinct system token present in the run, in first-seen order.
/// `keep_st` opts into the single-thread VARIANT dimension: when `true` an
/// `_st` row surfaces as its own system (distinct from its multi-core base);
/// when `false` (the default) it collapses into the base — see
/// [`system_token_variant`].
fn discover_systems_variant(run: &BenchRun, keep_st: bool) -> Vec<String> {
let mut seen: Vec<String> = Vec::new();
for r in &run.results {
let tok = system_token_variant(&r.name, keep_st);
if !tok.is_empty() && !seen.iter().any(|s| *s == tok) {
seen.push(tok);
}
}
seen
}
/// The system with the most result rows (first-seen wins ties) — the "we bench
/// ourselves across many workloads" fallback for detecting *ours* when neither an
/// explicit `self=` nor the repo/package name resolves to a discovered system.
fn most_frequent_system(run: &BenchRun) -> Option<String> {
let mut counts: Vec<(String, usize)> = Vec::new();
for r in &run.results {
let tok = system_token(&r.name);
if tok.is_empty() {
continue;
}
if let Some(e) = counts.iter_mut().find(|(t, _)| t == tok) {
e.1 += 1;
} else {
counts.push((tok.to_string(), 1));
}
}
let mut best: Option<(String, usize)> = None;
for (t, c) in counts {
if best.as_ref().is_none_or(|b| c > b.1) {
best = Some((t, c));
}
}
best.map(|(t, _)| t)
}
/// The repo's own name for the *ours* heuristic: the `[package] name` in
/// `<repo_root>/Cargo.toml` when present (a workspace-root manifest may have none),
/// falling back to the repo-root directory basename.
fn repo_self_name(ctx: &Ctx) -> String {
let cargo = ctx.repo_root.join("Cargo.toml");
if let Ok(txt) = std::fs::read_to_string(&cargo) {
if let Ok(val) = txt.parse::<toml::Table>().map(toml::Value::Table) {
if let Some(n) = val
.get("package")
.and_then(|p| p.get("name"))
.and_then(|n| n.as_str())
{
return n.to_string();
}
}
}
ctx.repo_root
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("")
.to_string()
}
/// Map a wanted token (from `self=`) onto a discovered system when they overlap
/// case-insensitively, else return it verbatim so `compare=` can still match it.
fn resolve_token(want: &str, systems: &[String]) -> String {
let wl = want.to_ascii_lowercase();
systems
.iter()
.find(|t| {
let tl = t.to_ascii_lowercase();
tl == wl || tl.contains(&wl) || wl.contains(&tl)
})
.cloned()
.unwrap_or_else(|| want.to_string())
}
/// Auto-detect *ours* among the discovered systems. Priority:
/// 1. explicit `self=TOKEN` override (resolved onto a discovered system);
/// 2. the system whose token overlaps the repo/package name (case-insensitive,
/// either-contains — so package `holger` ↔ system `holger`, `nornir` ↔
/// `nornir_embedded`);
/// 3. fallback: the most-frequent system (we bench ourselves across many
/// workloads). Failure mode: if a rival happens to carry more rows than ours
/// *and* the repo name doesn't match any token, ours is mis-guessed — pin it
/// with `self=`.
fn detect_ours(
ctx: &Ctx,
run: &BenchRun,
systems: &[String],
args: &HashMap<String, String>,
) -> String {
if let Some(s) = args.get("self") {
return resolve_token(s, systems);
}
let repo = repo_self_name(ctx).to_ascii_lowercase();
if !repo.is_empty() {
if let Some(t) = systems.iter().find(|t| {
let tl = t.to_ascii_lowercase();
tl == repo || tl.contains(&repo) || repo.contains(&tl)
}) {
return t.clone();
}
}
most_frequent_system(run).unwrap_or_else(|| systems[0].clone())
}
/// The single biggest-diff pairwise story: across every metric ours and a rival
/// share, the (rival, metric) where ours wins by the largest ratio (direction-
/// aware — throughput ratios ours/rival, latency rival/ours). Mirrors the max-
/// ratio tracking of [`render_bench_hero`], generalised past its `q<n>_s` items to
/// any metric and any rival. `None` when ours never wins (or there are no rivals).
struct MashupStory {
token: String,
metric: String,
ratio: f64,
ours_val: f64,
rival_val: f64,
}
fn biggest_story(
ours: &nornir_bench::BenchResult,
rivals: &[(String, &nornir_bench::BenchResult)],
overrides: &HashMap<String, nornir_bench::MetricDirection>,
only_metric: Option<&str>,
) -> Option<MashupStory> {
use nornir_bench::{MetricDirection, direction_of};
let mut best: Option<MashupStory> = None;
for (k, v) in ours.metrics.iter() {
if let Some(m) = only_metric {
if k != m {
continue;
}
}
let Some(ov) = v.as_f64() else { continue };
if ov <= 0.0 {
continue;
}
let higher_better = match direction_of(overrides, k) {
MetricDirection::High => true,
MetricDirection::Low => false,
MetricDirection::Neutral => continue,
};
for (tok, rr) in rivals {
let Some(rv) = rr.metrics.get(k).and_then(|x| x.as_f64()) else {
continue;
};
if rv <= 0.0 {
continue;
}
let ratio = if higher_better { ov / rv } else { rv / ov };
if ratio <= 1.0 {
continue; // ours must actually win this pairing
}
if best.as_ref().is_none_or(|b| ratio > b.ratio) {
best = Some(MashupStory {
token: tok.clone(),
metric: k.clone(),
ratio,
ours_val: ov,
rival_val: rv,
});
}
}
}
best
}
/// `mashup` renderer — the **zero-config** top-of-README comparison table. Unlike
/// `benches`, it takes NO required args: it AUTO-DISCOVERS the repo's own system
/// (*ours*) and EVERY rival system from the latest [`BenchRun`], then renders the
/// crisp N-way table (effectively `benches compare=<ours+rivals> winner=row` with
/// meaty-first columns — throughput, then p50→p99 latency — and ours' winning
/// cells bolded). Above the table it surfaces the single biggest-diff pairwise
/// story (max-ratio, à la `bench_hero`) when a clear standout exists.
///
/// All args are optional:
/// - `self=TOKEN` — pin *ours* instead of guessing from the repo/package name.
/// - `exclude=PAT` — drop rival systems whose token contains `PAT`.
/// - `metric=KEY` — restrict the table to (and headline to) a single metric column.
/// - `cols=a,b,c` — explicit ordered column whitelist (scope the table to these
/// metrics only, e.g. decode metrics in a bake-off); does not scope the headline.
/// - `top=N` — keep only the N strongest systems (ranked as in `benches`).
/// - `best=metric:dir` — per-metric direction overrides (passed through).
///
/// Empty/absent bench data → a graceful placeholder, matching the other bench
/// sections (never a panic/error).
/// True if `token` matches any of the comma-separated `exclude` patterns
/// (case-insensitive substring). Lets a marker drop several rival systems at
/// once, e.g. `exclude=zstd,lz4,xz` — the opponents of a decompressor are only
/// the reference tools of the formats it reimplements, not unrelated codecs.
fn is_excluded(token: &str, exclude: Option<&str>) -> bool {
let tl = token.to_ascii_lowercase();
exclude.is_some_and(|pats| {
pats.split(',')
.map(str::trim)
.filter(|p| !p.is_empty())
.any(|p| tl.contains(&p.to_ascii_lowercase()))
})
}
fn render_mashup(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
let Some(run) = usable_run(ctx, args) else {
return Ok(
"_(no bench history yet — run a benchmark to populate this section)_\n".to_string(),
);
};
if run.results.is_empty() {
return Ok("_(no bench results)_\n".to_string());
}
let exclude = args.get("exclude").map(|s| s.as_str());
// VARIANT DIMENSION (`variant=st` / `variant=cores` / `variant=true`): keep a
// single-thread (`_st`) bench a DISTINCT mashup row from its multi-core base
// system, so a single-core-vs-multi-core story (znippy-zoomies "lens B":
// `zoomies-lgz_st` vs `gzip`) can be a grouped row. OFF by default — `_st`
// collapses into its base system (one row), the historical behaviour. The
// `_st` marker is the SAME one the core-saturation gate exempts.
let keep_st = args
.get("variant")
.map(|v| {
let v = v.trim().to_ascii_lowercase();
!matches!(v.as_str(), "" | "false" | "none" | "off" | "0" | "no")
})
.unwrap_or(false);
// (b) auto-discover every distinct ROW-system (`<system>_<workload>` scheme),
// then drop excluded rivals.
let systems: Vec<String> = discover_systems_variant(run, keep_st)
.into_iter()
.filter(|t| !is_excluded(t, exclude))
.collect();
// The row-wise scheme only applies when *ours* is actually one of the row
// systems (a `self=` pin, or the repo name matching a row prefix) AND there are
// ≥2 systems to compare. Otherwise the "systems" are really workload names
// (holger's `java_jar`, `pip_wheel`, …) and the rivals live in COLUMNS — fall
// through to the column-wise scheme.
let repo = repo_self_name(ctx).to_ascii_lowercase();
let ours_is_row_system = args.contains_key("self")
|| systems.iter().any(|t| {
let tl = t.to_ascii_lowercase();
!repo.is_empty() && (tl == repo || tl.contains(&repo) || repo.contains(&tl))
});
if ours_is_row_system && systems.len() >= 2 {
// GROUPED PAIRING (`grouped` / `pairing=grouped`): instead of one flat
// N-way table, pair each of OUR codecs against its DIRECT rival by format
// and emit one sub-table per format group (e.g. group "gzip": our
// `zoomie-lgz` vs `gzip`; group "bzip2": our `zoomie-lbzip2` vs `bzip2`).
// Pairing is data-driven (longest-common-substring of the codec names) —
// no repo-specific hardcoding.
let grouped = args.get("grouped").map(|v| v != "false").unwrap_or(false)
|| matches!(
args.get("pairing").map(|s| s.as_str()),
Some("grouped" | "group" | "pairs" | "format")
);
if grouped {
if let Some(s) = render_mashup_grouped(ctx, args, run, &systems)? {
return Ok(s);
}
}
return render_mashup_rowwise(ctx, args, run, &systems);
}
// (B) COLUMN-WISE: rivals are column prefixes sharing a metric suffix
// (`holger_served_mbs` vs `nexus_served_mbs` vs `artifactory_served_mbs`).
if let Some(s) = render_mashup_columnwise(ctx, args, run, exclude)? {
return Ok(s);
}
// (C) SINGLE-SYSTEM: no rival persisted anywhere — a clean headline line, not a
// lonely raw row (the full matrix, if any, lives in README-full).
Ok(render_mashup_solo(run))
}
/// The original ROW-WISE mashup: rows are `<system>_<workload>`, *ours* is one of
/// them, and every rival is a sibling row sharing a metric column. Delegates the
/// pivot to `render_benches compare=<ours+rivals> winner=row`.
fn render_mashup_rowwise(
ctx: &Ctx,
args: &HashMap<String, String>,
run: &nornir_bench::BenchRun,
systems: &[String],
) -> Result<String> {
// (a) auto-detect ours, then order ours-first + rivals in discovery order.
let ours = detect_ours(ctx, run, systems, args);
let mut order: Vec<String> = Vec::new();
if systems.iter().any(|s| *s == ours) {
order.push(ours.clone());
}
for s in systems {
if !order.iter().any(|o| o == s) {
order.push(s.clone());
}
}
// (c) delegate the pivot table to `render_benches` — the N-way `compare=` +
// `winner=row` mashup, with its meaty-first column ranking and winner bolding.
// No rendering logic is duplicated here.
let mut bargs: HashMap<String, String> = HashMap::new();
bargs.insert("compare".into(), order.join(","));
bargs.insert("winner".into(), "row".into());
// Tell the pivot which row is *ours* so winner-ties bold ours, not a rival.
// `detect_ours` resolved a single token (e.g. `zoomies-lbz2`); `row_is_ours`
// widens it to the whole `-`-family (`zoomies-lbz2` + `zoomies-lgz`).
bargs.insert("self".into(), ours.clone());
// Column scoping: `cols=a,b,c` is an explicit ordered whitelist (e.g. scope a
// decode bake-off to its decode metrics, dropping format-level columns);
// `metric=KEY` is the single-column shorthand. `cols=` wins when both appear.
// `metric=` additionally scopes the headline below; `cols=` does not.
if let Some(c) = args.get("cols") {
bargs.insert("cols".into(), c.clone());
} else if let Some(m) = args.get("metric") {
bargs.insert("cols".into(), m.clone()); // single meaty column
}
if let Some(t) = args.get("top") {
bargs.insert("top".into(), t.clone());
}
if let Some(b) = args.get("best") {
bargs.insert("best".into(), b.clone());
}
let table = render_benches(ctx, &bargs)?;
// (d) the single biggest-diff headline — reuse the max-ratio logic across the
// same one-row-per-system set `compare=` picks (first name-containing hit).
let overrides = parse_best_overrides(args.get("best").map(|s| s.as_str()));
let pick = |tok: &str| run.results.iter().find(|r| r.name.contains(tok));
let only_metric = args.get("metric").map(|s| s.as_str());
let mut headline = String::new();
if let Some(ours_row) = pick(&ours) {
let rivals: Vec<(String, &nornir_bench::BenchResult)> = order
.iter()
.filter(|t| **t != ours)
.filter_map(|t| pick(t).map(|r| (t.clone(), r)))
.collect();
if let Some(s) = biggest_story(ours_row, &rivals, &overrides, only_metric) {
headline = format!(
"> **Biggest gap — `{}`: {} leads {} by {:.1}×** ({} vs {}).\n\n",
s.metric,
ours,
s.token,
s.ratio,
fmt_metric(s.ours_val),
fmt_metric(s.rival_val),
);
}
}
Ok(format!("{headline}{table}"))
}
/// Length of the longest common (contiguous) substring of two strings, comparing
/// only lowercase alphanumerics (separators/case ignored). The data-driven
/// pairing key: `zoomie-lgz` ↔ `gzip` share `gz` (2); `zoomie-lbzip2` ↔ `bzip2`
/// share `bzip2` (5); `zoomie-lgz` ↔ `bzip2` share only `z` (1) — so each of our
/// codecs snaps to its true format rival, not a spurious one.
fn common_run(a: &str, b: &str) -> usize {
let norm = |s: &str| -> Vec<u8> {
s.bytes()
.filter(|c| c.is_ascii_alphanumeric())
.map(|c| c.to_ascii_lowercase())
.collect()
};
let (a, b) = (norm(a), norm(b));
if a.is_empty() || b.is_empty() {
return 0;
}
let mut best = 0usize;
let mut prev = vec![0usize; b.len() + 1];
for i in 1..=a.len() {
let mut cur = vec![0usize; b.len() + 1];
for j in 1..=b.len() {
if a[i - 1] == b[j - 1] {
cur[j] = prev[j - 1] + 1;
best = best.max(cur[j]);
}
}
prev = cur;
}
best
}
/// GROUPED-PAIRING mashup: snap each of OUR codecs to its single closest rival
/// (by [`common_run`]), then GROUP our codecs by that rival and render one
/// sub-table per format group. Usually 2 rows (ours + rival), but when several
/// of ours snap to the same rival — notably a base system AND its single-thread
/// `_st` variant (`variant=st`), both snapping to the same format tool — they
/// share ONE sub-table so the single-core and multi-core rows sit together under
/// their format. `ours` is the detected family; rivals are every other discovered
/// system. A group is emitted only when the best match clears a 2-char shared run
/// (so unrelated systems don't get force-paired). Returns `None` (caller falls
/// back to the flat row-wise table) when no real group forms. Each sub-table
/// reuses [`render_benches`] (`compare=ours…,rival winner=row self=ours`) so winner
/// bolding + tie-favour-ours + `cols=`/`drop=`/`metric=` scoping all apply
/// per-group unchanged.
fn render_mashup_grouped(
ctx: &Ctx,
args: &HashMap<String, String>,
run: &nornir_bench::BenchRun,
systems: &[String],
) -> Result<Option<String>> {
let ours = detect_ours(ctx, run, systems, args);
let ours_family: Vec<String> = vec![ours.clone()];
// Partition discovered systems into ours (family match) and rivals.
let (mine, rivals): (Vec<&String>, Vec<&String>) =
systems.iter().partition(|s| row_is_ours(s, &ours_family));
if mine.is_empty() || rivals.is_empty() {
return Ok(None);
}
// Pair each of our codecs to its best rival (max shared run, ≥2 chars), then
// GROUP our codecs by that rival: all of ours snapping to one rival (e.g. a
// base `zoomies-lgz` AND its single-thread `zoomies-lgz_st` variant, which
// both snap to `gzip`) share ONE format sub-table — the single-core and
// multi-core rows sit together under their format, not in duplicate headers.
// A rival may back more than one of ours only when it genuinely wins the run
// for each — deterministic, data-driven, no 1:1 assumption baked in.
let mut groups: Vec<(String, Vec<String>)> = Vec::new(); // (rival/header, ours…)
for m in &mine {
let mut best: Option<(usize, &String)> = None;
for rv in &rivals {
let run_len = common_run(m, rv);
if best.as_ref().is_none_or(|(l, _)| run_len > *l) {
best = Some((run_len, rv));
}
}
if let Some((len, rv)) = best {
if len >= 2 {
// Header names the format = the rival token (e.g. `gzip`, `bzip2`).
match groups.iter_mut().find(|(h, _)| h.as_str() == rv.as_str()) {
Some((_, ours_here)) => ours_here.push((*m).clone()),
None => groups.push(((*rv).clone(), vec![(*m).clone()])),
}
}
}
}
if groups.is_empty() {
return Ok(None);
}
// Render one sub-table per group, reusing the pivot for each ours-vs-rival set.
let mut out = String::new();
for (rival_tok, mine_toks) in &groups {
let header = rival_tok;
// `compare=` claims one DISTINCT row per token (first unused hit, in token
// order), so a token that is a substring of another must come FIRST to
// claim its own row. Order ours by descending length — the more specific
// `zoomies-lgz_st` precedes the base `zoomies-lgz` it contains — then the
// rival last. `self=` widens to the whole family, so any of ours bolds.
let mut ordered = mine_toks.clone();
ordered.sort_by(|a, b| b.len().cmp(&a.len()).then_with(|| a.cmp(b)));
let self_tok = ordered.first().cloned().unwrap_or_default();
let mut compare = ordered;
compare.push(rival_tok.clone());
let mut bargs: HashMap<String, String> = HashMap::new();
bargs.insert("compare".into(), compare.join(","));
bargs.insert("winner".into(), "row".into());
bargs.insert("self".into(), self_tok);
// Column scoping / format-col dropping pass straight through per group.
for k in ["cols", "metric", "drop", "best", "top"] {
if let Some(v) = args.get(k) {
let key = if k == "metric" && !args.contains_key("cols") {
"cols"
} else {
k
};
bargs.insert(key.into(), v.clone());
}
}
let table = render_benches(ctx, &bargs)?;
// Strip the per-run bench header from all but the first group so the
// grouped block reads as one section with per-format sub-headers.
let table = if out.is_empty() {
table
} else {
strip_bench_header(&table)
};
out.push_str(&format!("**{header}**\n\n{table}\n"));
}
Ok(Some(out))
}
/// Drop a leading `bench_header` block (the italic `_run …_` provenance line plus
/// its trailing blank line) from a rendered table, keeping only the markdown
/// table — used so grouped sub-tables share one header instead of repeating it.
fn strip_bench_header(table: &str) -> String {
// The table proper starts at the first pipe-row (`| workload |`).
match table.find("\n| ") {
Some(idx) => table[idx + 1..].to_string(),
None => table.to_string(),
}
}
/// Split a column key into `(system, suffix)` given the detected rival systems:
/// a leading `<system>_` prefix that names a discovered system peels off (so
/// `nexus_served_mbs` → `("nexus", "served_mbs")`); anything else belongs to
/// *ours*, with the whole key as the suffix (so a bare `ops_per_sec` → `(ours,
/// "ops_per_sec")` and aligns with a rival's `artifactory_ops_per_sec`).
fn classify_column<'a>(
col: &'a str,
systems: &std::collections::BTreeSet<String>,
ours: &str,
) -> (String, &'a str) {
if let Some((head, tail)) = col.split_once('_') {
if systems.iter().any(|s| s == head) {
return (head.to_string(), tail);
}
}
(ours.to_string(), col)
}
/// COLUMN-WISE mashup — the scheme holger uses: rows are workloads and the rivals
/// live in **columns** that share a metric suffix across a system prefix
/// (`holger_served_mbs` / `nexus_served_mbs` / `artifactory_served_mbs`). We
/// (1) discover systems from prefixes that share a suffix, (2) keep only the
/// columns whose suffix is contested by ≥2 systems, (3) keep only the workload
/// rows that carry a rival's column (dropping self-only throughput + internal
/// micro-benchmarks), then render the tight ours-vs-rivals table + the biggest
/// same-suffix win as the headline. Returns `None` when no column-wise rivalry
/// exists (so the caller can fall through to the single-system line).
fn render_mashup_columnwise(
ctx: &Ctx,
args: &HashMap<String, String>,
run: &nornir_bench::BenchRun,
exclude: Option<&str>,
) -> Result<Option<String>> {
use nornir_bench::{MetricDirection, direction_of};
use std::collections::{BTreeMap, BTreeSet};
let ours = repo_self_name(ctx).to_ascii_lowercase();
if ours.is_empty() {
return Ok(None);
}
// Every numeric column across the run, first-seen order.
let mut cols: Vec<String> = Vec::new();
for r in &run.results {
for (k, v) in &r.metrics {
if v.as_f64().is_some() && !cols.contains(k) {
cols.push(k.clone());
}
}
}
// (1) Detect rival systems. A first-segment is a *system* only when it prefixes
// ≥2 distinct metric suffixes AND contests at least one suffix with another
// such prefix. This separates real system names (holger/nexus/artifactory,
// each spanning several metrics) from metric-name fragments that merely
// share a unit word — `mb_per_sec` & `ops_per_sec` split to heads `mb`/`ops`
// over the SAME tail `per_sec`, but neither prefixes a second suffix, so
// neither is a system.
let mut head_tails: BTreeMap<&str, BTreeSet<&str>> = BTreeMap::new();
for c in &cols {
if let Some((head, tail)) = c.split_once('_') {
head_tails.entry(head).or_default().insert(tail);
}
}
let candidates: BTreeSet<&str> = head_tails
.iter()
.filter(|(_, t)| t.len() >= 2)
.map(|(h, _)| *h)
.collect();
let mut tail_cands: BTreeMap<&str, BTreeSet<&str>> = BTreeMap::new();
for h in &candidates {
for t in &head_tails[h] {
tail_cands.entry(*t).or_default().insert(*h);
}
}
let mut systems: BTreeSet<String> = BTreeSet::new();
for hs in tail_cands.values() {
if hs.len() >= 2 {
for h in hs {
systems.insert((*h).to_string());
}
}
}
// Drop excluded rivals (never ours). `exclude=` is a comma list.
systems.retain(|s| s.eq_ignore_ascii_case(&ours) || !is_excluded(s, exclude));
let has_rival = systems.iter().any(|s| !s.eq_ignore_ascii_case(&ours));
if !has_rival {
return Ok(None);
}
// (2) Group columns by suffix; a suffix contested by ≥2 systems is a rival group.
let mut suffix_systems: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
for c in &cols {
let (sys, suf) = classify_column(c, &systems, &ours);
suffix_systems
.entry(suf.to_string())
.or_default()
.insert(sys);
}
// A suffix is shown only when it is contested by ≥2 systems AND *ours* is one of
// them — i.e. a real head-to-head where our column can crush the rivals'. A
// suffix carried only by rivals (no ours column) is not a comparison we win, so
// it's dropped. `ours` is the repo/package name — nothing here is repo-specific.
let rival_suffixes: BTreeSet<String> = suffix_systems
.iter()
.filter(|(_, sys)| sys.len() >= 2 && sys.iter().any(|s| s.eq_ignore_ascii_case(&ours)))
.map(|(s, _)| s.clone())
.collect();
if rival_suffixes.is_empty() {
return Ok(None);
}
let overrides = parse_best_overrides(args.get("best").map(|s| s.as_str()));
// Order systems ours-first, then rivals; order the contested metrics meaty-first.
let mut ordered: Vec<String> = Vec::new();
if systems.iter().any(|s| s.eq_ignore_ascii_case(&ours)) {
ordered.push(ours.clone());
}
for s in &systems {
if !s.eq_ignore_ascii_case(&ours) {
ordered.push(s.clone());
}
}
let mut sufs: Vec<String> = rival_suffixes.iter().cloned().collect();
sufs.sort_by(|a, b| col_rank(a).cmp(&col_rank(b)).then_with(|| a.cmp(b)));
// Transpose the column-wise data into a crisp SYSTEMS-as-rows table: one row per
// system, one column per contested metric (the same shape as the row-wise
// mashup). For each (system, metric) take the best value — but ONLY over rows
// where a RIVAL competes on that metric, so ours' number is drawn from the same
// workloads as the rivals' (never inflated by a self-only workload with no rival).
let mut sys_rows: Vec<nornir_bench::BenchResult> = Vec::new();
for sys in &ordered {
let mut m = serde_json::Map::new();
for suf in &sufs {
let dir = direction_of(&overrides, suf);
let mut best: Option<f64> = None;
for r in &run.results {
let contested_here = r.metrics.iter().any(|(k, v)| {
v.as_f64().is_some() && {
let (cs, cf) = classify_column(k, &systems, &ours);
cf == suf.as_str() && !cs.eq_ignore_ascii_case(&ours)
}
});
if !contested_here {
continue;
}
for (k, v) in &r.metrics {
if let Some(f) = v.as_f64() {
let (cs, cf) = classify_column(k, &systems, &ours);
if cf == suf.as_str() && cs.eq_ignore_ascii_case(sys) {
best = Some(match dir {
MetricDirection::Low => best.map_or(f, |b| b.min(f)),
_ => best.map_or(f, |b| b.max(f)),
});
}
}
}
}
if let Some(b) = best {
m.insert(suf.clone(), serde_json::Value::from(b));
}
}
if !m.is_empty() {
sys_rows.push(nornir_bench::BenchResult {
name: sys.clone(),
metrics: m,
..Default::default()
});
}
}
// Need ours + ≥1 rival with data to be a comparison.
if sys_rows.len() < 2 || !sys_rows.iter().any(|r| r.name.eq_ignore_ascii_case(&ours)) {
return Ok(None);
}
// Render the systems×metrics pivot via `render_benches winner=row` (rows are
// systems, columns are metrics; bold the best system down each metric column) on
// a synthetic run that carries the real run's header metadata.
let synth = nornir_bench::BenchRun {
// A SYNTHETIC run for rendering only — it is never persisted, so it
// carries the source run's id rather than minting one. Two rows with
// one id would be a lie about identity.
id: run.id,
machine_facts: run.machine_facts.clone(),
date: run.date.clone(),
timestamp: run.timestamp.clone(),
version: run.version.clone(),
machine: run.machine.clone(),
cores: run.cores,
results: sys_rows.clone(),
tests: vec![],
};
let sctx = Ctx {
run: Some(&synth),
..*ctx
};
let mut bargs: HashMap<String, String> = HashMap::new();
bargs.insert("winner".into(), "row".into());
bargs.insert("cols".into(), sufs.join(","));
let table = render_benches(&sctx, &bargs)?;
// Headline: the biggest ours-vs-rival ratio across the contested metrics.
let ours_row = sys_rows.iter().find(|r| r.name.eq_ignore_ascii_case(&ours));
let mut best: Option<(String, String, f64, f64, f64)> = None; // suffix, rival, ratio, ours, rival
if let Some(orow) = ours_row {
for suf in &sufs {
let Some(ov) = orow.metrics.get(suf).and_then(|v| v.as_f64()) else {
continue;
};
if ov <= 0.0 {
continue;
}
let higher = match direction_of(&overrides, suf) {
MetricDirection::High => true,
MetricDirection::Low => false,
MetricDirection::Neutral => continue,
};
for rr in &sys_rows {
if rr.name.eq_ignore_ascii_case(&ours) {
continue;
}
let Some(rv) = rr.metrics.get(suf).and_then(|v| v.as_f64()) else {
continue;
};
if rv <= 0.0 {
continue;
}
let ratio = if higher { ov / rv } else { rv / ov };
if ratio <= 1.0 {
continue;
}
if best.as_ref().is_none_or(|b| ratio > b.2) {
best = Some((suf.clone(), rr.name.clone(), ratio, ov, rv));
}
}
}
}
let headline = match best {
Some((suf, rsys, ratio, ov, rv)) => format!(
"> **Biggest gap — `{}`: {} leads {} by {:.1}×** ({} vs {}).\n\n",
suf,
ours,
rsys,
ratio,
fmt_metric(ov),
fmt_metric(rv),
),
None => String::new(),
};
Ok(Some(format!("{headline}{table}")))
}
/// SINGLE-SYSTEM mashup — no rival persisted (e.g. nornir's latest run is its own
/// `dep_graph_build`). Emit a clean one-line headline of *ours'* meatiest metrics
/// rather than a lonely raw table; the competitive matrix, if any, lives in
/// README-full.
fn render_mashup_solo(run: &nornir_bench::BenchRun) -> String {
// The row carrying the most numeric metrics is the representative self-bench.
let Some(row) = run
.results
.iter()
.max_by_key(|r| r.metrics.values().filter(|v| v.as_f64().is_some()).count())
else {
return "_(no bench results)_\n".to_string();
};
// Meaty-first: throughput/latency columns lead (col_rank), then the rest.
let mut keys: Vec<&String> = row
.metrics
.keys()
.filter(|k| row.metrics[*k].as_f64().is_some())
.collect();
keys.sort_by(|a, b| col_rank(a).cmp(&col_rank(b)).then_with(|| a.cmp(b)));
let bits: Vec<String> = keys
.iter()
.take(3)
.filter_map(|k| {
row.metrics[*k]
.as_f64()
.map(|f| format!("**{}** {}", fmt_metric(f), k))
})
.collect();
let header = bench_header(run);
if bits.is_empty() {
return format!("{header}_(single-system bench — no rival persisted yet.)_\n");
}
format!(
"{header}`{}` — {} _(single-system bench; competitive matrix in [`README-full`](.nornir/README-full.md))_\n",
row.name,
bits.join(" · "),
)
}
/// `bench_chart` renderer: a horizontal bar chart comparing one metric across
/// the matching workloads/systems — the visual counterpart to a `benches` table,
/// for the "featured mashup" at the top of a README (e.g. nornir vs nessie vs
/// polaris throughput). The winner (best by the metric's direction) is highlighted.
///
/// Args:
/// - `metric=KEY` — which metric column to chart (required).
/// - `name=PAT` — only workloads whose name contains `PAT`.
/// - `exclude=PAT` — drop workloads whose name contains `PAT`.
/// - `title=TEXT` — chart title (default: the metric name).
/// - `dir=high|low` — winner direction override (default: inferred from the unit).
/// - `log=true` — log-scale bar widths (default: auto when the spread >100×).
/// - `file=NAME` — asset basename under `.nornir/assets/` (default: derived).
///
/// With `export`: writes an SVG under `.nornir/assets/` and emits a markdown
/// image link. Without it: falls back to a unicode-bar text block so the section
/// still conveys the comparison.
fn render_bench_chart(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
use nornir_bench::{MetricDirection, direction_of};
let Some(run) = usable_run(ctx, args) else {
return Ok(
"_(no bench history yet — run a benchmark to populate this section)_\n".to_string(),
);
};
let metric = args
.get("metric")
.map(|s| s.as_str())
.ok_or_else(|| anyhow!("bench_chart requires a `metric=` arg"))?;
let filter = args.get("name").map(|s| s.as_str());
let exclude = args.get("exclude").map(|s| s.as_str());
// (workload, value) for every matching result that carries this metric.
let mut points: Vec<(String, f64)> = run
.results
.iter()
.filter(|r| filter.is_none_or(|p| r.name.contains(p)))
.filter(|r| exclude.is_none_or(|p| !r.name.contains(p)))
.filter_map(|r| {
r.metrics
.get(metric)
.and_then(|v| v.as_f64())
.map(|f| (r.name.clone(), f))
})
.collect();
if points.is_empty() {
return Ok(format!("_(no bench results carry metric `{metric}`)_\n"));
}
// Winner direction: explicit `dir=` wins, else the `best=` override, else the
// unit's natural direction (ops/sec → high, latency → low).
let overrides = parse_best_overrides(args.get("best").map(|s| s.as_str()));
let dir = match args.get("dir").map(|s| s.to_ascii_lowercase()) {
Some(d) if d == "high" || d == "max" => MetricDirection::High,
Some(d) if d == "low" || d == "min" => MetricDirection::Low,
_ => direction_of(&overrides, metric),
};
// Sort so the winner is on top (best first); ties keep input order.
match dir {
MetricDirection::High => points.sort_by(|a, b| b.1.total_cmp(&a.1)),
MetricDirection::Low => points.sort_by(|a, b| a.1.total_cmp(&b.1)),
MetricDirection::Neutral => {}
}
let best_val = match dir {
MetricDirection::High => points.iter().map(|p| p.1).fold(f64::MIN, f64::max),
MetricDirection::Low => points.iter().map(|p| p.1).fold(f64::MAX, f64::min),
MetricDirection::Neutral => f64::NAN,
};
let title = args
.get("title")
.cloned()
.unwrap_or_else(|| format!("{metric} by workload"));
let unit = friendly_unit(metric);
// Auto log scale when the spread exceeds 100× (embedded-vs-REST throughput).
let max_v = points.iter().map(|p| p.1).fold(f64::MIN, f64::max);
let min_pos = points
.iter()
.map(|p| p.1)
.filter(|v| *v > 0.0)
.fold(f64::MAX, f64::min);
let log = match args.get("log").map(|s| s.as_str()) {
Some("true") => true,
Some("false") => false,
_ => min_pos.is_finite() && max_v > 0.0 && max_v / min_pos > 100.0,
};
#[cfg(feature = "export")]
{
let bars: Vec<crate::svg::Bar> = points
.iter()
.map(|(label, v)| crate::svg::Bar {
label: label.clone(),
value: *v,
best: dir != MetricDirection::Neutral && *v == best_val,
})
.collect();
let chart = crate::svg::BarChart {
title: title.clone(),
unit: unit.clone(),
bars,
log,
};
let base = args
.get("file")
.cloned()
.unwrap_or_else(|| format!("bench_{}", sanitize_asset(metric)));
let rel = std::path::PathBuf::from(format!(".nornir/assets/{base}.svg"));
let abs = ctx.repo_root.join(&rel);
// Markdown-only (pre-commit hook): reuse the committed chart asset.
if !ctx.no_assets {
crate::svg::render_bars_to_file(&chart, &abs)?;
}
// Alt text: strip the only chars that would break ``.
let alt = title.replace(['[', ']'], "");
return Ok(format!("\n", alt, rel.display()));
}
#[cfg(not(feature = "export"))]
{
// Text fallback: a unicode bar per workload, winner marked with ★.
let _ = (log, best_val);
let mut out = format!("**{title}**\n\n");
let max = points
.iter()
.map(|p| p.1)
.fold(f64::MIN, f64::max)
.max(f64::MIN_POSITIVE);
let scale = column_format(&points.iter().map(|p| p.1).collect::<Vec<_>>());
for (label, v) in &points {
let filled = ((v / max).clamp(0.0, 1.0) * 24.0).round() as usize;
let bar: String = "█".repeat(filled);
let star = if dir != MetricDirection::Neutral && *v == best_val {
" ★"
} else {
""
};
let unit_sfx = if unit.is_empty() {
String::new()
} else {
format!(" {unit}")
};
out.push_str(&format!(
"- `{label}` {bar} {}{}{star}\n",
fmt_metric_scaled(*v, scale),
unit_sfx
));
}
out.push('\n');
Ok(out)
}
}
/// Human-readable unit suffix for a metric key, for chart value labels.
fn friendly_unit(metric: &str) -> String {
let m = metric.to_ascii_lowercase();
if m.contains("ops_sec") || m.contains("ops_per_sec") || m.contains("commits_per_sec") {
"ops/sec".into()
} else if m.contains("rows_per_sec") || m.ends_with("_per_sec") {
"/sec".into()
} else if m.ends_with("_us") {
"µs".into()
} else if m.ends_with("_ms") {
"ms".into()
} else if m.ends_with("_ns") {
"ns".into()
} else if m.ends_with("_mbs") || m.contains("mbps") || m.contains("mb_per_sec") {
"MB/s".into()
} else if m.ends_with("_secs") || m == "seconds" {
"s".into()
} else {
String::new()
}
}
/// Sanitize a metric key into a safe asset-file basename component.
#[cfg_attr(not(feature = "export"), allow(dead_code))]
fn sanitize_asset(s: &str) -> String {
s.chars()
.map(|c| if c.is_ascii_alphanumeric() { c } else { '_' })
.collect()
}
/// `tests` renderer: enumerate the repo's tests grouped by the Rust-standard
/// categories — **unit** (`#[test]` under `src/`), **integration** (under
/// `tests/`), and **doc** (runnable fences in `///`/`//!` comments) — via `syn`,
/// so the inventory stays correct as tests come and go.
///
/// Optional args:
/// - `crate=PAT` — only crates whose name contains `PAT`.
/// - `kind=unit|integration|doc` — render only that category (default: all).
/// - `include_benches=true` — also count perf-benchmark files (`*_bench.rs`,
/// `perf_*.rs`, …), which are skipped by default so they don't inflate the
/// test count (they belong in a `benches` section).
fn render_tests(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
let include_benches = args
.get("include_benches")
.map(|v| v == "true")
.unwrap_or(false);
let inv = crate::test_inventory::scan_opts(
ctx.repo_root,
args.get("crate").map(|s| s.as_str()),
include_benches,
);
let only = args.get("kind").map(|s| s.as_str());
let mut out = String::new();
let want = |k: &str| only.is_none_or(|o| o == k);
if want("unit") {
out.push_str(&format!("**Unit tests** ({})\n\n", inv.unit_total()));
out.push_str(&file_tests_table(&inv.unit));
out.push('\n');
}
if want("integration") {
out.push_str(&format!(
"**Integration tests** ({})\n\n",
inv.integration_total()
));
out.push_str(&file_tests_table(&inv.integration));
out.push('\n');
}
if want("doc") {
out.push_str(&format!("**Doc tests** ({})\n\n", inv.doc_total()));
if inv.doc.is_empty() {
out.push_str("_(none)_\n");
} else {
out.push_str("| file | doc-tests |\n|---|--:|\n");
for f in &inv.doc {
out.push_str(&format!("| `{}` | {} |\n", md_escape(&f.file), f.count));
}
}
}
Ok(out.trim_end().to_string() + "\n")
}
/// Render the `nornir:gen:coverage` section (feature N — maven-style): an
/// overall line/region % line + a per-crate table, from the pre-fetched latest
/// `coverage` run (`Ctx::coverage`). With `worst=N`, also a worst-files table.
/// Empty (a graceful `_(no coverage recorded)_`) when no run is pre-fetched, so
/// a repo that never ran `nornir coverage` still renders.
fn render_coverage(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
let rows = ctx.coverage;
if rows.is_empty() {
return Ok("_(no coverage recorded — run `nornir coverage <repo>`)_\n".to_string());
}
let mut out = String::new();
if let Some(o) = rows.iter().find(|r| r.scope == "overall") {
out.push_str(&format!(
"**Overall coverage:** {:.1}% lines · {:.1}% regions\n\n",
o.line_pct(),
o.region_pct()
));
}
out.push_str("| crate | line % | region % |\n|---|--:|--:|\n");
for c in rows.iter().filter(|r| r.scope.starts_with("crate:")) {
out.push_str(&format!(
"| `{}` | {:.1}% | {:.1}% |\n",
md_escape(&c.krate),
c.line_pct(),
c.region_pct()
));
}
// Optional worst-files table (`worst=N`).
if let Some(n) = args.get("worst").and_then(|v| v.parse::<usize>().ok()) {
let mut files: Vec<_> = rows
.iter()
.filter(|r| r.scope.starts_with("file:") && r.lines > 0)
.collect();
files.sort_by(|a, b| a.line_pct().total_cmp(&b.line_pct()));
out.push_str("\n**Worst-covered files**\n\n| file | line % |\n|---|--:|\n");
for f in files.iter().take(n) {
out.push_str(&format!(
"| `{}` | {:.1}% |\n",
md_escape(&f.file),
f.line_pct()
));
}
}
Ok(out.trim_end().to_string() + "\n")
}
/// Map a numeric coverage code to the matrix glyph. Mirrors the numeric
/// convention a [`BenchSource::Static`](nornir_bench::BenchSource) capability
/// writer emits (skade's `Cov::code`): `1.0`=✅ full, `0.5`=◐ partial, `0.0`=✗
/// none, `-1.0`=NA not-applicable, `-2.0`=— unknown/unmeasured. Any code outside
/// that set also renders as `—` (unknown), so a stray value can never
/// masquerade as support. Compared with a small tolerance because the codes
/// round-trip through JSON floats.
fn capability_glyph(code: f64) -> &'static str {
const EPS: f64 = 1e-9;
if (code - 1.0).abs() < EPS {
"✅"
} else if (code - 0.5).abs() < EPS {
"◐"
} else if code.abs() < EPS {
"✗"
} else if (code + 1.0).abs() < EPS {
"NA"
} else {
// `-2.0` (explicit unknown) and any unrecognised code both land here.
"—"
}
}
/// Render the `nornir:gen:capabilities` section: a ✓/✗ capability matrix built
/// from a [`BenchSource::Static`](nornir_bench::BenchSource) row's
/// `cap.<slug>.<rival>` numeric keys — the first-class renderer for the
/// static-capabilities seam (issue #13) so any repo can render its
/// feature-matrix without hand-rolling a namespaced marker.
///
/// Rows are capability slugs; columns are `ours` + the rival systems, with the
/// `ours` column bold. Deterministic order: rows by slug, columns `ours`-first
/// then the rest alphabetically. Graceful (never an error) when no run or no
/// matching Static row is present, so `nornir docs check` doesn't fail merely
/// because the static bench hasn't been (re)run.
///
/// Args:
/// - `source=<bench-name>` (required) — the Static result to read (e.g.
/// `skade.capabilities`).
/// - `ours=<rival-key>` — the column to bold as *ours*; defaults to the
/// `source` name with a trailing `.capabilities` stripped (so
/// `skade.capabilities` ⇒ `skade`).
fn render_capabilities(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
let source = args
.get("source")
.ok_or_else(|| anyhow!("`capabilities` needs a `source=<bench-name>` arg"))?;
let Some(run) = usable_run(ctx, args) else {
return Ok(format!(
"_(no capability matrix yet — run the `{source}` static-capabilities bench)_\n"
));
};
// Only a Static row is a capability matrix; a measured row sharing the name
// would be a category error, so require the tag.
let Some(res) = run
.results
.iter()
.find(|r| r.name == *source && r.is_static())
else {
return Ok(format!(
"_(no `{source}` static-capabilities row in the latest run)_\n"
));
};
// Which column is *ours* (bold). Default: the source name minus a trailing
// `.capabilities` (`skade.capabilities` ⇒ `skade`).
let ours = args.get("ours").cloned().unwrap_or_else(|| {
source
.strip_suffix(".capabilities")
.unwrap_or(source)
.to_string()
});
// Collect (slug, rival) → code from the `cap.<slug>.<rival>` numeric keys.
// The `rsplit_once('.')` peels the rival (last dot-segment) off the slug, so
// the accompanying `capability_rows` count and any non-`cap.` metric are
// naturally skipped.
let mut cells: HashMap<(String, String), f64> = HashMap::new();
let mut slugs: Vec<String> = Vec::new();
let mut rivals: Vec<String> = Vec::new();
for (k, v) in &res.metrics {
let Some(rest) = k.strip_prefix("cap.") else {
continue;
};
let Some((slug, rival)) = rest.rsplit_once('.') else {
continue;
};
let Some(code) = v.as_f64() else { continue };
if !slugs.iter().any(|s| s == slug) {
slugs.push(slug.to_string());
}
if !rivals.iter().any(|r| r == rival) {
rivals.push(rival.to_string());
}
cells.insert((slug.to_string(), rival.to_string()), code);
}
if cells.is_empty() {
return Ok(format!(
"_(the `{source}` row carries no `cap.<slug>.<rival>` capability cells)_\n"
));
}
// Deterministic order: rows by slug; columns = ours first (if present),
// then the remaining rivals alphabetically.
slugs.sort();
rivals.sort();
let mut cols: Vec<String> = Vec::new();
if rivals.iter().any(|r| *r == ours) {
cols.push(ours.clone());
}
for r in &rivals {
if *r != ours {
cols.push(r.clone());
}
}
let is_ours = |c: &str| c == ours;
// Header + alignment separator (each column centre-aligned like skade's).
let mut out = String::from("| capability |");
for c in &cols {
if is_ours(c) {
out.push_str(&format!(" **{}** |", md_escape(c)));
} else {
out.push_str(&format!(" {} |", md_escape(c)));
}
}
out.push_str("\n|---|");
for _ in &cols {
out.push_str(":--:|");
}
out.push('\n');
// One line per capability; the `ours` cell is bold.
for slug in &slugs {
out.push_str(&format!("| {} |", md_escape(slug)));
for c in &cols {
let glyph = cells
.get(&(slug.clone(), c.clone()))
.map(|code| capability_glyph(*code))
.unwrap_or("—");
if is_ours(c) {
out.push_str(&format!(" **{glyph}** |"));
} else {
out.push_str(&format!(" {glyph} |"));
}
}
out.push('\n');
}
Ok(out)
}
fn file_tests_table(files: &[crate::test_inventory::FileTests]) -> String {
if files.is_empty() {
return "_(none)_\n".to_string();
}
let mut out = String::from("| file | tests |\n|---|---|\n");
for f in files {
let names = f
.tests
.iter()
.map(|t| format!("`{}`", md_escape(t)))
.collect::<Vec<_>>()
.join(", ");
out.push_str(&format!("| `{}` | {} |\n", md_escape(&f.file), names));
}
out
}
/// Parse a `best=metric:low,other:high` marker arg into per-metric direction
/// overrides. Unparseable pairs are skipped (the suffix fallback then applies).
fn parse_best_overrides(arg: Option<&str>) -> HashMap<String, nornir_bench::MetricDirection> {
use nornir_bench::MetricDirection;
let mut out = HashMap::new();
let Some(s) = arg else { return out };
for pair in s.split(',') {
if let Some((metric, dir)) = pair.split_once(':') {
let d = match dir.trim().to_ascii_lowercase().as_str() {
"high" | "max" | "up" => MetricDirection::High,
"low" | "min" | "down" => MetricDirection::Low,
"neutral" | "none" => MetricDirection::Neutral,
_ => continue,
};
out.insert(metric.trim().to_string(), d);
}
}
out
}
/// Format a metric value for readability. Millions and up collapse to a unit
/// suffix (3_390_444.67 → "3.39M", 2_375_747.21 → "2.38M") so wide columns stay
/// scannable instead of becoming a wall of digits; smaller values keep full
/// precision as PLAIN digits — no thousands separators (12809.87 → "12809.87",
/// L3/L8). Integers drop the decimal tail. Display only — winner selection
/// always uses the raw f64.
pub(crate) fn fmt_metric(f: f64) -> String {
let a = f.abs();
if a >= 1e6 {
let (scaled, suffix) = if a >= 1e12 {
(f / 1e12, "T")
} else if a >= 1e9 {
(f / 1e9, "B")
} else {
(f / 1e6, "M")
};
let s = format!("{scaled:.2}");
let s = s.trim_end_matches('0').trim_end_matches('.');
return format!("{s}{suffix}");
}
// A thousand and up: round to a whole number — the fractional tail (8,869.62)
// is just noise at that magnitude.
if a >= 1e3 {
return group_thousands(&format!("{}", f.round() as i64));
}
let plain = if f.fract() == 0.0 {
format!("{}", f as i64)
} else {
format!("{f:.2}")
};
group_thousands(&plain)
}
/// Pick one uniform magnitude (divisor, suffix) for a whole column / chart series
/// from its largest |value|, so every cell renders in the *same* unit — 2.12M
/// next to 0.00M — instead of mixing "2.12M" with "3,930". Returns `None` below a
/// million, where plain, un-separated values already read cleanly.
pub(crate) fn metric_scale(max_abs: f64) -> Option<(f64, &'static str)> {
if max_abs >= 1e12 {
Some((1e12, "T"))
} else if max_abs >= 1e9 {
Some((1e9, "B"))
} else if max_abs >= 1e6 {
Some((1e6, "M"))
} else {
None
}
}
/// Decide how a whole column / chart series prints: a shared magnitude
/// (divisor, suffix) from the column max, plus a shared decimal count chosen so
/// the *smallest* non-zero value still shows ~3 significant figures — otherwise a
/// 2.12M leader would force a 3,930 also-ran to round to a useless "0.00M". The
/// decimals apply to every cell, so the column stays aligned (2.11930M next to
/// 0.00393M). Returns `None` below a million, where plain, un-separated
/// per-value [`fmt_metric`] already reads cleanly.
pub(crate) fn column_format(vals: &[f64]) -> Option<(f64, &'static str, usize)> {
let max_abs = vals.iter().copied().map(f64::abs).fold(0.0_f64, f64::max);
let (div, suffix) = metric_scale(max_abs)?;
let min_scaled = vals
.iter()
.copied()
.map(f64::abs)
.filter(|v| *v > 0.0)
.map(|v| v / div)
.fold(f64::INFINITY, f64::min);
// ~3 sig-figs of the smallest scaled value; floor 2, cap 6.
let decimals = if min_scaled.is_finite() && min_scaled > 0.0 {
((2 - min_scaled.log10().floor() as i32).max(2) as usize).min(6)
} else {
2
};
Some((div, suffix, decimals))
}
/// Format one value inside its column's [`column_format`] (uniform suffix +
/// decimals), or fall back to per-value [`fmt_metric`] when the column carries no
/// million-plus value.
pub(crate) fn fmt_metric_scaled(f: f64, fmt: Option<(f64, &'static str, usize)>) -> String {
match fmt {
Some((div, suffix, decimals)) => format!("{:.*}{suffix}", decimals, f / div),
None => fmt_metric(f),
}
}
/// Numeric strings render WITHOUT thousands separators — `1622`, never `1,622`
/// or `1.622` (a decision for the bench tables: bare digits read cleanest and
/// never collide with a decimal point). Kept as a pass-through so every caller
/// funnels through one place if grouping is ever wanted back.
fn group_thousands(s: &str) -> String {
s.to_string()
}
/// Trophy (🏆) set: the names of *our* rows that beat a long-reigning
/// "hard-broken record" rival — one declared in `<repo>/.nornir/records.toml`
/// (keyed by the rival's workload name; fields `metric` default `decompress_mbs`).
/// A record earns the badge ONLY when one of our rows actually beats it on that
/// metric in THIS table, so a contended run where we lose shows no trophy. Empty
/// when the file is absent/unparseable or no record is present/beaten.
fn hard_broken_trophies(
ctx: &Ctx,
results: &[&nornir_bench::BenchResult],
ours_tokens: &[String],
) -> std::collections::HashSet<String> {
let mut trophies = std::collections::HashSet::new();
let Ok(text) = std::fs::read_to_string(ctx.repo_root.join(".nornir/records.toml")) else {
return trophies;
};
let Ok(tbl) = text.parse::<toml::Table>() else {
return trophies;
};
// Word-bounded (H3): a `self=` token marks a row ours only on an exact or
// segment match — `self=bzip2` must NOT claim `lbzip2`/`zoomies-lbzip2`.
let is_ours = |name: &str| ours_tokens.iter().any(|t| token_matches(name, t));
for (rival, spec) in &tbl {
// The record rival must be one of THIS table's rows to be beatable here.
let Some(rv) = results.iter().find(|r| r.name.as_str() == rival.as_str()) else {
continue;
};
let metric = spec
.get("metric")
.and_then(|v| v.as_str())
.unwrap_or("decompress_mbs");
let Some(rival_val) = rv.metrics.get(metric).and_then(|v| v.as_f64()) else {
continue;
};
// The fastest of OUR rows on that metric — trophy it iff it beats the record.
let best = results
.iter()
.filter(|r| is_ours(&r.name))
.filter_map(|r| {
r.metrics
.get(metric)
.and_then(|v| v.as_f64())
.map(|f| (f, r.name.clone()))
})
.fold(None::<(f64, String)>, |acc, (f, n)| match acc {
Some((bf, _)) if bf >= f => acc,
_ => Some((f, n)),
});
if let Some((of, on)) = best {
if of > rival_val {
trophies.insert(on);
}
}
}
trophies
}
fn render_depgraph(ctx: &Ctx) -> Result<String> {
// DR1: materialize sibling path-deps (clone+symlink) before `cargo metadata`
// so a headless server-side docs render of a path-dep'd repo (nornir → skade)
// resolves the same siblings the SBOM security scan does. The workspace's
// `workspace_root` is the `git/` dir holding every member checkout — the
// scan_root. DR2: a still-unresolved sibling surfaces a clear named error.
// The network clone half of the prep stays in nornir (needs gitio) and is
// INJECTED via `Ctx::path_dep_cloner` (nornir passes
// `security::clone_missing_path_dep_siblings`); this leaf crate does the
// pure symlink materialization + unresolved diagnostics. No cloner ⇒ 0.
let g = depgraph::extract_with_path_deps(ctx.repo_root, ctx.workspace_root, &|repo, root| {
match ctx.path_dep_cloner {
Some(clone_missing) => clone_missing(repo, root),
None => 0,
}
})?;
// No silent gaps: if `cargo metadata` yielded no crates (not a cargo
// workspace, or metadata produced nothing), emit a visible note instead of
// an empty marker so the reader knows *why* there's no graph and how to fix
// it — rather than seeing nothing at all.
if g.nodes.is_empty() {
return Ok(format!(
"_no dependency-graph data for `{}` — `cargo metadata` returned no \
packages. Ensure this is a Cargo workspace and re-run \
`nornir docs render`._\n",
ctx.repo_root.display()
));
}
#[cfg(feature = "export")]
{
// Generated media lives under `.nornir/assets/` (tracked) alongside the
// doc sources — one nornir-owned namespace, no copy at the repo root.
// Git hosts (Codeberg/GitHub) resolve this relative path in the web UI,
// and the typst exporter resolves it via the repo-root filesystem root.
let rel = std::path::PathBuf::from(".nornir/assets/depgraph.svg");
let abs = ctx.repo_root.join(&rel);
// Markdown-only (pre-commit hook): emit the image link but do NOT
// regenerate the SVG — the committed asset from a prior full render is
// reused, keeping the commit cheap and the markdown deterministic.
if !ctx.no_assets {
crate::svg::render_to_file(&g, &abs)?;
}
// Emit the link relative to the doc being rendered. README (repo root)
// ⇒ `.nornir/assets/depgraph.svg`; a doc under `.nornir/` ⇒
// `assets/depgraph.svg`. Git hosts resolve image src relative to the
// file, so a repo-root-relative path would 404 inside `.nornir/`.
let link = doc_relative_asset(ctx, &abs);
return Ok(format!("\n"));
}
#[cfg(not(feature = "export"))]
{
// No typst engine in this build: emit the self-contained static SVG
// inline (no JS, no build step — NUKE-MERMAID house style).
let mut out = g.to_svg();
if !out.ends_with('\n') {
out.push('\n');
}
Ok(out)
}
}
/// Compute the link to `asset_abs` (an absolute path under `repo_root`)
/// relative to the doc being rendered. Falls back to the repo-root-relative
/// path when `ctx.doc_dir` is unset or the two diverge outside the repo.
///
/// Pure path arithmetic over normalised components (no filesystem access):
/// walk off the shared prefix of `doc_dir` and the asset, emitting one `..`
/// per remaining `doc_dir` component, then the asset's tail. Both inputs are
/// taken relative to `repo_root` so the result is repo-portable.
// Reached from the `export`-gated asset emitters (depgraph/bench-chart); the
// no-export build still compiles it for the unit test below.
#[cfg_attr(not(feature = "export"), allow(dead_code))]
fn doc_relative_asset(ctx: &Ctx, asset_abs: &Path) -> String {
use std::path::Component;
let repo_rel = asset_abs
.strip_prefix(ctx.repo_root)
.map(|p| p.to_path_buf())
.unwrap_or_else(|_| asset_abs.to_path_buf());
let Some(doc_dir) = ctx.doc_dir else {
return repo_rel.display().to_string();
};
let Ok(doc_rel) = doc_dir.strip_prefix(ctx.repo_root) else {
return repo_rel.display().to_string();
};
let from: Vec<&std::ffi::OsStr> = doc_rel
.components()
.filter_map(|c| match c {
Component::Normal(s) => Some(s),
_ => None,
})
.collect();
let to: Vec<&std::ffi::OsStr> = repo_rel
.components()
.filter_map(|c| match c {
Component::Normal(s) => Some(s),
_ => None,
})
.collect();
// Drop the shared leading components.
let common = from
.iter()
.zip(to.iter())
.take_while(|(a, b)| a == b)
.count();
let mut out = PathBuf::new();
for _ in common..from.len() {
out.push("..");
}
for s in &to[common..] {
out.push(s);
}
if out.as_os_str().is_empty() {
".".to_string()
} else {
out.display().to_string()
}
}
fn resolve_binary(ctx: &Ctx, raw: &str) -> PathBuf {
let p = PathBuf::from(raw);
if p.is_absolute() {
p
} else if ctx.repo_root.join(&p).exists() {
ctx.repo_root.join(&p)
} else {
ctx.workspace_root.join(&p)
}
}
fn render_symbols(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
let bin_arg = args
.get("binary")
.ok_or_else(|| anyhow!("section `symbols` needs binary=PATH"))?;
let limit: usize = args.get("limit").and_then(|v| v.parse().ok()).unwrap_or(20);
let binary = resolve_binary(ctx, bin_arg);
let syms = artifact::extract_symbols(&binary, ctx.workspace_root)?;
let mut visible: Vec<&artifact::Symbol> = syms
.iter()
.filter(|s| !s.name_demangled.contains("::{{closure}}"))
.collect();
visible.sort_by(|a, b| b.size_bytes.unwrap_or(0).cmp(&a.size_bytes.unwrap_or(0)));
visible.truncate(limit);
let mut out = String::new();
out.push_str(&format!(
"_top {} symbols by size from `{}`_\n\n",
visible.len(),
bin_arg
));
out.push_str("| symbol | size | file |\n|--------|------|------|\n");
for s in &visible {
let file = if s.file.is_empty() {
"?"
} else {
s.file.as_str()
};
let size = s
.size_bytes
.map(|n| n.to_string())
.unwrap_or_else(|| "-".into());
out.push_str(&format!(
"| `{}` | {} | {} |\n",
md_escape(&s.name_demangled),
size,
file
));
}
Ok(out)
}
fn render_callgraph(ctx: &Ctx, args: &HashMap<String, String>) -> Result<String> {
let bin_arg = args
.get("binary")
.ok_or_else(|| anyhow!("section `callgraph` needs binary=PATH"))?;
let limit: usize = args.get("limit").and_then(|v| v.parse().ok()).unwrap_or(15);
let binary = resolve_binary(ctx, bin_arg);
let edges = callgraph_dwarf::extract_callgraph(&binary, ctx.workspace_root)?;
let mut count: HashMap<String, usize> = HashMap::new();
for e in &edges {
*count.entry(e.callee.clone()).or_default() += 1;
}
let mut ranked: Vec<(String, usize)> = count.into_iter().collect();
ranked.sort_by(|a, b| b.1.cmp(&a.1).then(a.0.cmp(&b.0)));
ranked.truncate(limit);
let mut out = String::new();
out.push_str(&format!(
"_{} inline edges in `{}`; top {} callees:_\n\n",
edges.len(),
bin_arg,
ranked.len()
));
out.push_str("| callee | inline sites |\n|--------|-------------:|\n");
for (n, c) in ranked {
out.push_str(&format!("| `{}` | {} |\n", md_escape(&n), c));
}
Ok(out)
}
fn md_escape(s: &str) -> String {
s.replace('|', "\\|").replace('`', "\\`")
}
// ----- tests -----------------------------------------------------------------
#[cfg(test)]
mod tests {
use super::*;
use serde_json::{Map, Value};
fn ctx<'a>(root: &'a Path, run: Option<&'a BenchRun>) -> Ctx<'a> {
Ctx::new(root, root, run)
}
#[test]
fn doc_relative_asset_is_relative_to_doc_dir() {
let root = Path::new("/repo");
let asset = Path::new("/repo/.nornir/assets/depgraph.svg");
// README at repo root → repo-root-relative path.
let c_root = Ctx::new(root, root, None).with_doc_dir(root);
assert_eq!(
doc_relative_asset(&c_root, asset),
".nornir/assets/depgraph.svg"
);
// design.md under .nornir/ → the leading `.nornir/` is cancelled by one
// `../`, yielding the clean doc-relative `assets/depgraph.svg`.
let nornir_dir = root.join(".nornir");
let c_nornir = Ctx::new(root, root, None).with_doc_dir(&nornir_dir);
assert_eq!(doc_relative_asset(&c_nornir, asset), "assets/depgraph.svg");
// No doc_dir → repo-root-relative (historical default).
let c_none = Ctx::new(root, root, None);
assert_eq!(
doc_relative_asset(&c_none, asset),
".nornir/assets/depgraph.svg"
);
}
/// `Ctx::markdown_only()` suppresses the heavy SVG asset write: with
/// `export`, `render_depgraph` must emit the image link but NOT create
/// the depgraph.svg file (reusing the committed one). This is the engine of
/// the pre-commit hook's `--no-assets` behaviour. Inject a real cargo crate
/// so the depgraph has a known node, then assert the marker body has the link
/// and the SVG file was NOT written.
#[cfg(feature = "export")]
#[test]
fn markdown_only_suppresses_svg_asset_write() {
let t = tempfile::TempDir::new().unwrap();
let root = t.path();
std::fs::write(
root.join("Cargo.toml"),
"[package]\nname = \"widget_lib\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n\
[dependencies]\nserde = \"1\"\n",
)
.unwrap();
std::fs::create_dir_all(root.join("src")).unwrap();
std::fs::write(root.join("src/lib.rs"), "pub fn noop() {}\n").unwrap();
let svg = root.join(".nornir/assets/depgraph.svg");
// markdown_only ⇒ NO file written, but the link is still emitted.
let c = Ctx::new(root, root, None).markdown_only();
let body = render_depgraph(&c).expect("render depgraph");
assert!(
body.contains("![workspace dependency graph]"),
"markdown-only depgraph must still emit the image link; got {body}"
);
assert!(
!svg.exists(),
"markdown-only render must NOT (re)write the depgraph SVG asset"
);
// Full render (assets on) DOES write the SVG.
let c_full = Ctx::new(root, root, None);
render_depgraph(&c_full).expect("render depgraph full");
assert!(
svg.exists(),
"full render must write the depgraph SVG asset"
);
}
fn run_fixture() -> BenchRun {
let mut m: Map<String, Value> = Map::new();
m.insert("ops".to_string(), Value::from(123.45f64));
BenchRun {
id: Default::default(),
machine_facts: None,
date: "2026-05-30".into(),
timestamp: None,
version: "0.1.0".into(),
machine: "test".into(),
cores: 8,
results: vec![nornir_bench::BenchResult {
name: "decode".into(),
metrics: m,
..Default::default()
}],
tests: vec![],
}
}
fn arm(name: &str, val: f64) -> nornir_bench::BenchResult {
let mut m: Map<String, Value> = Map::new();
m.insert("decompress_mbs".into(), Value::from(val));
nornir_bench::BenchResult {
name: name.into(),
metrics: m,
..Default::default()
}
}
fn multi_arm_run(arms: &[(&str, f64)]) -> BenchRun {
BenchRun {
id: Default::default(),
machine_facts: None,
date: "2026-07-13".into(),
timestamp: None,
version: "0.1.0".into(),
machine: "test".into(),
cores: 12,
results: arms.iter().map(|(n, v)| arm(n, *v)).collect(),
tests: vec![],
}
}
/// A PREVIEW run must be invisible to an ordinary bench marker, so the
/// non-destructive guard keeps the committed table. znippy-zoomies' README
/// carries measured 12-core `loki` numbers; an under-floor 32-core preview must
/// not replace a single one of them.
#[test]
fn a_preview_run_never_reaches_a_marker_that_did_not_ask_for_it() {
let mut run = multi_arm_run(&[("znippy-gz", 12800.0), ("pigz", 459.19)]);
run.machine =
"PREVIEW · AMD Ryzen Threadripper PRO 3975WX 32-Cores · corpus 1024 MB".into();
let root = Path::new("/repo");
let c = ctx(root, Some(&run));
// No opt-in ⇒ the renderer reports absence …
let plain = render_benches(&c, &HashMap::new()).unwrap();
assert!(
is_no_data_placeholder(&plain),
"preview leaked into a plain marker: {plain}"
);
// … and `render` therefore KEEPS whatever the doc already had.
let m = Marker {
name: "benches".into(),
args: HashMap::new(),
line_start: 0,
line_end: 2,
};
let kept = render(
&c,
&m,
"| workload | decompress_mbs |\n| znippy-gz | 5760 |\n",
)
.unwrap();
assert!(
kept.contains("5760"),
"committed table was overwritten by a preview: {kept}"
);
// Opting in is the only way through.
let args: HashMap<String, String> = [("preview".to_string(), "yes".to_string())]
.into_iter()
.collect();
let opted = render_benches(&c, &args).unwrap();
assert!(
opted.contains("12800"),
"preview=yes did not admit the preview: {opted}"
);
assert!(
opted.contains("PREVIEW"),
"the preview stamp must survive into the table"
);
}
/// A non-preview run is unaffected by the gate.
#[test]
fn an_ordinary_run_still_renders_without_opting_in() {
let run = multi_arm_run(&[("znippy-gz", 5760.0), ("pigz", 589.32)]);
let body = render_benches(&ctx(Path::new("/repo"), Some(&run)), &HashMap::new()).unwrap();
assert!(body.contains("5760"), "{body}");
}
/// `suspect=exclude` withholds an implausible arm AND says so — the table and
/// its caveat come from one screen, so they cannot disagree.
#[test]
fn an_implausible_arm_is_withheld_with_a_rendered_reason() {
let mut fabric: Map<String, Value> = Map::new();
fabric.insert("fabric_s".into(), Value::from(1.4929e-5f64));
fabric.insert("rows_per_s".into(), Value::from(6.698e9f64));
let mut honest: Map<String, Value> = Map::new();
honest.insert("scan_s".into(), Value::from(0.00206f64));
honest.insert("rows_per_s".into(), Value::from(48.5e6f64));
let run = BenchRun {
id: Default::default(),
machine_facts: None,
date: "2026-07-23".into(),
timestamp: None,
version: "0.1.3".into(),
machine: "oden".into(),
cores: 32,
results: vec![
nornir_bench::BenchResult {
name: "bifrost_fabric".into(),
metrics: fabric,
..Default::default()
},
nornir_bench::BenchResult {
name: "file_source_scan".into(),
metrics: honest,
..Default::default()
},
],
tests: vec![],
};
let c = ctx(Path::new("/repo"), Some(&run));
let args: HashMap<String, String> = [("suspect".to_string(), "exclude".to_string())]
.into_iter()
.collect();
let body = render_benches(&c, &args).unwrap();
assert!(
body.contains("file_source_scan"),
"the honest arm must stay: {body}"
);
assert!(
!body.contains("| bifrost_fabric |"),
"the arm was still tabled: {body}"
);
assert!(
body.contains("withheld from this table"),
"no rendered reason: {body}"
);
// Default is `flag`: the row stays, marked, with the reason underneath.
let flagged = render_benches(&c, &HashMap::new()).unwrap();
assert!(flagged.contains("bifrost_fabric ⚠"), "{flagged}");
assert!(flagged.contains("not credible as real work"), "{flagged}");
// `off` restores the unscreened table.
let off: HashMap<String, String> = [("suspect".to_string(), "off".to_string())]
.into_iter()
.collect();
let raw = render_benches(&c, &off).unwrap();
assert!(!raw.contains('⚠'), "suspect=off still flagged: {raw}");
}
#[test]
fn token_matches_is_word_bounded_not_substring() {
// H3: rival ids are bare substrings of ours (letter-adjacent) → NO match.
assert!(
!token_matches("lbzip2", "bzip2"),
"bzip2 ⊄ lbzip2 (l-adjacent)"
);
assert!(
!token_matches("zoomies-lbzip2", "bzip2"),
"bzip2 ⊄ zoomies-lbzip2"
);
// Exact + separator-delimited segments DO match.
assert!(token_matches("bzip2", "bzip2"));
assert!(token_matches("zoomies-jar", "zoomies-jar"));
assert!(
token_matches("zoomies-lbzip2", "lbzip2"),
"`-`-delimited segment matches"
);
// Variant-folding (skade/holger mashups) must survive: `_`-delimited segment.
assert!(token_matches("data_pipe_skade_s3", "skade_s3"));
assert!(token_matches("oxr_skade", "skade"));
assert!(!token_matches("data_pipe_skade_nvme", "skade_s3"));
// self= word-boundary: `self=zoomies-lbzip2` claims ONLY ours.
assert!(!token_matches("lbzip2", "zoomies-lbzip2"));
assert!(!token_matches("bzip2", "zoomies-lbzip2"));
}
#[test]
fn compare_selects_exact_arm_no_rival_contamination() {
// H3 verified case: rival ids are substrings of our arm ids. `compare=bzip2`
// must select ONLY the `bzip2` row (value 100) — NOT lbzip2 (700) and NOT
// zoomies-lbzip2 (755, the number that leaked into the bzip2 row pre-fix).
let run = multi_arm_run(&[
("bzip2", 100.0),
("lbzip2", 700.0),
("zoomies-lbzip2", 755.0),
("jar", 200.0),
("zoomies-jar", 900.0),
("zip", 300.0),
("zoomies-zip", 800.0),
]);
let root = Path::new("/repo");
let c = ctx(root, Some(&run));
let mut args = HashMap::new();
args.insert("compare".to_string(), "bzip2".to_string());
args.insert("cols".to_string(), "decompress_mbs".to_string());
let out = render_benches(&c, &args).unwrap();
assert!(out.contains("bzip2"), "bzip2 row present:\n{out}");
assert!(out.contains("100"), "bzip2's OWN number shown:\n{out}");
assert!(
!out.contains("755"),
"must NOT show zoomies-lbzip2's number:\n{out}"
);
assert!(
!out.contains("700"),
"must NOT show lbzip2's number:\n{out}"
);
assert!(
!out.contains("zoomies-lbzip2"),
"rival row excluded:\n{out}"
);
// The renamed self= arm selects itself cleanly (no substring bleed).
let mut a2 = HashMap::new();
a2.insert("compare".to_string(), "zoomies-lbzip2".to_string());
a2.insert("self".to_string(), "zoomies-lbzip2".to_string());
a2.insert("cols".to_string(), "decompress_mbs".to_string());
let out2 = render_benches(&c, &a2).unwrap();
assert!(
out2.contains("755"),
"ours row shows its own number:\n{out2}"
);
assert!(
!out2.contains("100"),
"bzip2's number must not leak in:\n{out2}"
);
}
#[test]
fn parses_and_rewrites_bench_section() {
let r = run_fixture();
let d = tempfile::tempdir().unwrap();
let p = d.path().join("README.md");
std::fs::write(
&p,
"# x\n\n<!-- nornir:gen:start:bench -->\nstale\n<!-- nornir:gen:end:bench -->\n\ntail\n",
)
.unwrap();
let c = ctx(d.path(), Some(&r));
let rep = assemble_file(&p, &c).unwrap();
assert!(rep.changed);
assert_eq!(rep.sections, vec!["bench".to_string()]);
let txt = std::fs::read_to_string(&p).unwrap();
assert!(txt.contains("v0.1.0"));
assert!(txt.contains("decode"));
assert!(txt.ends_with("tail\n"));
}
#[test]
fn bench_history_renders_rows_newest_first() {
let mk = |date: &str, ver: &str, ops: f64| -> BenchRun {
let mut m: Map<String, Value> = Map::new();
m.insert("ops".into(), Value::from(ops));
BenchRun {
id: Default::default(),
machine_facts: None,
date: date.into(),
timestamp: None,
version: ver.into(),
machine: "tr".into(),
cores: 8,
results: vec![nornir_bench::BenchResult {
name: "x".into(),
metrics: m,
..Default::default()
}],
tests: vec![],
}
};
// Intentionally not pre-sorted — the renderer must order newest-first.
let history = vec![
mk("2026-05-01", "0.1.0", 100.0),
mk("2026-06-01", "0.2.0", 200.0),
];
let root = Path::new(".");
let c = Ctx::new(root, root, None).with_history(&history);
let text =
"<!-- nornir:gen:start:bench_history -->\n<!-- nornir:gen:end:bench_history -->\n";
let (out, sections) = rewrite_str(text, &c).unwrap();
assert_eq!(sections, vec!["bench_history".to_string()]);
assert!(
out.contains("| Version | Date | Machine |"),
"header: {out}"
);
let i2 = out.find("v0.2.0").expect("v0.2.0 present");
let i1 = out.find("v0.1.0").expect("v0.1.0 present");
assert!(i2 < i1, "newest (0.2.0) must come first:\n{out}");
assert!(
out.contains("200.00") && out.contains("100.00"),
"best-metric values: {out}"
);
}
#[test]
fn bench_history_empty_is_graceful() {
let root = Path::new(".");
let c = Ctx::new(root, root, None); // no history attached
let text =
"<!-- nornir:gen:start:bench_history -->\n<!-- nornir:gen:end:bench_history -->\n";
let (out, _) = rewrite_str(text, &c).unwrap();
assert!(out.contains("no bench history"), "graceful empty: {out}");
}
/// feature N: the `nornir:gen:coverage` marker fills with the overall % + a
/// per-crate table (+ worst files with `worst=N`) from pre-fetched coverage
/// rows. INJECT known rows → ASSERT the rendered table values (LAW 1).
#[test]
fn coverage_marker_fills_overall_and_per_crate() {
use nornir_coverage::CoverageRow;
let r = |scope: &str, krate: &str, file: &str, l: i64, lc: i64| CoverageRow {
run_id: "run".into(),
ts_micros: 1,
repo: "nornir".into(),
scope: scope.into(),
krate: krate.into(),
file: file.into(),
lines: l,
lines_covered: lc,
regions: l,
regions_covered: lc,
};
let rows = vec![
r("overall", "", "", 100, 80),
r("crate:nornir", "nornir", "", 60, 42),
r("crate:nornir-testmatrix", "nornir-testmatrix", "", 40, 38),
r(
"file:src/viz/test_tab.rs",
"nornir",
"src/viz/test_tab.rs",
50,
20,
),
];
let root = Path::new(".");
let c = Ctx::new(root, root, None).with_coverage(&rows);
let text = "<!-- nornir:gen:start:coverage worst=1 -->\n<!-- nornir:gen:end:coverage -->\n";
let (out, sections) = rewrite_str(text, &c).unwrap();
assert_eq!(sections, vec!["coverage".to_string()]);
// Overall 80/100 = 80.0% lines.
assert!(out.contains("80.0% lines"), "overall line %: {out}");
// Per-crate table rows: nornir 42/60 = 70.0%, testmatrix 38/40 = 95.0%.
assert!(
out.contains("`nornir`") && out.contains("70.0%"),
"nornir crate row: {out}"
);
assert!(
out.contains("`nornir-testmatrix`") && out.contains("95.0%"),
"testmatrix row: {out}"
);
// worst=1 → the viz file (40%) shows in the worst-files table.
assert!(out.contains("Worst-covered files"), "worst table: {out}");
assert!(
out.contains("src/viz/test_tab.rs") && out.contains("40.0%"),
"worst file: {out}"
);
}
/// The `coverage` marker renders a graceful fallback when no run is attached.
#[test]
fn coverage_marker_empty_is_graceful() {
let root = Path::new(".");
let c = Ctx::new(root, root, None); // no coverage attached
let text = "<!-- nornir:gen:start:coverage -->\n<!-- nornir:gen:end:coverage -->\n";
let (out, _) = rewrite_str(text, &c).unwrap();
assert!(
out.contains("no coverage recorded"),
"graceful empty: {out}"
);
}
/// A synthetic Static capability row (the `cap.<slug>.<rival>` seam) renders
/// into the expected ✅/◐/✗/NA/— glyph table, `ours` column bold, columns
/// `ours`-first (default derived from the `.capabilities` source name).
/// INJECT known codes → ASSERT the exact rendered glyph cells (LAW 1).
#[test]
fn capabilities_marker_renders_glyph_matrix() {
let mut m: Map<String, Value> = Map::new();
// rows: speed, safety, portable ; cols: mytool (ours) + rival.
m.insert("cap.speed.mytool".into(), Value::from(1.0)); // ✅
m.insert("cap.speed.rival".into(), Value::from(0.5)); // ◐
m.insert("cap.safety.mytool".into(), Value::from(1.0)); // ✅
m.insert("cap.safety.rival".into(), Value::from(0.0)); // ✗
m.insert("cap.portable.mytool".into(), Value::from(-1.0)); // NA
m.insert("cap.portable.rival".into(), Value::from(-2.0)); // —
m.insert("capability_rows".into(), Value::from(3.0)); // must be ignored
let run = BenchRun {
id: Default::default(),
machine_facts: None,
date: "2026-07-08".into(),
timestamp: None,
version: "0.1.0".into(),
machine: "t".into(),
cores: 4,
results: vec![nornir_bench::BenchResult::static_capabilities(
"mytool.capabilities",
m,
)],
tests: vec![],
};
let root = Path::new(".");
let c = ctx(root, Some(&run));
let text = "<!-- nornir:gen:start:capabilities source=mytool.capabilities -->\n<!-- nornir:gen:end:capabilities -->\n";
let (out, sections) = rewrite_str(text, &c).unwrap();
assert_eq!(sections, vec!["capabilities".to_string()]);
// Ours column (mytool) is bold in the header and defaulted from the
// source name (`mytool.capabilities` ⇒ `mytool`), placed first.
assert!(
out.contains("| capability | **mytool** | rival |"),
"header: {out}"
);
assert!(out.contains("|---|:--:|:--:|"), "separator: {out}");
// Each row: ours cell bold, rival cell plain; every glyph level present.
assert!(out.contains("| speed | **✅** | ◐ |"), "speed row: {out}");
assert!(out.contains("| safety | **✅** | ✗ |"), "safety row: {out}");
assert!(
out.contains("| portable | **NA** | — |"),
"portable row: {out}"
);
// The `capability_rows` count is not a `cap.` cell → no stray row.
assert!(
!out.contains("capability_rows"),
"count metric leaked: {out}"
);
}
/// `ours=` overrides which column is bold, and a graceful fallback renders
/// when the run has no matching Static row.
#[test]
fn capabilities_ours_override_and_graceful() {
let mut m: Map<String, Value> = Map::new();
m.insert("cap.speed.foo".into(), Value::from(1.0));
m.insert("cap.speed.bar".into(), Value::from(0.0));
let run = BenchRun {
id: Default::default(),
machine_facts: None,
date: "2026-07-08".into(),
timestamp: None,
version: "0.1.0".into(),
machine: "t".into(),
cores: 4,
results: vec![nornir_bench::BenchResult::static_capabilities("cap.row", m)],
tests: vec![],
};
let root = Path::new(".");
let c = ctx(root, Some(&run));
// ours=bar ⇒ bar column first and bold (not the default).
let text = "<!-- nornir:gen:start:capabilities source=cap.row ours=bar -->\n<!-- nornir:gen:end:capabilities -->\n";
let (out, _) = rewrite_str(text, &c).unwrap();
assert!(
out.contains("| capability | **bar** | foo |"),
"ours=bar header: {out}"
);
assert!(
out.contains("| speed | **✗** | ✅ |"),
"ours=bar row: {out}"
);
// Missing source row ⇒ graceful (never an error, so `docs check` is safe).
let text2 = "<!-- nornir:gen:start:capabilities source=absent.capabilities -->\n<!-- nornir:gen:end:capabilities -->\n";
let (out2, _) = rewrite_str(text2, &c).unwrap();
assert!(
out2.contains("no `absent.capabilities` static-capabilities row"),
"graceful: {out2}"
);
}
/// The code→glyph mapping is exhaustive AND a WRONG (out-of-set) code must
/// fall back to `—` (unknown) — a stray value can never masquerade as ✅.
#[test]
fn capability_glyph_maps_every_code_and_unknown_falls_back() {
assert_eq!(capability_glyph(1.0), "✅");
assert_eq!(capability_glyph(0.5), "◐");
assert_eq!(capability_glyph(0.0), "✗");
assert_eq!(capability_glyph(-1.0), "NA");
assert_eq!(capability_glyph(-2.0), "—");
// Out-of-set codes render as the unknown glyph, never as support.
assert_eq!(capability_glyph(0.7), "—");
assert_eq!(capability_glyph(42.0), "—");
assert_eq!(capability_glyph(-5.0), "—");
}
fn vs_run() -> BenchRun {
// One workload, paired ours-vs-legacy throughput + a derived speedup.
let mut m: Map<String, Value> = Map::new();
m.insert("ljar_mbs".into(), Value::from(2527.0f64));
m.insert("unzip_mbs".into(), Value::from(1404.0f64));
m.insert("speedup_x".into(), Value::from(1.8f64));
BenchRun {
id: Default::default(),
machine_facts: None,
date: "2026-06-05".into(),
timestamp: None,
version: "0.9.0".into(),
machine: "test".into(),
cores: 32,
results: vec![nornir_bench::BenchResult {
name: "jar_2000".into(),
metrics: m,
..Default::default()
}],
tests: vec![],
}
}
#[test]
fn benches_pivots_and_bolds_winner() {
let r = vs_run();
let c = ctx(Path::new("/tmp"), Some(&r));
let out = render_benches(&c, &HashMap::new()).unwrap();
// pivot header carries the metric columns + the workload row
assert!(out.contains("| workload |"), "got: {out}");
assert!(out.contains("ljar_mbs"));
assert!(out.contains("jar_2000"));
// ljar wins the mbs comparison → bolded; unzip not; speedup (unit `x`,
// a lone neutral cell) never bolded.
assert!(out.contains("**2527**"), "winner not bolded: {out}");
assert!(!out.contains("**1404**"), "loser bolded: {out}");
assert!(!out.contains("**1.8"), "neutral bolded: {out}");
}
#[test]
fn benches_exclude_and_bold_false() {
// Two results: one normal, one "_st"; exclude drops the _st row, and
// bold=false suppresses winner bolding.
let mut a: Map<String, Value> = Map::new();
a.insert("ljar_mbs".into(), Value::from(1000.0f64));
a.insert("unzip_mbs".into(), Value::from(60.0f64));
let mut b: Map<String, Value> = Map::new();
b.insert("ljar_mbs".into(), Value::from(120.0f64));
b.insert("unzip_mbs".into(), Value::from(60.0f64));
let run = BenchRun {
id: Default::default(),
machine_facts: None,
date: "2026-06-06".into(),
timestamp: None,
version: "0.9.0".into(),
machine: "tr".into(),
cores: 32,
results: vec![
nornir_bench::BenchResult {
name: "ljar_guava".into(),
metrics: a,
..Default::default()
},
nornir_bench::BenchResult {
name: "ljar_guava_st".into(),
metrics: b,
..Default::default()
},
],
tests: vec![],
};
let c = ctx(Path::new("/tmp"), Some(&run));
let mut args = HashMap::new();
args.insert("exclude".to_string(), "_st".to_string());
args.insert("bold".to_string(), "false".to_string());
let out = render_benches(&c, &args).unwrap();
assert!(out.contains("ljar_guava"), "got: {out}");
assert!(!out.contains("ljar_guava_st"), "exclude failed: {out}");
// no bolded *cell* (the run header is bold, but cells aren't)
assert!(
!out.contains("**1000**"),
"bold=false should suppress cell bolding: {out}"
);
// header carries machine + cores
assert!(
out.contains("tr · 32 cores"),
"header missing machine: {out}"
);
}
#[test]
fn benches_best_override_flips_direction() {
let r = vs_run();
let c = ctx(Path::new("/tmp"), Some(&r));
// Force "lower mbs is better" via the marker arg → unzip wins.
let mut args = HashMap::new();
args.insert("best".to_string(), "ljar_mbs:low,unzip_mbs:low".to_string());
let out = render_benches(&c, &args).unwrap();
assert!(out.contains("**1404**"), "override not applied: {out}");
assert!(!out.contains("**2527**"), "override ignored: {out}");
}
/// The data-driven pairing key snaps each of OUR codecs to its TRUE format
/// rival, not a spurious one sharing an incidental letter.
#[test]
fn common_run_snaps_codec_to_its_format_rival() {
assert_eq!(common_run("zoomie-lgz", "gzip"), 2); // "gz"
assert_eq!(common_run("zoomie-lbzip2", "bzip2"), 5); // "bzip2"
assert!(common_run("zoomie-lgz", "gzip") > common_run("zoomie-lgz", "bzip2"));
assert!(common_run("zoomie-lbzip2", "bzip2") > common_run("zoomie-lbzip2", "gzip"));
}
/// A codec-family bench-run: two of OUR codecs (`zoomie-lgz`, `zoomie-lbzip2`)
/// and their two format rivals (`gzip`, `bzip2`), each carrying a decode metric
/// plus format-level `compress_mbs`/`ratio` columns.
fn codec_run() -> BenchRun {
let mk = |name: &str, decode: f64, compress: f64, ratio: f64| {
let mut m: Map<String, Value> = Map::new();
m.insert("decompress_mbs".into(), Value::from(decode));
m.insert("compress_mbs".into(), Value::from(compress));
m.insert("ratio".into(), Value::from(ratio));
nornir_bench::BenchResult {
name: name.into(),
metrics: m,
..Default::default()
}
};
BenchRun {
id: Default::default(),
machine_facts: None,
date: "2026-07-07".into(),
timestamp: None,
version: "0.1.0".into(),
machine: "test".into(),
cores: 32,
results: vec![
mk("zoomie-lgz", 900.0, 400.0, 2.8),
mk("gzip", 500.0, 90.0, 2.8),
mk("zoomie-lbzip2", 700.0, 120.0, 3.5),
mk("bzip2", 300.0, 20.0, 3.5),
],
tests: vec![],
}
}
/// GROUPED PAIRING: `grouped` emits ONE sub-table per format, each pairing our
/// codec against its direct rival — 4 rows in 2 groups — not one flat 4-way
/// table. `drop=` strips the format-level columns from a decompressor mashup.
#[test]
fn mashup_grouped_pairs_each_codec_with_its_rival() {
let r = codec_run();
let c = ctx(Path::new("/tmp"), Some(&r));
let mut args = HashMap::new();
args.insert("self".to_string(), "zoomie".to_string());
args.insert("grouped".to_string(), "true".to_string());
args.insert("drop".to_string(), "compress_mbs,ratio".to_string());
let out = render_mashup(&c, &args).unwrap();
// Two per-format group headers.
assert!(out.contains("**gzip**"), "missing gzip group header: {out}");
assert!(
out.contains("**bzip2**"),
"missing bzip2 group header: {out}"
);
// Every codec + rival row present.
for row in ["zoomie-lgz", "gzip", "zoomie-lbzip2", "bzip2"] {
assert!(out.contains(row), "missing row {row}: {out}");
}
// The gzip group header precedes its rows and the bzip2 group.
let gz = out.find("**gzip**").unwrap();
let bz = out.find("**bzip2**").unwrap();
assert!(gz < bz, "gzip group must precede bzip2 group: {out}");
// `drop=` stripped the format-level columns; the decode metric survives.
// Match the column-HEADER form (`| compress_mbs |`) so the check isn't
// fooled by `decompress_mbs`, which contains `compress_mbs` as a substring.
assert!(
out.contains("decompress_mbs"),
"decode column dropped: {out}"
);
assert!(
!out.contains("| compress_mbs |"),
"compress_mbs column not dropped: {out}"
);
assert!(
!out.contains("| ratio |"),
"ratio column not dropped: {out}"
);
// Our codec wins the decode column in each group → bolded.
assert!(
out.contains("**900**"),
"gzip-group winner not bolded: {out}"
);
assert!(
out.contains("**700**"),
"bzip2-group winner not bolded: {out}"
);
// The run-provenance header appears exactly once (shared across groups).
assert_eq!(
out.matches("32 cores").count(),
1,
"header repeated per group: {out}"
);
}
/// A codec run carrying a multi-core base row (`zoomies-lgz`), its
/// single-thread `_st` variant (`zoomies-lgz_st`), and the format rival
/// (`gzip`) — the znippy-zoomies "lens B" single-core-vs-multi-core shape.
fn variant_run() -> BenchRun {
let mk = |name: &str, decode: f64| {
let mut m: Map<String, Value> = Map::new();
m.insert("decompress_mbs".into(), Value::from(decode));
nornir_bench::BenchResult {
name: name.into(),
metrics: m,
..Default::default()
}
};
BenchRun {
id: Default::default(),
machine_facts: None,
date: "2026-07-08".into(),
timestamp: None,
version: "0.1.0".into(),
machine: "test".into(),
cores: 32,
results: vec![
mk("zoomies-lgz", 900.0), // multi-core base
mk("zoomies-lgz_st", 250.0), // single-thread variant
mk("gzip", 500.0), // format rival
],
tests: vec![],
}
}
/// VARIANT DIMENSION: `variant=st` keeps a single-thread (`_st`) bench a
/// DISTINCT mashup row from its multi-core base, and (with `pairing=grouped`)
/// both land as two rows under the SAME format group — the single-core and
/// multi-core stories side by side vs the rival. Off by default the `_st`
/// row collapses into its base system (one row).
#[test]
fn mashup_variant_keeps_st_as_distinct_grouped_row() {
let r = variant_run();
let c = ctx(Path::new("/tmp"), Some(&r));
// OPT-IN: variant=st + grouped → base and `_st` are two distinct rows.
let mut args = HashMap::new();
args.insert("self".to_string(), "zoomies".to_string());
args.insert("pairing".to_string(), "grouped".to_string());
args.insert("variant".to_string(), "st".to_string());
let out = render_mashup(&c, &args).unwrap();
// One `gzip` format group holding BOTH of our variants + the rival.
assert!(out.contains("**gzip**"), "missing gzip group header: {out}");
assert_eq!(
out.matches("**gzip**").count(),
1,
"variants must share ONE group: {out}"
);
for row in ["zoomies-lgz_st", "zoomies-lgz", "gzip"] {
assert!(out.contains(row), "missing row {row}: {out}");
}
// Two DISTINCT nornir rows: the base appears as a row label AND the `_st`
// variant appears as its own row label (a leading `| zoomies-lgz… |`).
assert!(
out.contains("| zoomies-lgz |"),
"base row not rendered distinctly: {out}"
);
assert!(
out.contains("| zoomies-lgz_st |"),
"`_st` variant row not rendered distinctly: {out}"
);
// DEFAULT (no `variant=`): the `_st` row collapses into its base — a
// single nornir row, no distinct `_st` row.
let mut base_args = HashMap::new();
base_args.insert("self".to_string(), "zoomies".to_string());
base_args.insert("pairing".to_string(), "grouped".to_string());
let base_out = render_mashup(&c, &base_args).unwrap();
assert!(
!base_out.contains("| zoomies-lgz_st |"),
"without variant= the `_st` row must collapse, not render distinctly: {base_out}"
);
}
/// The mashup's VARIANT `_st` notion is literally the core-saturation gate's:
/// both route through the ONE shared helper in `nornir_bench`, so a name the
/// mashup keeps distinct is exactly the one the gate exempts — they cannot
/// drift apart.
#[test]
fn st_suffix_notion_is_shared_with_the_gate() {
assert!(nornir_bench::is_single_thread("zoomies-lgz_st"));
assert!(!nornir_bench::is_single_thread("zoomies-lgz"));
assert_eq!(
nornir_bench::strip_single_thread("zoomies-lgz_st"),
"zoomies-lgz"
);
// Variant-aware token: distinct only when opted in.
assert_eq!(
system_token_variant("zoomies-lgz_st", true),
"zoomies-lgz_st"
);
assert_eq!(system_token_variant("zoomies-lgz_st", false), "zoomies-lgz");
assert_eq!(system_token_variant("zoomies-lgz", true), "zoomies-lgz");
}
/// Without `grouped`, the same run renders the original flat row-wise table
/// (one table, no per-format sub-headers) — grouped pairing is opt-in.
#[test]
fn mashup_without_grouped_stays_flat() {
let r = codec_run();
let c = ctx(Path::new("/tmp"), Some(&r));
let mut args = HashMap::new();
args.insert("self".to_string(), "zoomie".to_string());
let out = render_mashup(&c, &args).unwrap();
// A single workload table: one header row, all four systems as rows.
assert_eq!(
out.matches("| workload |").count(),
1,
"expected one flat table: {out}"
);
}
/// Rows-are-systems shape (the skade-katalog case): `winner=row` must bold
/// the best cell *down each column*, not the smallest percentile per row.
fn systems_run() -> BenchRun {
let mk = |name: &str, ops: f64, p50: f64, p99: f64| {
let mut m: Map<String, Value> = Map::new();
m.insert("ops_sec".into(), Value::from(ops));
m.insert("p50_us".into(), Value::from(p50));
m.insert("p99_us".into(), Value::from(p99));
nornir_bench::BenchResult {
name: name.into(),
metrics: m,
..Default::default()
}
};
BenchRun {
id: Default::default(),
machine_facts: None,
date: "2026-06-08".into(),
timestamp: None,
version: "0.4.0".into(),
machine: "oden".into(),
cores: 32,
results: vec![
mk("nessie_table_exists", 5270.0, 156.51, 882.0),
mk("nornir_embedded_table_exists", 2345144.0, 0.30, 0.40),
mk("polaris_table_exists", 3282.0, 291.91, 457.0),
],
tests: vec![],
}
}
#[test]
fn benches_winner_by_row_bolds_best_system_per_column() {
let r = systems_run();
let c = ctx(Path::new("/tmp"), Some(&r));
let mut args = HashMap::new();
args.insert("winner".to_string(), "row".to_string());
let out = render_benches(&c, &args).unwrap();
// nornir_embedded wins every column: highest ops/sec, lowest p50 + p99.
// The ops/sec column is forced to a uniform M with enough decimals for the
// smallest also-ran (polaris 0.00328M) to keep its digits, so the leader
// renders at that same precision (2.34514M) rather than a lone "2.35M".
assert!(
out.contains("**2.34514M**"),
"ops/sec winner not bolded: {out}"
);
assert!(
out.contains("**0.3**") || out.contains("**0.30**"),
"p50 winner not bolded: {out}"
);
assert!(
out.contains("**0.4**") || out.contains("**0.40**"),
"p99 winner not bolded: {out}"
);
// The losers' percentiles must NOT be bolded (the old bug bolded p50 per row).
assert!(
!out.contains("**156.51**"),
"nessie p50 wrongly bolded: {out}"
);
assert!(
!out.contains("**291.91**"),
"polaris p50 wrongly bolded: {out}"
);
}
/// Winner-tie must favour OURS (the znippy-zoomies bug): when ours ties a rival
/// for the best value in a column, `self=` makes ours win the bold — and a
/// single `self=` token claims its whole `-`-family (both `zoomies-*` rows).
#[test]
fn benches_winner_tie_favours_ours() {
// compress_mbs: zoomies-lgz, pigz, gzip all tie at 256.39 (higher-better).
// ratio: zoomies-lbz2, bzip2 tie at 7.50 (higher-better).
let mk = |name: &str, compress: f64, ratio: f64| {
let mut m: Map<String, Value> = Map::new();
m.insert("compress_mbs".into(), Value::from(compress));
m.insert("ratio".into(), Value::from(ratio));
nornir_bench::BenchResult {
name: name.into(),
metrics: m,
..Default::default()
}
};
let run = BenchRun {
id: Default::default(),
machine_facts: None,
date: "2026-07-04".into(),
timestamp: None,
version: "0.1.15".into(),
machine: "ryzen".into(),
cores: 12,
results: vec![
mk("zoomies-lbz2", 22.84, 7.50),
mk("zoomies-lgz", 256.39, 4.64),
mk("pigz", 256.39, 4.64),
mk("gzip", 256.39, 4.64),
mk("bzip2", 22.84, 7.50),
],
tests: vec![],
};
let c = ctx(Path::new("/tmp"), Some(&run));
// With self= pinning zoomies-lbz2, BOTH zoomies rows count as ours.
let mut args = HashMap::new();
args.insert("winner".to_string(), "row".to_string());
args.insert("self".to_string(), "zoomies-lbz2".to_string());
args.insert(
"best".to_string(),
"ratio:high,compress_mbs:high".to_string(),
);
let out = render_benches(&c, &args).unwrap();
// Ours wins each tie: exactly one bold per column, on a zoomies row.
assert_eq!(
out.matches("**256.39**").count(),
1,
"compress_mbs bold count: {out}"
);
assert_eq!(
out.matches("**7.5**").count() + out.matches("**7.50**").count(),
1,
"ratio bold count: {out}"
);
let lgz = out
.lines()
.find(|l| l.starts_with("| zoomies-lgz |"))
.unwrap();
assert!(
lgz.contains("**256.39**"),
"zoomies-lgz should win compress_mbs tie: {out}"
);
let lbz2 = out
.lines()
.find(|l| l.starts_with("| zoomies-lbz2 |"))
.unwrap();
assert!(
lbz2.contains("**7.5**") || lbz2.contains("**7.50**"),
"zoomies-lbz2 should win ratio tie: {out}"
);
// The tied rivals must NOT steal the bold.
let gzip = out.lines().find(|l| l.starts_with("| gzip |")).unwrap();
assert!(
!gzip.contains("**256.39**"),
"gzip wrongly bolded (old bug): {out}"
);
let bzip2 = out.lines().find(|l| l.starts_with("| bzip2 |")).unwrap();
assert!(
!bzip2.contains("**7.5**") && !bzip2.contains("**7.50**"),
"bzip2 wrongly bolded: {out}"
);
}
/// K2 inject-assert: `mode=compact` must collapse the three rivals to a
/// SINGLE synthetic best-of row whose every cell is the absurd-best value per
/// column (max ops/sec, min p50/p99), drawn from whichever rival held it.
#[test]
fn benches_compact_mode_takes_best_per_column() {
let r = systems_run(); // nessie / nornir_embedded / polaris
let c = ctx(Path::new("/tmp"), Some(&r));
let mut args = HashMap::new();
args.insert("mode".to_string(), "compact".to_string());
The file goes on; the console shows all of it.