use std::path::{Path, PathBuf};
use ignore::WalkBuilder;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FileDiscoveryError {
NonRFilePath { path: PathBuf },
WalkError { path: PathBuf, message: String },
}
pub fn collect_r_files(paths: &[PathBuf]) -> Result<Vec<PathBuf>, FileDiscoveryError> {
let mut files = Vec::new();
for path in paths {
if path.is_file() {
if !is_r_file(path) {
return Err(FileDiscoveryError::NonRFilePath { path: path.clone() });
}
files.push(path.clone());
continue;
}
if path.is_dir() {
let mut builder = WalkBuilder::new(path);
builder.standard_filters(true);
builder.hidden(false);
for entry in builder.build() {
match entry {
Ok(entry) => {
if !entry.file_type().is_some_and(|ft| ft.is_file()) {
continue;
}
let entry_path = entry.path().to_path_buf();
if is_r_file(&entry_path) {
files.push(entry_path);
}
}
Err(err) => {
return Err(FileDiscoveryError::WalkError {
path: path.clone(),
message: err.to_string(),
});
}
}
}
continue;
}
return Err(FileDiscoveryError::WalkError {
path: path.clone(),
message: "path does not exist".to_string(),
});
}
files.sort();
files.dedup();
Ok(files)
}
fn is_r_file(path: &Path) -> bool {
path.extension()
.and_then(|ext| ext.to_str())
.is_some_and(|ext| ext.eq_ignore_ascii_case("r"))
}