use alloc::vec::Vec;
use super::EroFS;
use super::dirent::ReadDir;
use crate::backend::AsyncImage;
use crate::dirent::DirEntry;
use crate::{Error, Result, types::Inode};
use typed_path::UnixPath;
pub struct WalkDir<'a, I: AsyncImage> {
erofs: &'a EroFS<I>,
dir_stack: Vec<(usize, ReadDir<'a, I>)>,
max_depth: usize,
}
pub struct WalkDirEntry {
pub depth: usize,
pub dir_entry: DirEntry,
pub inode: Inode,
}
impl<'a, I: AsyncImage> WalkDir<'a, I> {
pub(crate) async fn new(erofs: &'a EroFS<I>, root: impl AsRef<UnixPath>) -> Result<Self> {
let read_dir = {
let inode = erofs
.get_path_inode(root.as_ref())
.await?
.ok_or_else(|| Error::PathNotFound(root.as_ref().to_string_lossy().into_owned()))?;
if !inode.file_type().is_dir() {
return Err(Error::NotADirectory(
root.as_ref().to_string_lossy().into_owned(),
));
}
ReadDir::new(erofs, inode, root).await?
};
Ok(WalkDir {
erofs,
dir_stack: vec![(1, read_dir)],
max_depth: 0,
})
}
pub fn max_depth(mut self, depth: usize) -> Self {
self.max_depth = depth;
self
}
async fn get_walk_dir_entry(
&mut self,
dir_entry: DirEntry,
depth: usize,
) -> Result<WalkDirEntry> {
let inode = self.erofs.get_inode(dir_entry.nid()).await?;
if (depth < self.max_depth || self.max_depth == 0) && dir_entry.file_type().is_dir() {
let child_dir = ReadDir::new(self.erofs, inode, dir_entry.path()).await?;
self.dir_stack.push((depth + 1, child_dir));
}
Ok(WalkDirEntry {
depth,
dir_entry,
inode,
})
}
pub async fn next_entry(&mut self) -> Option<Result<WalkDirEntry>> {
loop {
let (depth, next_item) = {
let (depth, dir) = self.dir_stack.last_mut()?;
let next = dir.next_entry().await;
(*depth, next)
};
match next_item {
Ok(Some(entry)) => return Some(self.get_walk_dir_entry(entry, depth).await),
Ok(None) => {
self.dir_stack.pop();
}
Err(e) => return Some(Err(e)),
}
}
}
}