//! **Every crate here names this repository's real home** (A28 L17).
//!
//! The old forge is gone. A `repository =` that still points at it, or at
//! another of our own old addresses, sends a reader of crates.io or a
//! `cargo metadata` consumer to a dead or wrong place. This test walks every
//! `Cargo.toml` in the repository (detached workspaces and fixtures too) and
//! every `THIRD_PARTY_LICENSES*.md`, and fails on the first such line.
use std::path::{Path, PathBuf};
/// Where this repository lives.
const HOME: &str = "https://git.gunnar.rs/mikronova/funnel-core";
/// The old forge's host, spelled so a search for it finds only real uses.
const OLD_FORGE: &str = concat!("code", "berg", ".org");
/// Our own hosts and organisations: a `repository =` on one of these must be [`HOME`].
const OURS: &[&str] = &[
"https://gunnar.rs/",
"https://git.gunnar.rs/",
"https://github.com/vetra-automation/",
"https://github.com/nordisk/",
"https://github.com/mikronova/",
];
/// The repository root: the first ancestor holding `.git`, else the top-most
/// ancestor that still holds a `Cargo.toml`.
fn repo_root() -> PathBuf {
let start = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let mut top = start.clone();
for dir in start.ancestors() {
if dir.join(".git").exists() {
return dir.to_path_buf();
}
if dir.join("Cargo.toml").is_file() {
top = dir.to_path_buf();
}
}
top
}
fn walk(dir: &Path, out: &mut Vec<PathBuf>) {
let Ok(entries) = std::fs::read_dir(dir) else { return };
for entry in entries.flatten() {
let path = entry.path();
let name = entry.file_name();
let name = name.to_string_lossy();
let Ok(meta) = std::fs::symlink_metadata(&path) else { continue };
if meta.is_dir() {
if name == "target" || name == "node_modules" || name.starts_with('.') {
continue;
}
walk(&path, out);
} else if meta.is_file() && (name == "Cargo.toml" || (name.starts_with("THIRD_PARTY_LICENSES") && name.ends_with(".md"))) {
out.push(path);
}
}
}
/// The quoted value of a `repository = "…"` line, if this is one.
fn repository_value(line: &str) -> Option<&str> {
let rest = line.trim_start().strip_prefix("repository")?;
let rest = rest.trim_start().strip_prefix('=')?.trim_start().strip_prefix('"')?;
rest.split('"').next()
}
fn offences(path: &Path, text: &str) -> Vec<String> {
let mut found = Vec::new();
let manifest = path.file_name().is_some_and(|n| n == "Cargo.toml");
for (i, line) in text.lines().enumerate() {
let at = format!("{}:{}", path.display(), i + 1);
if line.contains(OLD_FORGE) {
found.push(format!("{at}: names the old forge"));
continue;
}
if manifest {
if let Some(v) = repository_value(line) {
if OURS.iter().any(|o| v.starts_with(o)) && v != HOME {
found.push(format!("{at}: repository is {v}, not {HOME}"));
}
}
}
}
found
}
#[test]
fn every_manifest_names_this_repositorys_real_home() {
let root = repo_root();
let mut files = Vec::new();
walk(&root, &mut files);
files.sort();
assert!(files.iter().any(|p| p.ends_with("Cargo.toml")), "no Cargo.toml found under {}", root.display());
let mut all = Vec::new();
for f in &files {
let text = std::fs::read_to_string(f).unwrap_or_default();
all.extend(offences(f, &text));
}
assert!(all.is_empty(), "{} line(s) name an old home:\n{}", all.len(), all.join("\n"));
}
#[test]
fn the_guard_reads_a_repository_line_and_spots_the_old_forge() {
assert_eq!(repository_value(r#"repository = "https://x/y""#), Some("https://x/y"));
assert_eq!(repository_value(r#"repository = "a""#), Some("a"));
assert_eq!(repository_value("repository.workspace = true"), None);
let p = Path::new("Cargo.toml");
let old = format!("repository = \"https://{OLD_FORGE}/nordisk/x\"");
assert_eq!(offences(p, &old).len(), 1);
assert_eq!(offences(p, "repository = \"https://github.com/vetra-automation/elsewhere\"").len(), 1);
assert!(offences(p, &format!("repository = \"{HOME}\"")).is_empty());
assert!(offences(p, "repository = \"https://github.com/apache/iceberg-rust\"").is_empty());
}