cha_core/path_shape.rs
1//! Path-shape utility helpers shared across detectors.
2//!
3//! These functions used to live as private duplicated impls inside
4//! `boundary_leak` and `module_envy`. Centralized here so `ProjectQuery`
5//! implementations and standalone helpers stay aligned.
6
7use std::path::Path;
8
9/// True if `path` is under a test directory or named like a test file.
10///
11/// Recognized patterns:
12/// - segment match: `tests/`, `test/`, `__tests__/`, `__mocks__/`,
13/// `spec/`, `specs/`
14/// - filename: starts with `test_` / ends with `_test`, `.test`,
15/// `.spec`, `_spec`
16pub fn is_test_path(path: &Path) -> bool {
17 const TEST_DIRS: &[&str] = &["tests", "test", "__tests__", "__mocks__", "spec", "specs"];
18 let has_test_segment = path.components().any(|c| {
19 c.as_os_str()
20 .to_str()
21 .is_some_and(|s| TEST_DIRS.contains(&s))
22 });
23 if has_test_segment {
24 return true;
25 }
26 if let Some(stem) = path.file_stem().and_then(|s| s.to_str())
27 && (stem.starts_with("test_")
28 || stem.ends_with("_test")
29 || stem.ends_with(".test")
30 || stem.ends_with(".spec")
31 || stem.ends_with("_spec"))
32 {
33 return true;
34 }
35 false
36}