use std::{
fs,
path::{Path, PathBuf},
};
#[test]
fn source_tree_does_not_introduce_erased_error_surfaces() {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let source_dir = manifest_dir.join("src");
let mut violations = Vec::new();
visit_rust_files(&source_dir, &mut |path| {
let contents = fs::read_to_string(path).expect("source file should be readable");
for (line_index, line) in contents.lines().enumerate() {
let line_number = line_index + 1;
if line.contains("Box<dyn Error")
|| line.contains("Box<dyn std::error::Error")
|| line.contains("anyhow::Error")
|| line.contains("eyre::")
{
violations.push(format!(
"{}:{line_number}: erased error surface: {line}",
path.display()
));
}
if line.contains("impl Error for") || line.contains("impl std::error::Error for") {
violations.push(format!(
"{}:{line_number}: handwritten Error impl: {line}",
path.display()
));
}
if (line.contains("dyn Error") || line.contains("dyn std::error::Error"))
&& !is_standard_source_signature(line)
{
violations.push(format!(
"{}:{line_number}: dyn Error outside Error::source: {line}",
path.display()
));
}
}
});
assert!(
violations.is_empty(),
"typed error policy violations:\n{}",
violations.join("\n")
);
}
fn visit_rust_files(path: &Path, visit: &mut impl FnMut(&Path)) {
if path.is_dir() {
for entry in fs::read_dir(path).expect("source directory should be readable") {
let entry = entry.expect("source directory entry should be readable");
visit_rust_files(&entry.path(), visit);
}
} else if path.extension().is_some_and(|extension| extension == "rs") {
visit(path);
}
}
fn is_standard_source_signature(line: &str) -> bool {
line.trim() == "fn source(&self) -> Option<&(dyn Error + 'static)> {"
|| line.trim() == "fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {"
}