async_std/fs/
read_dir.rs

1use std::future::Future;
2use std::pin::Pin;
3
4use crate::fs::DirEntry;
5use crate::io;
6use crate::path::Path;
7use crate::stream::Stream;
8use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
9use crate::utils::Context as _;
10
11/// Returns a stream of entries in a directory.
12///
13/// The stream yields items of type [`io::Result`]`<`[`DirEntry`]`>`. Note that I/O errors can
14/// occur while reading from the stream.
15///
16/// This function is an async version of [`std::fs::read_dir`].
17///
18/// [`io::Result`]: ../io/type.Result.html
19/// [`DirEntry`]: struct.DirEntry.html
20/// [`std::fs::read_dir`]: https://doc.rust-lang.org/std/fs/fn.read_dir.html
21///
22/// # Errors
23///
24/// An error will be returned in the following situations:
25///
26/// * `path` does not point to an existing directory.
27/// * The current process lacks permissions to read the contents of the directory.
28/// * Some other I/O error occurred.
29///
30/// # Examples
31///
32/// ```no_run
33/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
34/// #
35/// use async_std::fs;
36/// use async_std::prelude::*;
37///
38/// let mut entries = fs::read_dir(".").await?;
39///
40/// while let Some(res) = entries.next().await {
41///     let entry = res?;
42///     println!("{}", entry.file_name().to_string_lossy());
43/// }
44/// #
45/// # Ok(()) }) }
46/// ```
47pub async fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
48    let path = path.as_ref().to_owned();
49    spawn_blocking(move || {
50        std::fs::read_dir(&path)
51            .context(|| format!("could not read directory `{}`", path.display()))
52    })
53    .await
54    .map(ReadDir::new)
55}
56
57/// A stream of entries in a directory.
58///
59/// This stream is returned by [`read_dir`] and yields items of type
60/// [`io::Result`]`<`[`DirEntry`]`>`. Each [`DirEntry`] can then retrieve information like entry's
61/// path or metadata.
62///
63/// This type is an async version of [`std::fs::ReadDir`].
64///
65/// [`read_dir`]: fn.read_dir.html
66/// [`io::Result`]: ../io/type.Result.html
67/// [`DirEntry`]: struct.DirEntry.html
68/// [`std::fs::ReadDir`]: https://doc.rust-lang.org/std/fs/struct.ReadDir.html
69#[derive(Debug)]
70pub struct ReadDir(State);
71
72/// The state of an asynchronous `ReadDir`.
73///
74/// The `ReadDir` can be either idle or busy performing an asynchronous operation.
75#[derive(Debug)]
76enum State {
77    Idle(Option<std::fs::ReadDir>),
78    Busy(JoinHandle<(std::fs::ReadDir, Option<io::Result<std::fs::DirEntry>>)>),
79}
80
81impl ReadDir {
82    /// Creates an asynchronous `ReadDir` from a synchronous handle.
83    pub(crate) fn new(inner: std::fs::ReadDir) -> ReadDir {
84        ReadDir(State::Idle(Some(inner)))
85    }
86}
87
88impl Stream for ReadDir {
89    type Item = io::Result<DirEntry>;
90
91    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
92        loop {
93            match &mut self.0 {
94                State::Idle(opt) => {
95                    let mut inner = opt.take().unwrap();
96
97                    // Start the operation asynchronously.
98                    self.0 = State::Busy(spawn_blocking(move || {
99                        let next = inner.next();
100                        (inner, next)
101                    }));
102                }
103                // Poll the asynchronous operation the file is currently blocked on.
104                State::Busy(task) => {
105                    let (inner, opt) = futures_core::ready!(Pin::new(task).poll(cx));
106                    self.0 = State::Idle(Some(inner));
107                    return Poll::Ready(opt.map(|res| res.map(DirEntry::new)));
108                }
109            }
110        }
111    }
112}