Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/edda

crates/nornir-testmatrix/examples/inventory.rs

//! **The verb** — `cargo run --release --example inventory -- --roots <repo> …`
//!
//! Answers, for a whole constellation of checkouts: *what does it measure, and
//! what does it claim?* Every bench target (cargo `[[bench]]`, criterion, a
//! `nornir-bench` arm, the house `examples/bench*.rs` form — including the ones
//! in detached manifests that open their own `[workspace]`), and every declared
//! test-matrix row against what actually emits it.
//!
//! It is an example rather than a `[[bin]]` for the same reason
//! `examples/target_compile.rs` is: this crate's `[dependencies]` block is
//! capped at `serde` + `serde_json` + `anyhow` so a leaf repo pulls it cheaply,
//! and an example is the crate's established door shape.
//!
//! # Flags
//!
//! * `--roots <path> [<path> …]` — the checkouts to walk. Repeatable; every
//!   path after the flag until the next flag is a root.
//! * `--probe-compile` — also run `cargo bench --no-run` per manifest that owns
//!   a bench target and mark the ones that do not build
//!   `declared-not-emitted`. Expensive (a full bench build per manifest); off by
//!   default so the enumeration stays cheap.
//! * `--json <path>` — also write the machine half.
//! * `--out <path>` — write the rendered table there instead of stdout.
//!
//! **Exit status is 0 only when the walk actually looked at something.** A run
//! that enumerated zero repos, or whose every `cargo metadata` refused, exits
//! non-zero — a survey that looked at nothing is shaped exactly like a clean
//! one, and that is the failure this whole module exists to refuse.

use std::path::PathBuf;
use std::process::ExitCode;

use nornir_testmatrix::inventory::{
    self, State, forbidden_vocabulary, render, scan_roots, to_json,
};

fn main() -> ExitCode {
    let mut roots: Vec<PathBuf> = Vec::new();
    let mut json: Option<PathBuf> = None;
    let mut out: Option<PathBuf> = None;
    let mut probe = false;

    let args: Vec<String> = std::env::args().skip(1).collect();
    let mut i = 0;
    while i < args.len() {
        match args[i].as_str() {
            "--roots" => {
                i += 1;
                while i < args.len() && !args[i].starts_with("--") {
                    roots.push(PathBuf::from(&args[i]));
                    i += 1;
                }
            }
            "--probe-compile" => {
                probe = true;
                i += 1;
            }
            "--json" => {
                i += 1;
                json = args.get(i).map(PathBuf::from);
                i += 1;
            }
            "--out" => {
                i += 1;
                out = args.get(i).map(PathBuf::from);
                i += 1;
            }
            "-h" | "--help" => {
                eprintln!(
                    "usage: inventory --roots <repo> [<repo> …] [--probe-compile] [--json <path>] [--out <path>]"
                );
                return ExitCode::SUCCESS;
            }
            other => {
                // A bare path with no --roots in front of it is still a root.
                if other.starts_with("--") {
                    eprintln!("unknown flag: {other}");
                    return ExitCode::from(2);
                }
                roots.push(PathBuf::from(other));
                i += 1;
            }
        }
    }

    if roots.is_empty() {
        eprintln!("inventory: no roots given — nothing was looked at");
        return ExitCode::from(2);
    }

    let missing: Vec<&PathBuf> = roots.iter().filter(|r| !r.is_dir()).collect();
    for m in &missing {
        eprintln!("inventory: not a directory: {}", m.display());
    }
    let roots: Vec<PathBuf> = roots.into_iter().filter(|r| r.is_dir()).collect();
    if roots.is_empty() {
        eprintln!("inventory: every root was unreadable");
        return ExitCode::from(2);
    }

    let inv = scan_roots(&roots, probe);
    let text = render(&inv);

    // The renderer's OWN vocabulary is guarded by a test
    // (`the_renderers_own_vocabulary_is_clean`) and cannot be dirty here. What
    // this scan finds is the constellation's own identifiers still carrying the
    // word — a real finding, reported as one, never a verdict on the render.
    let quoted = forbidden_vocabulary(&text);

    match &out {
        Some(p) => {
            if let Err(e) = std::fs::write(p, &text) {
                eprintln!("inventory: write {}: {e}", p.display());
                return ExitCode::FAILURE;
            }
            eprintln!("inventory: wrote {}", p.display());
        }
        None => print!("{text}"),
    }

    if let Some(p) = &json {
        match to_json(&inv) {
            Ok(j) => {
                if let Err(e) = std::fs::write(p, j) {
                    eprintln!("inventory: write {}: {e}", p.display());
                    return ExitCode::FAILURE;
                }
                eprintln!("inventory: wrote {}", p.display());
            }
            Err(e) => {
                eprintln!("inventory: serialise: {e}");
                return ExitCode::FAILURE;
            }
        }
    }

    // The counts, on stderr so a redirected table stays a table.
    let dne = inv.declared_not_emitted().len();
    let broken = inv.broken_benches().len();
    let prose = inv.prose_rows().len();
    eprintln!(
        "inventory: {} repos · {} benches · {} rows · {dne} declared-not-emitted · {broken} bench declarations that produce nothing · {prose} row ids that carry prose instead of a key",
        inv.repos.len(),
        inv.bench_count(),
        inv.row_count()
    );

    if !quoted.is_empty() {
        eprintln!(
            "inventory: {} line(s) quote a repo identifier that still uses the word {:?} — the model's word is typ",
            quoted.len(),
            inventory::FORBIDDEN_WORD
        );
    }

    let looked_at_nothing = inv
        .repos
        .iter()
        .all(|r| r.benches.iter().all(|b| b.state == State::Absent) && r.rows.is_empty());
    if looked_at_nothing {
        eprintln!("inventory: the walk found nothing anywhere — that is a refusal, not a pass");
        return ExitCode::FAILURE;
    }
    ExitCode::SUCCESS
}