crates/nornir-build-thing/src/analyze.rs
//! The **`ArtifactAnalyzer` injection seam** (task #65 slice 4b) — the post-produce
//! hook the `assemble()` [`dispatch`](crate::assemble) fires right after an
//! artifact is written, keyed by the artifact's `content_id`.
//!
//! It mirrors the existing [`ImageBuilder`](crate::image_build::ImageBuilder) /
//! [`VmImageBuilder`](crate::image_build::VmImageBuilder) injection exactly: a thin
//! trait with a **no-op default** so the dep-light leaf core (an xtask build) pays
//! nothing and links no `object` / `gimli`, while nornir injects the real
//! `nornir-introspect::binscan::analyze` through
//! [`Builders::with_analyzer`](crate::assemble::Builders::with_analyzer). The
//! analyzer returns [`BuildInfoRow`]s that dispatch threads into the same
//! `ctx.info` sink + the [`Assembled`](crate::assemble::Assembled) lineage, so
//! `produce → analyse → persist` is one pass and every binary fact joins
//! `build_info` on `content_id` for free.
use std::path::Path;
use crate::build_info::BuildInfoRow;
/// Post-produce hook: analyse one artifact file the dispatch just wrote and return
/// warehouse-ready [`BuildInfoRow`]s (size / hardening / SBOM facts), keyed by the
/// artifact's `content_id`. The default is a **no-op** — the leaf pays nothing; a
/// real analyzer (nornir's `binscan`) is injected. `build_id` ties the rows to the
/// producing build's provenance record.
pub trait ArtifactAnalyzer {
/// Analyse `artifact_path` and return facts keyed by `content_id`. Default:
/// no facts (the leaf runs no analysis).
fn analyze(
&self,
_artifact_path: &Path,
_content_id: &str,
_build_id: &str,
) -> Vec<BuildInfoRow> {
Vec::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
/// The default analyzer is an honest no-op — a unit type that implements the
/// trait produces no rows (so the leaf core stays free).
#[test]
fn default_analyzer_is_noop() {
struct Noop;
impl ArtifactAnalyzer for Noop {}
let rows = Noop.analyze(Path::new("/bin/true"), "cafef00d", "build-1");
assert!(rows.is_empty(), "default analyzer yields no facts");
}
}