pub(crate) const GENERATED_DIRS: [&str; 32] = [
"vendor/",
".bundle/",
"node_modules/",
"bower_components/",
"Pods/",
"Carthage/",
"target/",
"dist/",
"build/",
"out/",
".next/",
".nuxt/",
"_build/",
"deps/",
"coverage/",
"venv/",
".venv/",
"site-packages/",
"__pycache__/",
"third_party/",
"third-party/",
"3rdparty/",
"external/",
"DerivedData/",
".build/",
".gradle/",
".terraform/",
"elm-stuff/",
".stack-work/",
"dist-newstyle/",
".zig-cache/",
"zig-out/",
];
pub(crate) const GENERATED_FILE_SUFFIXES: [&str; 6] = [
"_pb.rb",
"_pb2.py",
".pb.go",
".freezed.dart",
".g.dart",
".mocks.dart",
];
pub(crate) const GENERATED_DIR_PAIRS: [(&str, &str); 1] = [("db", "migrate")];
pub(crate) fn is_generated_name(name: &std::ffi::OsStr) -> bool {
let n = name.to_string_lossy().to_ascii_lowercase();
let Some((stem, _)) = n.rsplit_once('.') else {
return false;
};
stem.ends_with(".min")
|| stem.ends_with(".bundle")
|| GENERATED_FILE_SUFFIXES.iter().any(|s| n.ends_with(s))
}
fn lowercased(os: &std::ffi::OsStr) -> String {
os.to_string_lossy().to_ascii_lowercase()
}
fn under_config(rel: &std::path::Path) -> bool {
rel.parent().is_some_and(|dir| {
dir.components()
.any(|c| c.as_os_str() == std::ffi::OsStr::new("config"))
})
}
fn parent_is_routes_dir(rel: &std::path::Path) -> bool {
rel.parent()
.and_then(std::path::Path::file_name)
.is_some_and(|dir| dir == std::ffi::OsStr::new("routes"))
}
fn route_rule(name: &str, rel: &std::path::Path) -> bool {
name == "routes.rb" || (parent_is_routes_dir(rel) && name.ends_with(".rb"))
}
pub(crate) fn is_route_table(rel: &std::path::Path) -> bool {
under_config(rel) && route_rule(&lowercased(rel.file_name().unwrap_or_default()), rel)
}
fn lower_of(prev: Option<&std::ffi::OsStr>) -> Option<String> {
prev.map(lowercased)
}
fn matches_dir_pair(parent_lower: Option<String>, child: &std::ffi::OsStr) -> bool {
let Some(parent) = parent_lower else {
return false;
};
GENERATED_DIR_PAIRS
.iter()
.any(|(a, b)| parent == *a && lowercased(child) == *b)
}
fn generated_component_name(name: &std::ffi::OsStr) -> bool {
let lower = lowercased(name);
GENERATED_DIRS
.iter()
.any(|d| lower == d.trim_end_matches('/'))
}
fn third_party_tree(path: &std::path::Path) -> bool {
let mut prev: Option<&std::ffi::OsStr> = None;
for comp in path.components() {
let name = comp.as_os_str();
if generated_component_name(name) || matches_dir_pair(lower_of(prev), name) {
return true;
}
prev = Some(name);
}
false
}
pub(crate) fn is_third_party(path: &std::path::Path) -> bool {
third_party_tree(path) || path.file_name().is_some_and(is_generated_name)
}
pub(crate) fn is_fixture_tree(path: &std::path::Path) -> bool {
path.components()
.any(|c| lowercased(c.as_os_str()) == "fixtures")
}