use std::path::PathBuf;
fn repo_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.canonicalize()
.expect("resolve repo root")
}
fn strip_comment(line: &str) -> &str {
match line.find('#') {
Some(i) => line[..i].trim_end(),
None => line,
}
}
fn value_of<'a>(fragment: &'a str, key: &str) -> Option<&'a str> {
let at = fragment.find(key)?;
let rest = &fragment[at + key.len()..];
let rest = rest.trim_start();
let rest = rest.strip_prefix('=')?.trim_start();
let rest = rest.strip_prefix('"')?;
let end = rest.find('"')?;
Some(&rest[..end])
}
fn section_of(trimmed: &str) -> Option<&str> {
trimmed
.strip_prefix('[')
.and_then(|s| s.strip_suffix(']'))
.map(str::trim)
}
#[test]
fn workspace_member_pins_match_the_workspace_version() {
let manifest = repo_root().join("Cargo.toml");
let contents = std::fs::read_to_string(&manifest)
.unwrap_or_else(|e| panic!("read {}: {e}", manifest.display()));
let mut section = String::new();
let mut workspace_version: Option<String> = None;
let mut member_pins: Vec<(String, String)> = Vec::new();
for raw in contents.lines() {
let line = strip_comment(raw).trim();
if line.is_empty() {
continue;
}
if let Some(header) = section_of(line) {
section = header.to_string();
continue;
}
match section.as_str() {
"workspace.package" => {
if workspace_version.is_none()
&& line.starts_with("version")
&& let Some(v) = value_of(line, "version")
{
workspace_version = Some(v.to_string());
}
}
"workspace.dependencies" => {
if line.contains("path = \"crates/")
&& let Some(version) = value_of(line, "version")
{
let name = line
.split('=')
.next()
.map(str::trim)
.unwrap_or_default()
.to_string();
member_pins.push((name, version.to_string()));
}
}
_ => {}
}
}
let workspace_version =
workspace_version.expect("[workspace.package] version not found in root Cargo.toml");
assert!(
!member_pins.is_empty(),
"no [workspace.dependencies] entry with `path = \"crates/...\"` and a `version` was found \
in {}. This guard exists to keep those pins in step with \
[workspace.package] version = \"{workspace_version}\"; if the last such pin was \
deliberately removed, delete this test in the same commit and say why.",
manifest.display()
);
for (name, pinned) in &member_pins {
assert_eq!(
pinned, &workspace_version,
"`{name}` is pinned to \"{pinned}\" under [workspace.dependencies], but \
[workspace.package] version is \"{workspace_version}\".\n\n\
`VersionBump` rewrites only [workspace.package] version, so this pin has to be \
bumped alongside it. Leaving it stale builds, tests, and lints clean — a `path` \
dependency ignores the `version` field locally — and then fails at `cargo publish` \
with a duplicate-version rejection, on release day, after the tag is cut.\n\n\
Fix: set `{name} = {{ path = \"...\", version = \"{workspace_version}\" }}` in the \
root Cargo.toml."
);
}
}