use std::future::Future;
use std::path::Path;
use std::pin::Pin;
pub enum DirAction {
Recurse,
LoadFile(std::path::PathBuf),
}
pub async fn load_file<T, F>(path: &Path, parse: F, type_name: &str) -> crate::Result<T>
where
F: FnOnce(&str, &Path) -> crate::Result<T>,
{
let content = tokio::fs::read_to_string(path).await.map_err(|e| {
crate::Error::Config(format!(
"Failed to read {} file {:?}: {}",
type_name, path, e
))
})?;
parse(&content, path)
}
pub async fn scan_directory<T, F, Filter, DirHandler>(
dir: &Path,
load_fn: F,
file_filter: Filter,
dir_handler: DirHandler,
) -> crate::Result<Vec<T>>
where
T: Send,
F: for<'a> Fn(&'a Path) -> Pin<Box<dyn Future<Output = crate::Result<T>> + Send + 'a>>
+ Send
+ Sync,
Filter: Fn(&Path) -> bool + Send + Sync,
DirHandler: Fn(&Path) -> DirAction + Send + Sync,
{
let mut indices = Vec::new();
if !dir.exists() {
return Ok(indices);
}
scan_recursive(dir, &mut indices, &load_fn, &file_filter, &dir_handler).await?;
Ok(indices)
}
fn scan_recursive<'a, T, F, Filter, DirHandler>(
dir: &'a Path,
indices: &'a mut Vec<T>,
load_fn: &'a F,
file_filter: &'a Filter,
dir_handler: &'a DirHandler,
) -> Pin<Box<dyn Future<Output = crate::Result<()>> + Send + 'a>>
where
T: Send,
F: Fn(&Path) -> Pin<Box<dyn Future<Output = crate::Result<T>> + Send + '_>> + Send + Sync,
Filter: Fn(&Path) -> bool + Send + Sync,
DirHandler: Fn(&Path) -> DirAction + Send + Sync,
{
Box::pin(async move {
let mut entries = tokio::fs::read_dir(dir).await.map_err(|e| {
crate::Error::Config(format!("Failed to read directory {:?}: {}", dir, e))
})?;
while let Some(entry) = entries
.next_entry()
.await
.map_err(|e| crate::Error::Config(format!("Failed to read directory entry: {}", e)))?
{
let path = entry.path();
if path.is_dir() {
match dir_handler(&path) {
DirAction::Recurse => {
scan_recursive(&path, indices, load_fn, file_filter, dir_handler).await?;
}
DirAction::LoadFile(file_path) => {
if let Ok(index) = load_fn(&file_path).await {
indices.push(index);
}
}
}
} else if file_filter(&path) {
match load_fn(&path).await {
Ok(index) => indices.push(index),
Err(e) => {
tracing::warn!("Failed to load index from {:?}: {}", path, e);
}
}
}
}
Ok(())
})
}