use std::path::{Path, PathBuf};
const ALLOWED_DEPS: &[&str] = &["bevy", "bloodstain"];
const FORBIDDEN_DEP_MARKERS: &[&str] =
&["bevy_carnage", "bevy_hanabi", "wgpu", "avian", "emerge", "foundation_vs_slop"];
fn crate_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}
fn rust_sources(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();
if path.is_dir() {
rust_sources(&path, out);
} else if path.extension().is_some_and(|e| e == "rs") {
out.push(path);
}
}
}
fn strip_comments(src: &str) -> String {
let mut out = String::with_capacity(src.len());
let mut in_block = false;
for line in src.lines() {
let mut rest = line;
loop {
if in_block {
match rest.find("*/") {
Some(end) => {
in_block = false;
rest = &rest[end + 2..];
}
None => break,
}
} else {
let line_at = rest.find("//");
let block_at = rest.find("/*");
match (line_at, block_at) {
(Some(l), b) if b.is_none_or(|b| l < b) => {
out.push_str(&rest[..l]);
break;
}
(_, Some(b)) => {
out.push_str(&rest[..b]);
in_block = true;
rest = &rest[b + 2..];
}
_ => {
out.push_str(rest);
break;
}
}
}
}
out.push('\n');
}
out
}
#[test]
fn no_source_file_reaches_across_the_layering() {
let src = crate_root().join("src");
let mut files = Vec::new();
rust_sources(&src, &mut files);
assert!(
files.len() >= 4,
"expected to scan the whole crate, found only {} file(s) — has the layout moved?",
files.len()
);
let mut offenders = Vec::new();
for path in &files {
let Ok(text) = std::fs::read_to_string(path) else {
continue;
};
let code = strip_comments(&text);
for (n, line) in code.lines().enumerate() {
for marker in FORBIDDEN_DEP_MARKERS {
if line.contains(marker) {
offenders.push(format!(
"{}:{} — {}",
path.strip_prefix(crate_root()).unwrap_or(path).display(),
n + 1,
line.trim()
));
}
}
}
}
assert!(
offenders.is_empty(),
"bevy_wetmap must stay below the gore layer and off the GPU, but {} line(s) reference a \
forbidden crate.\n {}\n\n\
`bevy_carnage` composes this crate, so a dependency the other way inverts the layering — \
that is why `src/uv.rs` reimplements Moller-Trumbore rather than importing it. A compute or \
physics dependency would put the authority back on the GPU, which is the one thing this \
crate exists not to do.",
offenders.len(),
offenders.join("\n ")
);
}
#[test]
fn the_dependency_list_stays_closed() {
let manifest = std::fs::read_to_string(crate_root().join("Cargo.toml"))
.expect("bevy_wetmap must have a Cargo.toml");
let deps = manifest
.split("[dependencies]")
.nth(1)
.expect("bevy_wetmap must declare a [dependencies] table");
let deps = deps.split("\n[").next().unwrap_or(deps);
for line in deps.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
let name = line.split(['=', ' ', '.']).next().unwrap_or("");
if name.is_empty() || !name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
{
continue;
}
assert!(
ALLOWED_DEPS.contains(&name),
"bevy_wetmap declares `{name}`, which is not in its allowed set {ALLOWED_DEPS:?}.\n\
Widening this is a design decision, not a convenience. If it is genuinely warranted, add \
it to ALLOWED_DEPS in this test in the same commit, so the change is visible in review."
);
for marker in FORBIDDEN_DEP_MARKERS {
assert!(
!name.contains(marker),
"bevy_wetmap declares `{name}` — see this file's header for why that name in \
particular is refused."
);
}
}
}