1 | use std::future::Future; |
2 | use std::pin::Pin; |
3 | |
4 | use crate::fs::DirEntry; |
5 | use crate::io; |
6 | use crate::path::Path; |
7 | use crate::stream::Stream; |
8 | use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; |
9 | use 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 | /// ``` |
47 | pub async fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> { |
48 | let path: PathBuf = 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(op: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)] |
70 | pub 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)] |
76 | enum State { |
77 | Idle(Option<std::fs::ReadDir>), |
78 | Busy(JoinHandle<(std::fs::ReadDir, Option<io::Result<std::fs::DirEntry>>)>), |
79 | } |
80 | |
81 | impl 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 | |
88 | impl 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 | } |
113 | |