- CodeGraph::merge_project_edges() replaces presentation-layer function - Language::is_test_file() centralises test file detection (was in walkdir) - AnalysisConfig::is_standard_excluded() centralises default dir exclusions (was in walkdir) - normalize_cargo_package() / normalize_python_package() in domain replace duplicated normalisers in each adapter - walkdir, cargo-workspace, python-project updated to call domain methods
99 lines
2.9 KiB
Rust
99 lines
2.9 KiB
Rust
use std::path::Path;
|
|
|
|
use ignore::WalkBuilder;
|
|
|
|
use archlens_domain::{
|
|
AnalysisConfig, DomainError, FilePath, Language, SourceFile, ports::FileDiscovery,
|
|
};
|
|
|
|
pub struct WalkdirDiscovery;
|
|
|
|
impl Default for WalkdirDiscovery {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl WalkdirDiscovery {
|
|
pub fn new() -> Self {
|
|
Self
|
|
}
|
|
|
|
fn detect_language(path: &Path) -> Option<Language> {
|
|
match path.extension()?.to_str()? {
|
|
"rs" => Some(Language::Rust),
|
|
"py" => Some(Language::Python),
|
|
"cs" => Some(Language::CSharp),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn is_excluded(path: &Path, root: &Path, excludes: &[String]) -> bool {
|
|
let relative = path.strip_prefix(root).unwrap_or(path);
|
|
let relative_str = relative.to_string_lossy();
|
|
|
|
for component in relative.components() {
|
|
let name = component.as_os_str().to_string_lossy();
|
|
if AnalysisConfig::is_standard_excluded(&name) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
excludes
|
|
.iter()
|
|
.any(|exclude| relative_str.contains(exclude.trim_end_matches('/')))
|
|
}
|
|
}
|
|
|
|
impl FileDiscovery for WalkdirDiscovery {
|
|
fn discover(
|
|
&self,
|
|
root: &Path,
|
|
config: &AnalysisConfig,
|
|
) -> Result<Vec<SourceFile>, DomainError> {
|
|
let mut files = Vec::new();
|
|
|
|
let walker = WalkBuilder::new(root).hidden(true).git_ignore(true).build();
|
|
|
|
for entry in walker.filter_map(|e| e.ok()) {
|
|
let path = entry.path();
|
|
|
|
if !path.is_file() {
|
|
continue;
|
|
}
|
|
|
|
if Self::is_excluded(path, root, config.excludes()) {
|
|
continue;
|
|
}
|
|
|
|
if let Some(scope) = config.scope() {
|
|
let relative = path.strip_prefix(root).unwrap_or(path);
|
|
if !relative.starts_with(scope) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if let Some(language) = Self::detect_language(path) {
|
|
if !config.include_tests() && language.is_test_file(path) {
|
|
continue;
|
|
}
|
|
if let Some(changed) = config.changed_files() {
|
|
let relative = path.strip_prefix(root).unwrap_or(path).to_string_lossy();
|
|
if !changed
|
|
.iter()
|
|
.any(|c| relative.ends_with(c.as_str()) || c.ends_with(relative.as_ref()))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
let absolute = path.canonicalize().unwrap_or_else(|_| path.to_path_buf());
|
|
let file_path = FilePath::new(&absolute.to_string_lossy())
|
|
.map_err(|e| DomainError::IoError(e.to_string()))?;
|
|
files.push(SourceFile::new(file_path, language));
|
|
}
|
|
}
|
|
|
|
Ok(files)
|
|
}
|
|
}
|