use std::process::Command;
fn parts(version: &str) -> (u32, u32, u32) {
let mut it = version.split('.').map(|p| p.parse().unwrap_or(0));
(
it.next().unwrap_or(0),
it.next().unwrap_or(0),
it.next().unwrap_or(0),
)
}
fn metadata() -> Option<String> {
let out = Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
.args(["metadata", "--format-version", "1"])
.output()
.ok()?;
out.status
.success()
.then(|| String::from_utf8_lossy(&out.stdout).into_owned())
}
fn declared_floors(json: &str) -> Vec<(String, String)> {
let mut out = Vec::new();
let mut rest = json;
while let Some(at) = rest.find("\"rust_version\":") {
let after = &rest[at + "\"rust_version\":".len()..];
let Some(open) = after.find('"') else { break };
let Some(close) = after[open + 1..].find('"') else {
break;
};
let version = after[open + 1..open + 1 + close].to_owned();
let name = rest[..at].rfind("\"name\":").map_or_else(
|| "?".into(),
|n| {
let seg = &rest[n + "\"name\":".len()..at];
seg.trim()
.trim_start_matches('"')
.split('"')
.next()
.unwrap_or("?")
.to_owned()
},
);
if !version.is_empty() {
out.push((name, version));
}
rest = &after[open + 1 + close..];
}
out
}
fn runtime_packages() -> Vec<String> {
let out = Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
.args(["tree", "-e", "no-dev", "--prefix", "none"])
.output();
let Ok(out) = out else { return Vec::new() };
if !out.status.success() {
return Vec::new();
}
let mut names: Vec<String> = String::from_utf8_lossy(&out.stdout)
.lines()
.filter_map(|line| line.split_whitespace().next())
.map(str::to_owned)
.collect();
names.sort();
names.dedup();
names
}
#[test]
fn the_manifest_publishes_the_floor_the_tree_actually_requires() {
let Some(json) = metadata() else {
eprintln!("skipping: `cargo metadata` unavailable");
return;
};
let runtime = runtime_packages();
let floors: Vec<_> = declared_floors(&json)
.into_iter()
.filter(|(name, _)| name != "disarm" && runtime.iter().any(|r| r == name))
.collect();
assert!(
floors.len() > 5,
"parsed {} runtime packages with a rust-version, expected the whole graph — the \
scan is broken, not the manifest",
floors.len()
);
let declared = env!("CARGO_PKG_RUST_VERSION");
let (name, highest) = floors
.iter()
.max_by_key(|(_, v)| parts(v))
.expect("at least one package declares a rust-version");
assert!(
parts(declared) >= parts(highest),
"Cargo.toml publishes `rust-version = \"{declared}\"`, but `{name}` in the resolved \
graph requires {highest}. A consumer on {declared} cannot build this crate.\n\n\
Raise `rust-version` to {highest} and say why in the comment above it, or pin the \
dependency back with `cargo update -p {name} --precise <older>`."
);
}
#[test]
fn the_gate_is_actually_running() {
assert!(
metadata().is_some(),
"`cargo metadata --locked` failed, so the MSRV gate skipped rather than checked. \
Fix the invocation — do not leave this passing."
);
}
#[test]
fn the_scan_finds_the_crate_that_sets_the_floor() {
let Some(json) = metadata() else { return };
let runtime = runtime_packages();
let floors: Vec<_> = declared_floors(&json)
.into_iter()
.filter(|(name, _)| name != "disarm" && runtime.iter().any(|r| r == name))
.collect();
let (name, version) = floors
.iter()
.max_by_key(|(_, v)| parts(v))
.expect("no rust-version in the graph");
assert!(!name.is_empty() && name != "?", "floor has no package name");
assert!(parts(version) >= (1, 0, 0), "implausible floor {version}");
}