use std::fs;
use std::path::{Path, PathBuf};
use std::sync::LazyLock;
fn workspace_root() -> &'static Path {
static ROOT: LazyLock<PathBuf> = LazyLock::new(|| {
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.and_then(Path::parent)
.expect("crate lives at <workspace>/crates/<name>")
.to_path_buf()
});
&ROOT
}
fn sources() -> &'static [SourceFile] {
static SOURCES: LazyLock<Vec<SourceFile>> = LazyLock::new(|| {
let mut paths = Vec::new();
let crates = fs::read_dir(workspace_root().join("crates")).expect("crates/ dir");
for entry in crates.flatten() {
collect_rs(&entry.path().join("src"), &mut paths);
}
assert!(!paths.is_empty(), "found no source files to scan");
paths.sort();
paths.iter().map(|p| SourceFile::read(p)).collect()
});
&SOURCES
}
struct SourceFile {
path: String,
lines: Vec<(usize, String)>,
}
impl SourceFile {
fn read(path: &Path) -> Self {
let text = fs::read_to_string(path)
.unwrap_or_else(|e| panic!("cannot read {}: {e}", path.display()));
let rel = path
.strip_prefix(workspace_root())
.unwrap_or(path)
.to_string_lossy()
.replace('\\', "/");
SourceFile {
path: rel,
lines: text
.lines()
.enumerate()
.map(|(i, l)| (i + 1, l.to_string()))
.filter(|(_, l)| !l.trim_start().starts_with("//"))
.collect(),
}
}
}
fn collect_rs(dir: &Path, out: &mut Vec<PathBuf>) {
let Ok(entries) = fs::read_dir(dir) else {
return;
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
collect_rs(&path, out);
} else if path.extension().is_some_and(|e| e == "rs") {
out.push(path);
}
}
}
fn scan(skip: impl Fn(&str) -> bool, hit: impl Fn(&str) -> bool) -> Vec<String> {
sources()
.iter()
.filter(|f| !skip(&f.path))
.flat_map(|f| {
f.lines
.iter()
.filter(|(_, l)| hit(l))
.map(move |(n, l)| format!("{}:{n} {}", f.path, l.trim()))
})
.collect()
}
#[test]
fn iou_formula_only_in_primitives_sim() {
const FORMULA_HOMES: &[&str] = &["crates/hotcoco/src/primitives/sim.rs"];
let violations = scan(
|path| FORMULA_HOMES.contains(&path),
|line| line.contains("union") && line.contains('+') && line.contains(" - "),
);
assert!(
violations.is_empty(),
"a similarity denominator may only be defined in: {}.\n\
Found what looks like another copy at:\n {}\n\n\
Call `primitives::sim` instead — it exposes matrix kernels (`bbox_iou`, \
`mask_iou`, `obb_iou`) and the scalar `bbox_iou_pair`. If this really is \
a different formula (see the panoptic note on this test), add its file to \
FORMULA_HOMES with a comment.",
FORMULA_HOMES.join(", "),
violations.join("\n ")
);
}
fn iou_shape(line: &str) -> bool {
fn is_ident(c: char) -> bool {
c.is_ascii_alphanumeric() || c == '_' || c == '.'
}
let flat: Vec<char> = line.chars().filter(|c| !c.is_whitespace()).collect();
let n = flat.len();
for i in 0..n {
if flat[i] != '/' || i + 1 >= n || flat[i + 1] != '(' {
continue;
}
let num_start = (0..i).rev().take_while(|&j| is_ident(flat[j])).last();
let Some(num_start) = num_start else { continue };
let numerator: String = flat[num_start..i].iter().collect();
let mut depth = 0usize;
let mut close = None;
for (j, &c) in flat.iter().enumerate().skip(i + 1) {
match c {
'(' => depth += 1,
')' => {
depth -= 1;
if depth == 0 {
close = Some(j);
break;
}
}
_ => {}
}
}
let Some(close) = close else { continue };
let body: String = flat[i + 2..close].iter().collect();
if let Some((sum, subtracted)) = body.rsplit_once('-') {
if let Some((a, b)) = sum.split_once('+') {
if subtracted == numerator
&& !a.is_empty()
&& !b.is_empty()
&& a.chars().all(is_ident)
&& b.chars().all(is_ident)
{
return true;
}
}
}
}
false
}
#[test]
fn iou_formula_shape_only_in_primitives_sim() {
const FORMULA_HOMES: &[&str] = &["crates/hotcoco/src/primitives/sim.rs"];
let violations = scan(|path| FORMULA_HOMES.contains(&path), iou_shape);
assert!(
violations.is_empty(),
"a similarity denominator may only be defined in: {}.\n\
Found the `x / (a + b - x)` shape at:\n {}\n\n\
Call `primitives::sim` instead — it exposes matrix kernels (`bbox_iou`, \
`mask_iou`, `obb_iou`) and the scalar `bbox_iou_pair`.",
FORMULA_HOMES.join(", "),
violations.join("\n ")
);
}
#[test]
fn exactly_one_min_parallel_work_constant() {
let found = scan(|_| false, |line| line.contains("const MIN_PARALLEL_WORK"));
assert_eq!(
found.len(),
1,
"expected exactly one MIN_PARALLEL_WORK declaration, found {}:\n {}\n\n\
Per-kernel copies drift. Kernels share the one in `primitives::sim`.",
found.len(),
found.join("\n ")
);
}
#[test]
fn detection_cap_only_derived_in_params() {
const CAP_OWNER: &[&str] = &["crates/hotcoco/src/params.rs"];
let violations = scan(
|path| CAP_OWNER.contains(&path),
|line| {
line.contains("max_dets.last(")
|| (line.contains("max_dets") && line.contains(".max()"))
},
);
assert!(
violations.is_empty(),
"the per-image detection cap may only be derived in: {}.\n\
Found another derivation at:\n {}\n\n\
Call `params.max_det()` instead — positional and max-based spellings \
agree on sorted input and silently diverge on unsorted input.",
CAP_OWNER.join(", "),
violations.join("\n ")
);
}
#[test]
fn greedy_matching_only_in_primitives() {
let violations = scan(
|path| path.contains("/primitives/"),
|line| line.contains("best_iou") || line.contains("best_gi"),
);
assert!(
violations.is_empty(),
"greedy matching belongs to `primitives::greedy`.\n\
Hand-rolled matcher found at:\n {}",
violations.join("\n ")
);
}
#[test]
fn similarity_cache_stays_driver_private() {
const CACHE_OWNERS: &[&str] = &[
"crates/hotcoco/src/detection/mod.rs",
"crates/hotcoco/src/detection/evaluate.rs",
"crates/hotcoco/src/detection/matching.rs",
];
let violations = scan(
|path| CACHE_OWNERS.contains(&path),
|line| line.contains(".ious") || line.contains("ious:"),
);
assert!(
violations.is_empty(),
"the whole-dataset similarity cache is driver-private; only {} may touch \
it.\nFound a direct field access at:\n {}\n\n\
Call `COCOeval::cell_ious(img_id, cat_id)` instead — it hands out one \
cell and cannot leak the map. Exposing the map as a shared \"similarity \
cache\" type would foreclose the 1.2 recompute-instead-of-retain lever.",
CACHE_OWNERS.join(", "),
violations.join("\n ")
);
}
fn import_target(line: &str) -> Option<&str> {
let mut rest = line.trim_start();
if let Some(after_pub) = rest.strip_prefix("pub") {
let after_pub = after_pub.trim_start();
rest = match after_pub.strip_prefix('(') {
Some(vis) => vis.split_once(')')?.1.trim_start(),
None => after_pub,
};
}
rest.strip_prefix("use ")
}
fn foreign_imports(dir: &str, allowed: &[&str]) -> Vec<String> {
sources()
.iter()
.filter(|f| f.path.contains(dir))
.flat_map(|f| {
let is_module_root = f.path.ends_with("/mod.rs");
f.lines.iter().filter_map(move |(n, line)| {
let nested = line.starts_with(char::is_whitespace);
let target = import_target(line)?;
let ok = if target.starts_with("super::super::") {
nested
} else if target.starts_with("super::") {
nested || !is_module_root
} else {
allowed.iter().any(|prefix| target.starts_with(prefix))
};
(!ok).then(|| format!("{}:{n} {}", f.path, line.trim()))
})
})
.collect()
}
const NEUTRAL: &[&str] = &["std::", "core::", "serde", "rand", "rayon", "self::"];
fn metrics_allowed() -> Vec<&'static str> {
[
NEUTRAL,
&[
"crate::metrics", "crate::error", "crate::report", ],
]
.concat()
}
fn primitives_allowed() -> Vec<&'static str> {
[
NEUTRAL,
&[
"crate::primitives::sim",
"crate::primitives::greedy",
"crate::primitives::assign",
"crate::geometry",
"crate::mask",
"crate::types",
],
]
.concat()
}
fn inline_crate_paths(line: &str) -> Vec<&str> {
let mut out = Vec::new();
let mut start = 0;
while let Some(pos) = line[start..].find("crate::") {
let begin = start + pos;
let ok_prefix = line[..begin]
.chars()
.next_back()
.is_none_or(|prev| !(prev.is_alphanumeric() || prev == '_'));
let end = line[begin..]
.char_indices()
.find(|&(i, c)| i >= "crate::".len() && !(c.is_alphanumeric() || c == '_' || c == ':'))
.map_or(line.len(), |(i, _)| begin + i);
if ok_prefix {
out.push(&line[begin..end]);
}
start = end.max(begin + "crate::".len());
}
out
}
fn inline_foreign_paths(dir: &str, allowed: &[&str]) -> Vec<String> {
sources()
.iter()
.filter(|f| f.path.contains(dir))
.flat_map(|f| {
f.lines
.iter()
.take_while(|(_, l)| !l.trim_start().starts_with("#[cfg(test)"))
.filter(|(_, l)| import_target(l).is_none())
.filter(|(_, line)| {
inline_crate_paths(line)
.iter()
.any(|path| !allowed.iter().any(|p| path.starts_with(p)))
})
.map(move |(n, line)| format!("{}:{n} {}", f.path, line.trim()))
})
.collect()
}
#[test]
fn metrics_never_depends_on_a_family_driver() {
let allowed = metrics_allowed();
let violations = foreign_imports("/metrics/", &allowed);
assert!(
violations.is_empty(),
"`metrics/` may only import {allowed:?}.\nFound:\n {}\n\n\
A metric function takes flat arrays — `(scores, matched)`, label pairs, a \
closure. If it needs something from a family driver, that something is the \
adapter's job to extract and pass in. Move the family-specific part into \
`detection/` and keep the math here.\n\n\
Widening this allowlist is a decision about what the layer means. Make it \
deliberately, not to make a build pass.",
violations.join("\n ")
);
}
#[test]
fn metrics_never_names_a_family_driver_inline() {
let allowed = metrics_allowed();
let violations = inline_foreign_paths("/metrics/", &allowed);
assert!(
violations.is_empty(),
"`metrics/` may only name {allowed:?} in qualified paths.\nFound:\n {}\n\n\
See `metrics_never_depends_on_a_family_driver` — the rule is the same; \
writing the path inline instead of importing it does not change what \
the layer now depends on.",
violations.join("\n ")
);
}
#[test]
fn primitives_never_depends_on_metrics() {
let allowed = primitives_allowed();
let violations = foreign_imports("/primitives/", &allowed);
assert!(
violations.is_empty(),
"`primitives/` may only import {allowed:?}.\nFound:\n {}\n\n\
Kernels match; metrics score. If a primitive needs a metric, the layering \
is inverted — the caller should compose the two instead.",
violations.join("\n ")
);
}
#[test]
fn primitives_never_names_metrics_inline() {
let allowed = primitives_allowed();
let violations = inline_foreign_paths("/primitives/", &allowed);
assert!(
violations.is_empty(),
"`primitives/` may only name {allowed:?} in qualified paths.\nFound:\n {}\n\n\
Kernels match; metrics score. Writing `crate::metrics::…` (or a crate-root \
re-export) inline instead of importing it does not change the dependency.",
violations.join("\n ")
);
}