use walkdir::WalkDir;
use rayon::prelude::*;
use super::types::FileInfo;
use super::filter::{ExcludeMatcher};
use crate::infra::error::SyncError;
use std::path::Path;
use std::sync::Arc;
use tracing::warn;
pub fn scan_directory<P: AsRef<Path>>(
root: P,
exclude_patterns: &[String],
compute_hash: bool,
) -> Result<Vec<FileInfo>, SyncError> {
let root = root.as_ref();
if !root.exists() {
return Err(SyncError::SourceNotFound(root.to_path_buf()));
}
let matcher = ExcludeMatcher::new(exclude_patterns)
.map_err(|e| SyncError::IoError(std::io::Error::new(std::io::ErrorKind::InvalidInput, e)))?;
let entries: Vec<_> = WalkDir::new(root)
.into_iter()
.filter_map(|e| -> Option<Arc<Path>> {
let entry = match e {
Ok(entry) => entry,
Err(e) => {
warn!(error = ?e, "Failed to read directory entry");
return None;
}
};
let path = entry.path();
if !path.is_file() {
return None;
}
if matcher.is_excluded(path, root) {
return None;
}
Some(Arc::from(path))
})
.collect();
let files: Vec<_> = entries
.par_iter() .filter_map(|path_arc| {
match FileInfo::from_path(path_arc.as_ref(), compute_hash) {
Ok(info) => Some(info),
Err(e) => {
warn!(
error = ?e,
path = %path_arc.display(),
"Failed to read file metadata"
);
None
}
}
})
.collect();
Ok(files)
}