use std::ffi::OsString;
use std::io::{ErrorKind, Result};
use std::path::{Path, PathBuf};
use std::task::Context;
use std::task::Poll;
use super::{
fs::{resolve, Fd, MemoryFd, PathTarget},
metadata, FileType, Metadata,
};
pub async fn read_dir(path: impl AsRef<Path>) -> Result<ReadDir> {
if let Some(target) = resolve(path.as_ref()).await {
let children = match target {
PathTarget::Root(dir) => {
let dir = dir.read().await;
dir.files().clone()
}
PathTarget::Descriptor(fd) => {
let fd = fd.read().await;
match &*fd {
MemoryFd::Dir(dir) => dir.files().clone(),
_ => return Err(ErrorKind::PermissionDenied.into()),
}
}
};
let mut files = Vec::new();
for (name, fd) in children {
let path = {
let fd = fd.read().await;
fd.path().await
};
files.push((name, path, fd))
}
Ok(ReadDir {
iter: files.into_iter(),
})
} else {
Err(ErrorKind::NotFound.into())
}
}
#[derive(Debug)]
#[must_use = "streams do nothing unless polled"]
pub struct ReadDir {
iter: std::vec::IntoIter<(OsString, PathBuf, Fd)>,
}
impl ReadDir {
pub async fn next_entry(&mut self) -> Result<Option<DirEntry>> {
use std::future::poll_fn;
poll_fn(|cx| self.poll_next_entry(cx)).await
}
pub fn poll_next_entry(
&mut self,
_cx: &mut Context<'_>,
) -> Poll<Result<Option<DirEntry>>> {
if let Some((name, path, _fd)) = self.iter.next() {
let entry = DirEntry { path, name };
Poll::Ready(Ok(Some(entry)))
} else {
Poll::Ready(Ok(None))
}
}
}
#[derive(Debug)]
pub struct DirEntry {
path: PathBuf,
name: OsString,
}
impl DirEntry {
pub fn path(&self) -> PathBuf {
self.path.clone()
}
pub fn file_name(&self) -> OsString {
self.name.clone()
}
pub async fn metadata(&self) -> Result<Metadata> {
metadata(&self.path).await
}
pub async fn file_type(&self) -> Result<FileType> {
Ok(self.metadata().await?.file_type())
}
}