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 { 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, 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) } }