use std::fs;
use std::path::{Path, PathBuf};
const FACADE_DIRECTORIES: [&str; 2] = ["src/facade/neural", "src/facade/notebook"];
const INTERNAL_MODULES: [&str; 11] = [
"backend", "core", "derived", "emission", "engine", "facade", "graph", "neural", "notebook",
"op", "payload",
];
const LEAK_SUPPRESSIONS: [&str; 2] = ["private_bounds", "private_interfaces"];
fn rust_sources(directory: &Path, sources: &mut Vec<PathBuf>) {
let entries = fs::read_dir(directory).expect("facade directory is readable");
for entry in entries {
let path = entry.expect("directory entry is readable").path();
if path.is_dir() {
rust_sources(&path, sources);
continue;
}
if path.extension().is_some_and(|extension| extension == "rs") {
sources.push(path);
}
}
}
fn leading_identifier(text: &str) -> &str {
let end = text
.find(|character: char| !character.is_alphanumeric() && character != '_')
.unwrap_or(text.len());
&text[..end]
}
fn violations_in(path: &Path, text: &str) -> Vec<String> {
let mut violations = Vec::new();
for (index, line) in text.lines().enumerate() {
let mut rest = line;
while let Some(position) = rest.find("crate::") {
let after = &rest[position + "crate::".len()..];
let segment = leading_identifier(after);
if INTERNAL_MODULES.contains(&segment) {
violations.push(format!(
"{}:{}: names the private module `crate::{segment}`",
path.display(),
index + 1
));
}
rest = after;
}
for suppression in LEAK_SUPPRESSIONS {
if line.contains(suppression) {
violations.push(format!(
"{}:{}: suppresses `{suppression}`",
path.display(),
index + 1
));
}
}
}
violations
}
#[test]
fn the_gate_recognizes_both_leak_shapes() {
let text = "use crate::graph::network::Network;\n#[allow(private_bounds)]\nuse crate::Tape;";
let found = violations_in(Path::new("synthetic.rs"), text);
assert_eq!(found.len(), 2, "one module path plus one suppression");
}
#[test]
fn facades_compose_through_the_public_surface_alone() {
let root = Path::new(env!("CARGO_MANIFEST_DIR"));
let mut sources = Vec::new();
for directory in FACADE_DIRECTORIES {
rust_sources(&root.join(directory), &mut sources);
}
assert!(
!sources.is_empty(),
"the facade directories moved; update this gate"
);
let mut violations = Vec::new();
for path in &sources {
let text = fs::read_to_string(path).expect("facade source is readable");
violations.extend(violations_in(path, &text));
}
assert!(
violations.is_empty(),
"facades must compose through the public surface alone; \
publish the read the facade needs instead of reaching through:\n{}",
violations.join("\n")
);
}