holger-recipe/tests/support/known_crates.rs
//! **A guard that names a crate that does not exist proves nothing** (A32-17, HCUT
//! 2026-10-26). The tree guards forbid crates by name; every name (or `prefix*`) must be a
//! crate that exists: a package in holger or a sibling checkout, a crate in one of their
//! `Cargo.lock`s, or one of the named crates.io crates or future crates below, each with
//! its reason. Shared by `downstream_shape.rs` and `every_shape_stays_slim.rs`
//! (tests/support/: no test target of its own).
#![allow(dead_code)]
use std::collections::BTreeSet;
use std::path::{Path, PathBuf};
/// crates.io crates the guards forbid that no tree here resolves (that is the point):
/// the internet clients a default or downstream build must never pull in.
pub const ON_CRATES_IO: &[(&str, &str)] = &[
("isahc", "an HTTP client on crates.io (libcurl)"),
("curl", "libcurl's bindings on crates.io"),
("attohttpc", "an HTTP client on crates.io"),
("surf", "an HTTP client on crates.io"),
("instant-acme", "an ACME client on crates.io (it dials the CA)"),
("hyper-tls", "hyper's native-tls connector on crates.io"),
("native-tls", "the platform TLS on crates.io (pulls openssl on Linux)"),
("openssl", "OpenSSL's bindings on crates.io"),
("hyper-rustls", "hyper's rustls connector on crates.io (an HTTPS client)"),
("rpgp", "the OpenPGP implementation's crates.io name (its crate is `pgp`)"),
("rmcp", "the Rust MCP SDK on crates.io: nornir's `mcp` feature links it, and an online upstream's MCP door would"),
("rmcp-macros", "rmcp's macros on crates.io (the `macros` feature nornir turns on)"),
];
/// **Crates of a sibling repository that is not always checked out beside holger**
/// (OX-HOLF 2026-10-27): (crate, sibling repository, why it is forbidden). The guard knows
/// them without the checkout, so it holds in a lone clone or a CI tree without the sibling.
/// It keeps its strength: when the sibling IS checked out, [`sibling_pin_problems`] fails
/// the guard unless the crate is still a package there (a rename or a removal is caught),
/// and when it is not, the guard prints each name it took on this pin.
pub const IN_A_SIBLING: &[(&str, &str, &str)] = &[
(
"gunnar-public-site",
"gunnar-ui",
"gunnar-ui's public site, SEO and IndexNow module (crates/gunnar-public-site)",
),
(
"gunnar-mcp",
"gunnar-ui",
"gunnar-ui's MCP connector (crates/gunnar-mcp, behind gunnar-ui-server's `mcp` feature)",
),
];
/// Pure: for each pin, `packages_of(repo)` gives the package names of that sibling
/// checkout, or `None` when it is not checked out. Returns (pins the checkout contradicts,
/// pins taken on trust because the sibling is absent).
pub fn pin_problems(
pins: &[(&str, &str, &str)],
packages_of: impl Fn(&str) -> Option<BTreeSet<String>>,
) -> (Vec<String>, Vec<String>) {
let (mut wrong, mut trusted) = (Vec::new(), Vec::new());
for (krate, repo, _) in pins {
match packages_of(repo) {
Some(pkgs) if pkgs.contains(*krate) => {}
Some(_) => wrong.push(format!(
"{krate}: ../{repo} is checked out and has no package `{krate}`"
)),
None => trusted.push(format!("{krate} (../{repo} is not checked out)")),
}
}
(wrong, trusted)
}
/// The package names (only `[package]`, never a lock) under `dir`.
pub fn packages_under(dir: &Path) -> BTreeSet<String> {
let mut files = Vec::new();
walk(dir, 6, &mut files);
let mut names = BTreeSet::new();
for f in files
.iter()
.filter(|f| f.file_name().is_some_and(|n| n == "Cargo.toml"))
{
let Ok(text) = std::fs::read_to_string(f) else {
continue;
};
let mut in_package = false;
for l in text.lines().map(str::trim) {
if l.starts_with('[') {
in_package = l == "[package]";
} else if in_package && l.starts_with("name = \"") {
names.insert(
l.trim_start_matches("name = \"")
.trim_end_matches('"')
.to_string(),
);
}
}
}
names
}
/// The pins checked against the siblings beside this checkout: a pin a checkout
/// contradicts fails; each pin taken on trust is printed by name.
pub fn sibling_pins_hold() {
let parent = parent_of_repo();
let (wrong, trusted) = pin_problems(IN_A_SIBLING, |repo| {
let dir = parent.join(repo);
dir.join("Cargo.toml")
.is_file()
.then(|| packages_under(&dir))
});
assert!(
wrong.is_empty(),
"a pinned sibling crate is gone from its checkout: {wrong:?}"
);
for t in trusted {
eprintln!("known_crates: taken on its pin, not checked: {t}");
}
}
/// **The MCP crates the guards forbid** (HCON-7; owner rule 7: no MCP in the default and the
/// downstream builds, by name). The real ones, found with `cargo tree --all-features` over
/// holger and the shared crates: no Holger graph links an MCP crate; nornir's `mcp` feature
/// links `rmcp` (and its `rmcp-macros`), gunnar-ui's `mcp` feature builds `gunnar-mcp`, and
/// Holger's own door would be `holger-mcp`.
pub const MCP: &[&str] = &["rmcp", "rmcp-macros", "holger-mcp", "gunnar-mcp"];
/// Crates not written yet that a guard already keeps out, each with why.
pub const FUTURE: &[(&str, &str)] = &[(
"holger-mcp",
"owner rule 7 names Holger's MCP door; it has no crate yet, and when one is written under this name no open row may link it",
)];
fn parent_of_repo() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.parent()
.unwrap()
.to_path_buf()
}
fn walk(dir: &Path, depth: usize, out: &mut Vec<PathBuf>) {
if depth == 0 {
return;
}
let Ok(rd) = std::fs::read_dir(dir) else {
return;
};
for e in rd.flatten() {
let p = e.path();
let name = e.file_name().to_string_lossy().to_string();
if p.is_dir() {
if name.starts_with('.') || name == "target" || name == "node_modules" {
continue;
}
walk(&p, depth - 1, out);
} else if name == "Cargo.toml" || name == "Cargo.lock" {
out.push(p);
}
}
}
/// Every crate name holger and its sibling checkouts know: their packages and their locks.
pub fn known_crates() -> BTreeSet<String> {
let names = known_under(&parent_of_repo(), 6);
assert!(
names.len() > 300,
"the sibling trees name {} crates; are the siblings checked out beside holger?",
names.len()
);
names
}
/// The repository root (holger itself).
pub fn holger_root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.to_path_buf()
}
/// The crate names `dir` knows (its packages and its locks), `depth` directories down.
pub fn known_under(dir: &Path, depth: usize) -> BTreeSet<String> {
let mut files = Vec::new();
walk(dir, depth, &mut files);
let mut names = BTreeSet::new();
for f in files {
let Ok(text) = std::fs::read_to_string(&f) else {
continue;
};
let is_lock = f.file_name().is_some_and(|n| n == "Cargo.lock");
let mut in_package = false;
for line in text.lines() {
let l = line.trim();
if l.starts_with('[') {
in_package = l == "[package]" || l == "[[package]]";
continue;
}
if (in_package || is_lock) && l.starts_with("name = \"") {
names.insert(
l.trim_start_matches("name = \"")
.trim_end_matches('"')
.to_string(),
);
}
}
}
names
}
/// Pure: the forbidden names that name no known crate (and are no named crates.io or
/// future crate). A `prefix*` must match at least one known crate.
pub fn unknown<'a>(forbidden: &[&'a str], known: &BTreeSet<String>) -> Vec<&'a str> {
let named = |n: &str| {
ON_CRATES_IO.iter().chain(FUTURE).any(|(c, _)| *c == n)
|| IN_A_SIBLING.iter().any(|(c, _, _)| *c == n)
};
forbidden
.iter()
.copied()
.filter(|f| match f.strip_suffix('*') {
Some(prefix) => !known.iter().any(|k| k.starts_with(prefix)),
None => !known.contains(*f) && !named(f),
})
.collect()
}