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 + Send + 'static, ) -> mpsc::Receiver> { let (tx, rx) = mpsc::channel(1); tokio::spawn(walk_dir(dir, tx)); rx } pub async fn walk_dir(dir: impl Into, tx: mpsc::Sender>) { 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); } } } }