use haematite::db::vacuum::{
Mode as VacuumMode, Report as VacuumReport, SweepBlocker, WalTailReport,
};
use std::fmt::{self, Write as _};
pub fn render(report: &VacuumReport) -> Result<String, fmt::Error> {
let mut out = String::new();
header(&mut out, report)?;
totals(&mut out, report)?;
shards(&mut out, report)?;
marks(&mut out, report)?;
metadata(&mut out, report)?;
uninventoried(&mut out, report)?;
blockers(&mut out, report)?;
sweep(&mut out, report)?;
writeln!(out, "trust boundary:\n {}", report.trust_boundary)?;
Ok(out)
}
fn header(out: &mut String, report: &VacuumReport) -> fmt::Result {
let banner = match report.mode {
VacuumMode::Stats => "haematite vacuum stats — report-only, nothing deleted",
VacuumMode::Sweep => match &report.sweep {
Some(summary) if summary.executed => "haematite vacuum sweep — nodes deleted",
_ => "haematite vacuum sweep — DRY RUN, nothing deleted",
},
};
writeln!(out, "{banner}")?;
writeln!(out, "data dir: {}", report.data_dir.display())?;
writeln!(
out,
"shard count: {} · duration: {} ms",
report.shard_count, report.duration_ms
)?;
writeln!(
out,
"torn tails: {} set-aside file(s)",
report.torn_tail_count
)?;
writeln!(
out,
"install debris: {} file(s), {} bytes, never nodes",
report.debris.files, report.debris.bytes
)?;
if report.lock_anchor_created {
writeln!(
out,
"note: this legacy dir had no writer.lock; acquiring the advisory lock \
created one empty anchor file — the run's single write"
)?;
}
if report.beside_writer {
writeln!(
out,
"note: read BESIDE a live writer, no lock held — never a deletion plan; see \
the trust boundary for the error in these numbers"
)?;
}
if !report.moving_shards.is_empty() {
writeln!(
out,
"MOVING (the writer moved them through every read; left out of the totals): \
shards {:?}",
report.moving_shards
)?;
}
out.push('\n');
Ok(())
}
fn totals(out: &mut String, report: &VacuumReport) -> fmt::Result {
let t = &report.totals;
writeln!(out, "TOTALS")?;
writeln!(
out,
" enumerated {:>12} {:>12}",
group(t.enumerated_nodes),
bytes(t.enumerated_bytes)
)?;
writeln!(
out,
" marked {:>12} {:>12} (reachable from durable pins)",
group(t.marked_nodes),
bytes(t.marked_bytes)
)?;
writeln!(
out,
" unmarked {:>12} {:>12} (version garbage a sweep would reclaim)",
group(t.unmarked_nodes),
bytes(t.unmarked_bytes)
)?;
out.push('\n');
Ok(())
}
fn shards(out: &mut String, report: &VacuumReport) -> fmt::Result {
let lazy = report.never_materialised_shards;
writeln!(out, "per shard:")?;
for shard in &report.shards {
let nodes = &shard.nodes;
write!(
out,
" shard-{:<4} enumerated {} ({}) · marked {} ({}) · unmarked {} ({})",
shard.shard_id,
group(nodes.enumerated_nodes),
bytes(nodes.enumerated_bytes),
group(nodes.marked_nodes),
bytes(nodes.marked_bytes),
group(nodes.unmarked_nodes),
bytes(nodes.unmarked_bytes),
)?;
if !shard.wal_committed_root {
write!(out, " · NO committed root")?;
}
if shard.roots_moved {
write!(out, " · roots MOVED during the read")?;
}
if shard.temp_debris_files > 0 {
write!(
out,
" · debris {} ({})",
shard.temp_debris_files,
bytes(shard.temp_debris_bytes)
)?;
}
match &shard.index_checkpoint {
Some(checkpoint) => {
write!(out, " · index checkpoint {}", bytes(checkpoint.size_bytes))?;
}
None => {
write!(out, " · NO index checkpoint (next spawn walks the tree)")?;
}
}
if shard.index_temp_debris_files > 0 {
write!(
out,
" · index debris {} ({})",
shard.index_temp_debris_files,
bytes(shard.index_temp_debris_bytes)
)?;
}
out.push('\n');
for file in &shard.torn_tails {
writeln!(
out,
" SET-ASIDE: {} · offset {} · length {} bytes",
file.path.display(),
file.offset,
file.length,
)?;
}
if let Some(tail) = shard.wal_tail {
match tail {
WalTailReport::TornLast { offset, length } => {
writeln!(out, " CUT OWED: offset {offset} · length {length} bytes")?;
}
WalTailReport::Overrun { offset, length } => {
writeln!(
out,
" WRITER OPEN REFUSES: Overrun · offset {offset} · \
length {length} bytes"
)?;
}
}
}
for malformed in &shard.malformed {
writeln!(
out,
" MALFORMED: {} — {}",
malformed.path.display(),
malformed.reason
)?;
}
}
if lazy > 0 {
writeln!(out, " ({lazy} shard(s) never materialised — lazy, empty)")?;
}
out.push('\n');
Ok(())
}
fn marks(out: &mut String, report: &VacuumReport) -> fmt::Result {
let m = &report.marks;
writeln!(
out,
"mark sources: WAL roots {} · branch anchors {} · branch heads {} · snapshot roots {}",
m.wal_roots, m.branch_anchors, m.branch_heads, m.snapshot_roots
)?;
writeln!(
out,
"hash verifications: {} (every traversed node re-hashed before trust)",
group(report.verified_nodes)
)?;
out.push('\n');
Ok(())
}
fn metadata(out: &mut String, report: &VacuumReport) -> fmt::Result {
let meta = &report.metadata;
writeln!(out, "metadata:")?;
match &meta.manifest {
Some(manifest) => {
writeln!(
out,
" manifest: {} (generation {})",
manifest.path.display(),
manifest.generation
)?;
for entry in &manifest.entries {
let readable = if entry.source_readable {
"readable"
} else {
"MISSING/UNREADABLE"
};
writeln!(
out,
" {} [{}] — state {} — {}",
entry.path.display(),
kind_name(entry.kind),
entry.state,
readable
)?;
}
}
None => {
writeln!(out, " manifest: none")?;
}
}
writeln!(
out,
" attestation (sweep-only, ignored by stats): {}",
if meta.attestation {
"asserted"
} else {
"not asserted"
}
)?;
if meta.sources.is_empty() {
writeln!(out, " consulted sources: none")?;
} else {
writeln!(out, " consulted sources:")?;
for source in &meta.sources {
write!(
out,
" {} [{} · {:?}] — {} record(s)",
source.path.display(),
kind_name(source.kind),
source.origin,
source.records
)?;
if source.temp_debris_files > 0 {
write!(
out,
" · {} temp debris (not cleaned)",
source.temp_debris_files
)?;
}
if source.foreign_entries > 0 {
write!(out, " · {} foreign entries", source.foreign_entries)?;
}
out.push('\n');
}
}
for missing in &meta.supplied_missing {
writeln!(
out,
" SUPPLIED PATH MISSING: {} (verify for typos — a wrong path here \
would silently omit real metadata)",
missing.display()
)?;
}
out.push('\n');
Ok(())
}
fn uninventoried(out: &mut String, report: &VacuumReport) -> fmt::Result {
if report.uninventoried.is_empty() {
return Ok(());
}
writeln!(
out,
"uninventoried top-level entries (not part of the database model):"
)?;
for entry in &report.uninventoried {
let size = entry
.size_bytes
.map_or_else(|| "size unreadable".to_owned(), bytes);
writeln!(out, " {} [{:?}] — {}", entry.name, entry.kind, size)?;
}
out.push('\n');
Ok(())
}
fn blockers(out: &mut String, report: &VacuumReport) -> fmt::Result {
if report.sweep_blockers.is_empty() {
writeln!(out, "sweep blockers: none observed by this run")?;
out.push('\n');
return Ok(());
}
writeln!(
out,
"sweep blockers (a sweep would REFUSE until each is resolved):"
)?;
for blocker in &report.sweep_blockers {
writeln!(out, " - {}", blocker_line(blocker))?;
}
out.push('\n');
Ok(())
}
fn sweep(out: &mut String, report: &VacuumReport) -> fmt::Result {
let Some(summary) = &report.sweep else {
return Ok(());
};
let verb = if summary.executed {
"deleted"
} else {
"would delete (dry run — nothing removed)"
};
writeln!(
out,
"sweep: {verb} {} node(s), {}",
group(summary.deleted_nodes),
bytes(summary.deleted_bytes)
)?;
out.push('\n');
Ok(())
}
fn blocker_line(blocker: &SweepBlocker) -> String {
match blocker {
SweepBlocker::ShardBeyondCount { id, shard_count } => format!(
"shard-{id} exists but shard_count is {shard_count}: the config's model of \
this directory is wrong, or the data belongs to another database"
),
SweepBlocker::MalformedStoreEntries { shard_id, count } => format!(
"shard-{shard_id} store holds {count} malformed entr(ies): that store's \
layout does not match the model"
),
SweepBlocker::UnreconciledWalTail { shard_id, tail } => {
haematite::db::vacuum::Error::UnreconciledWalTail {
shard_id: *shard_id,
tail: *tail,
}
.to_string()
}
SweepBlocker::MetadataUnattested => "no metadata manifest and no attestation: the \
metadata universe is not positively established"
.to_owned(),
SweepBlocker::StaleManifest { unlisted } => format!(
"the manifest omits the canonical source at {} — the manifest lies",
unlisted.display()
),
SweepBlocker::ManifestEntryUnsettled { path, state } => format!(
"manifest entry {} is in non-terminal state {state}",
path.display()
),
SweepBlocker::ManifestSourceMissing { path } => {
format!("manifest-listed source {} is absent", path.display())
}
SweepBlocker::SourceUnreadable { path, detail } => format!(
"metadata source {} exists but could not be read ({detail})",
path.display()
),
SweepBlocker::SuppliedPathMissing { path } => {
format!("supplied metadata path {} does not exist", path.display())
}
SweepBlocker::UnsupportedDurability => "this platform provides no real \
directory-entry durability barrier; every sweep mode refuses"
.to_owned(),
}
}
const fn kind_name(kind: haematite::db::vacuum::MetadataSourceKind) -> &'static str {
match kind {
haematite::db::vacuum::MetadataSourceKind::RefsDir => "refs dir",
haematite::db::vacuum::MetadataSourceKind::SnapshotRegistry => "snapshot registry",
}
}
fn group(value: usize) -> String {
let digits = value.to_string();
let mut grouped = String::with_capacity(digits.len() + digits.len() / 3);
for (index, digit) in digits.chars().enumerate() {
if index > 0 && (digits.len() - index).is_multiple_of(3) {
grouped.push(',');
}
grouped.push(digit);
}
grouped
}
fn bytes(value: u64) -> String {
const UNITS: [(&str, u64); 4] = [
("TiB", 1 << 40),
("GiB", 1 << 30),
("MiB", 1 << 20),
("KiB", 1 << 10),
];
for (unit, factor) in UNITS {
if value >= 4 * factor {
let tenths =
u64::try_from(u128::from(value) * 10 / u128::from(factor)).unwrap_or(u64::MAX);
return format!("{}.{} {unit}", tenths / 10, tenths % 10);
}
}
format!("{value} B")
}
#[cfg(test)]
#[path = "render_tests.rs"]
mod tests;