use std::collections::BTreeMap;
use std::path::Path;
type TestResult = Result<(), Box<dyn std::error::Error>>;
const TOKENS: [&str; 2] = ["RefusalCause::MissingGrant", "RefusalCause::HotMode"];
fn is_test_source(path: &Path) -> bool {
let name = path
.file_name()
.and_then(|name| name.to_str())
.unwrap_or("");
name.ends_with("_tests.rs")
|| name.ends_with("_test.rs")
|| name == "fixture.rs"
|| name == "tests.rs"
|| path.components().any(|component| {
let component = component.as_os_str().to_str().unwrap_or("");
component == "tests" || component.ends_with("_tests")
})
}
fn constructions(line: &str) -> usize {
let trimmed = line.trim_start();
if trimmed.starts_with("//") || line.contains("matches!(") || line.contains("| RefusalCause") {
return 0;
}
let arrow = line.find("=>");
TOKENS
.iter()
.flat_map(|token| line.match_indices(token))
.filter(|(index, token)| {
let before = line[..*index].trim_end();
let after = &line[index + token.len()..];
!(before.ends_with("==")
|| before.ends_with("!=")
|| after.trim_start().starts_with('|')
|| arrow.is_some_and(|arrow| *index < arrow))
})
.count()
}
fn census(dir: &Path, root: &Path, sites: &mut BTreeMap<String, usize>) -> TestResult {
for entry in std::fs::read_dir(dir)? {
let path = entry?.path();
if path.is_dir() {
census(&path, root, sites)?;
continue;
}
if path.extension().is_none_or(|extension| extension != "rs") || is_test_source(&path) {
continue;
}
let text = std::fs::read_to_string(&path)?;
let count = text.lines().map(constructions).sum::<usize>();
if count > 0 {
let relative = path.strip_prefix(root)?.display().to_string();
sites.insert(relative, count);
}
}
Ok(())
}
#[test]
fn every_missing_grant_and_hot_mode_construction_is_a_named_decision_point() -> TestResult {
let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
let mut sites = BTreeMap::new();
census(&root, &root, &mut sites)?;
let expected = BTreeMap::from([
("auth_lys/lys_pass_adapter.rs".to_owned(), 2),
("auth_lys/lys_pass_adapter/deliberate.rs".to_owned(), 1),
]);
assert_eq!(
sites, expected,
"a grant decision outside the three ACCESS-011 R2 places; route it through auth_lys::advisory"
);
Ok(())
}