use std::collections::BTreeMap;
use serde_json::Value;
pub struct SurfaceEntry {
pub path: String,
pub kind: String,
pub signature: String,
pub pkg: String,
pub summary: String,
pub docs: String,
}
pub fn render(pkg: &str, entries: &[SurfaceEntry], baseline: &Value) -> String {
let mut measured: BTreeMap<String, String> = BTreeMap::new();
if let Some(items) = baseline["items"].as_object() {
for (id, m) in items {
let target = m["covers"].as_str().filter(|c| !c.is_empty()).unwrap_or(id);
let mut facts = Vec::new();
if let Some(i) = m["perfcnt"]["instructions"].as_u64() {
facts.push(format!("{i} instructions/iter"));
}
if let Some(ns) = m["walltime"]["median_ns"].as_f64() {
facts.push(format!("{ns:.0}ns median"));
}
if let Some(a) = m["alloc"]["allocs"].as_u64() {
facts.push(format!("{a} allocs/iter"));
}
if let Some(p) = m["asyncexec"]["polls"].as_u64() {
facts.push(format!("{p} polls/iter"));
}
if !facts.is_empty() {
measured.insert(target.to_string(), facts.join(", "));
}
}
}
let mut groups: BTreeMap<&str, Vec<&SurfaceEntry>> = BTreeMap::new();
for e in entries {
if e.kind == "module" {
continue;
}
groups.entry(e.pkg.as_str()).or_default().push(e);
}
let multi_crate = groups.len() > 1;
let item_level = if multi_crate { "####" } else { "###" };
let mut out = format!(
"# {pkg}\n\n> Generated by `cargo soothfast report`, derived from the current code and\n> measurements rather than hand-written. Trust it over any prose.\n\n## Public API\n\n"
);
for (crate_name, items) in &groups {
if multi_crate {
out.push_str(&format!("### {crate_name}\n\n"));
}
for e in items {
let head = if e.signature.is_empty() {
e.path.as_str()
} else {
e.signature.as_str()
};
out.push_str(&format!("{item_level} `{head}`\n\n"));
let doc = e.docs.trim();
if !doc.is_empty() {
out.push_str(&nest_headings(doc, item_level.len()));
out.push_str("\n\n");
}
if let Some(facts) = measured.get(&e.path) {
out.push_str(&format!("**Measured:** {facts}\n\n"));
}
}
}
out
}
fn nest_headings(text: &str, item_depth: usize) -> String {
let mut in_fence = false;
text.lines()
.map(|line| {
if line.trim_start().starts_with("```") {
in_fence = !in_fence;
return line.to_string();
}
if in_fence {
return line.to_string();
}
let hashes = line.chars().take_while(|&c| c == '#').count();
if hashes > 0 && hashes <= 6 && line.as_bytes().get(hashes) == Some(&b' ') {
let depth = (item_depth + hashes).min(6);
format!("{}{}", "#".repeat(depth), &line[hashes..])
} else {
line.to_string()
}
})
.collect::<Vec<_>>()
.join("\n")
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
fn entry(pkg: &str, path: &str, docs: &str) -> SurfaceEntry {
SurfaceEntry {
path: path.to_string(),
kind: "function".to_string(),
signature: format!("pub fn {path}()"),
pkg: pkg.to_string(),
summary: docs.lines().next().unwrap_or("").to_string(),
docs: docs.to_string(),
}
}
#[test]
fn nest_headings_shifts_rustdoc_sections_below_the_item_heading() {
let doc = "Does a thing.\n\n# Panics\n\nIf you misuse it.";
assert_eq!(
nest_headings(doc, 4),
"Does a thing.\n\n##### Panics\n\nIf you misuse it."
);
}
#[test]
fn nest_headings_ignores_hashes_inside_fenced_code() {
let doc = "Usage:\n\n```ignore\n# not a heading\n#[attr]\nfn f() {}\n```\n\n# Examples";
let shifted = nest_headings(doc, 3);
assert!(shifted.contains("```ignore\n# not a heading\n#[attr]"));
assert!(shifted.contains("#### Examples"));
}
#[test]
fn nest_headings_caps_at_depth_six() {
assert_eq!(nest_headings("###### already max", 4), "###### already max");
}
#[test]
fn render_groups_multiple_crates_under_their_own_heading() {
let entries = vec![
entry("soothfast", "soothfast::keep", "Optimizer barrier."),
entry(
"soothfast-macros",
"soothfast_macros::bench",
"Register a bench.",
),
];
let out = render("soothfast, soothfast-macros", &entries, &json!({}));
assert!(out.contains("### soothfast\n"));
assert!(out.contains("### soothfast-macros\n"));
assert!(out.contains("#### `pub fn soothfast::keep()`"));
}
#[test]
fn render_single_crate_omits_the_redundant_crate_heading() {
let entries = vec![entry("soothfast", "soothfast::keep", "Optimizer barrier.")];
let out = render("soothfast", &entries, &json!({}));
assert!(!out.contains("### soothfast\n"));
assert!(out.contains("### `pub fn soothfast::keep()`"));
}
#[test]
fn render_attaches_measured_facts_by_covers_or_id() {
let entries = vec![entry("soothfast", "soothfast::keep", "Optimizer barrier.")];
let baseline = json!({
"items": {
"soothfast::keep": {
"perfcnt": { "instructions": 42 },
"walltime": { "median_ns": 12.0 },
}
}
});
let out = render("soothfast", &entries, &baseline);
assert!(out.contains("**Measured:** 42 instructions/iter, 12ns median"));
}
#[test]
fn render_skips_module_kind_entries() {
let mut e = entry("soothfast", "soothfast::registry", "");
e.kind = "module".to_string();
let out = render("soothfast", &[e], &json!({}));
assert!(!out.contains("registry"));
}
}