61 lines
1.6 KiB
Rust
61 lines
1.6 KiB
Rust
|
|
use eyre::Result;
|
||
|
|
use std::fs::Metadata;
|
||
|
|
use std::path::PathBuf;
|
||
|
|
use tokio::fs::read_dir;
|
||
|
|
use tokio::sync::mpsc;
|
||
|
|
|
||
|
|
pub fn spawn_walk_dir(
|
||
|
|
dir: impl Into<PathBuf> + Send + 'static,
|
||
|
|
) -> mpsc::Receiver<Result<(PathBuf, Metadata)>> {
|
||
|
|
let (tx, rx) = mpsc::channel(1);
|
||
|
|
tokio::spawn(walk_dir(dir, tx));
|
||
|
|
rx
|
||
|
|
}
|
||
|
|
|
||
|
|
pub async fn walk_dir(dir: impl Into<PathBuf>, tx: mpsc::Sender<Result<(PathBuf, Metadata)>>) {
|
||
|
|
let dir: PathBuf = dir.into();
|
||
|
|
|
||
|
|
let mut todo = std::collections::LinkedList::new();
|
||
|
|
if let Ok(rd) = read_dir(&dir).await {
|
||
|
|
todo.push_front(rd);
|
||
|
|
}
|
||
|
|
|
||
|
|
while let Some(rd) = todo.front_mut() {
|
||
|
|
let entry = match rd.next_entry().await {
|
||
|
|
Ok(v) => v,
|
||
|
|
Err(e) => {
|
||
|
|
if tx.send(Err(e.into())).await.is_err() {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
todo.pop_front(); // skip dir on error
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
let Some(entry) = entry else {
|
||
|
|
todo.pop_front();
|
||
|
|
continue;
|
||
|
|
};
|
||
|
|
|
||
|
|
let Ok(md) = entry.metadata().await else {
|
||
|
|
continue;
|
||
|
|
};
|
||
|
|
let is_dir = md.is_dir();
|
||
|
|
|
||
|
|
let Ok(path) = entry.path().strip_prefix(&dir).map(|p| p.to_path_buf()) else {
|
||
|
|
continue; // sub-entry not in dir, weird but semantically, we ignore
|
||
|
|
};
|
||
|
|
|
||
|
|
if tx.send(Ok((path, md))).await.is_err() {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// recurse in sub directories
|
||
|
|
if is_dir {
|
||
|
|
if let Ok(rd) = read_dir(entry.path()).await {
|
||
|
|
todo.push_front(rd);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|