use crate::fs::{asyncify, sys};
use std::ffi::OsString;
use std::fs::{FileType, Metadata};
use std::future::Future;
use std::io;
#[cfg(unix)]
use std::os::unix::fs::DirEntryExt;
use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::sync::Arc;
use std::task::Context;
use std::task::Poll;
pub async fn read_dir(path: impl AsRef<Path>) -> io::Result<ReadDir> {
let path = path.as_ref().to_owned();
let std = asyncify(|| std::fs::read_dir(path)).await?;
Ok(ReadDir(State::Idle(Some(std))))
}
#[derive(Debug)]
#[must_use = "streams do nothing unless polled"]
pub struct ReadDir(State);
#[derive(Debug)]
enum State {
Idle(Option<std::fs::ReadDir>),
Pending(sys::Blocking<(Option<io::Result<std::fs::DirEntry>>, std::fs::ReadDir)>),
}
impl ReadDir {
pub async fn next_entry(&mut self) -> io::Result<Option<DirEntry>> {
use crate::future::poll_fn;
poll_fn(|cx| self.poll_next_entry(cx)).await
}
#[doc(hidden)]
pub fn poll_next_entry(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<Option<DirEntry>>> {
loop {
match self.0 {
State::Idle(ref mut std) => {
let mut std = std.take().unwrap();
self.0 = State::Pending(sys::run(move || {
let ret = std.next();
(ret, std)
}));
}
State::Pending(ref mut rx) => {
let (ret, std) = ready!(Pin::new(rx).poll(cx))?;
self.0 = State::Idle(Some(std));
let ret = match ret {
Some(Ok(std)) => Ok(Some(DirEntry(Arc::new(std)))),
Some(Err(e)) => Err(e),
None => Ok(None),
};
return Poll::Ready(ret);
}
}
}
}
}
#[cfg(feature = "stream")]
impl crate::stream::Stream for ReadDir {
type Item = io::Result<DirEntry>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
Poll::Ready(match ready!(self.poll_next_entry(cx)) {
Ok(Some(entry)) => Some(Ok(entry)),
Ok(None) => None,
Err(err) => Some(Err(err)),
})
}
}
#[derive(Debug)]
pub struct DirEntry(Arc<std::fs::DirEntry>);
impl DirEntry {
pub fn path(&self) -> PathBuf {
self.0.path()
}
pub fn file_name(&self) -> OsString {
self.0.file_name()
}
pub async fn metadata(&self) -> io::Result<Metadata> {
let std = self.0.clone();
asyncify(move || std.metadata()).await
}
pub async fn file_type(&self) -> io::Result<FileType> {
let std = self.0.clone();
asyncify(move || std.file_type()).await
}
}
#[cfg(unix)]
impl DirEntryExt for DirEntry {
fn ino(&self) -> u64 {
self.0.ino()
}
}