add dynlay
This commit is contained in:
60
src/fs.rs
Normal file
60
src/fs.rs
Normal file
@ -0,0 +1,60 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user