1 use crate::fs::asyncify;
2
3 use std::ffi::OsString;
4 use std::fs::{FileType, Metadata};
5 use std::future::Future;
6 use std::io;
7 use std::path::{Path, PathBuf};
8 use std::pin::Pin;
9 use std::sync::Arc;
10 use std::task::Context;
11 use std::task::Poll;
12
13 #[cfg(test)]
14 use super::mocks::spawn_blocking;
15 #[cfg(test)]
16 use super::mocks::JoinHandle;
17 #[cfg(not(test))]
18 use crate::blocking::spawn_blocking;
19 #[cfg(not(test))]
20 use crate::blocking::JoinHandle;
21
22 /// Returns a stream over the entries within a directory.
23 ///
24 /// This is an async version of [`std::fs::read_dir`](std::fs::read_dir)
25 ///
26 /// This operation is implemented by running the equivalent blocking
27 /// operation on a separate thread pool using [`spawn_blocking`].
28 ///
29 /// [`spawn_blocking`]: crate::task::spawn_blocking
read_dir(path: impl AsRef<Path>) -> io::Result<ReadDir>30 pub async fn read_dir(path: impl AsRef<Path>) -> io::Result<ReadDir> {
31 let path = path.as_ref().to_owned();
32 let std = asyncify(|| std::fs::read_dir(path)).await?;
33
34 Ok(ReadDir(State::Idle(Some(std))))
35 }
36
37 /// Reads the the entries in a directory.
38 ///
39 /// This struct is returned from the [`read_dir`] function of this module and
40 /// will yield instances of [`DirEntry`]. Through a [`DirEntry`] information
41 /// like the entry's path and possibly other metadata can be learned.
42 ///
43 /// A `ReadDir` can be turned into a `Stream` with [`ReadDirStream`].
44 ///
45 /// [`ReadDirStream`]: https://docs.rs/tokio-stream/0.1/tokio_stream/wrappers/struct.ReadDirStream.html
46 ///
47 /// # Errors
48 ///
49 /// This stream will return an [`Err`] if there's some sort of intermittent
50 /// IO error during iteration.
51 ///
52 /// [`read_dir`]: read_dir
53 /// [`DirEntry`]: DirEntry
54 /// [`Err`]: std::result::Result::Err
55 #[derive(Debug)]
56 #[must_use = "streams do nothing unless polled"]
57 pub struct ReadDir(State);
58
59 #[derive(Debug)]
60 enum State {
61 Idle(Option<std::fs::ReadDir>),
62 Pending(JoinHandle<(Option<io::Result<std::fs::DirEntry>>, std::fs::ReadDir)>),
63 }
64
65 impl ReadDir {
66 /// Returns the next entry in the directory stream.
67 ///
68 /// # Cancel safety
69 ///
70 /// This method is cancellation safe.
next_entry(&mut self) -> io::Result<Option<DirEntry>>71 pub async fn next_entry(&mut self) -> io::Result<Option<DirEntry>> {
72 use crate::future::poll_fn;
73 poll_fn(|cx| self.poll_next_entry(cx)).await
74 }
75
76 /// Polls for the next directory entry in the stream.
77 ///
78 /// This method returns:
79 ///
80 /// * `Poll::Pending` if the next directory entry is not yet available.
81 /// * `Poll::Ready(Ok(Some(entry)))` if the next directory entry is available.
82 /// * `Poll::Ready(Ok(None))` if there are no more directory entries in this
83 /// stream.
84 /// * `Poll::Ready(Err(err))` if an IO error occurred while reading the next
85 /// directory entry.
86 ///
87 /// When the method returns `Poll::Pending`, the `Waker` in the provided
88 /// `Context` is scheduled to receive a wakeup when the next directory entry
89 /// becomes available on the underlying IO resource.
90 ///
91 /// Note that on multiple calls to `poll_next_entry`, only the `Waker` from
92 /// the `Context` passed to the most recent call is scheduled to receive a
93 /// wakeup.
poll_next_entry(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<Option<DirEntry>>>94 pub fn poll_next_entry(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<Option<DirEntry>>> {
95 loop {
96 match self.0 {
97 State::Idle(ref mut std) => {
98 let mut std = std.take().unwrap();
99
100 self.0 = State::Pending(spawn_blocking(move || {
101 let ret = std.next();
102 (ret, std)
103 }));
104 }
105 State::Pending(ref mut rx) => {
106 let (ret, std) = ready!(Pin::new(rx).poll(cx))?;
107 self.0 = State::Idle(Some(std));
108
109 let ret = match ret {
110 Some(Ok(std)) => Ok(Some(DirEntry(Arc::new(std)))),
111 Some(Err(e)) => Err(e),
112 None => Ok(None),
113 };
114
115 return Poll::Ready(ret);
116 }
117 }
118 }
119 }
120 }
121
122 feature! {
123 #![unix]
124
125 use std::os::unix::fs::DirEntryExt;
126
127 impl DirEntry {
128 /// Returns the underlying `d_ino` field in the contained `dirent`
129 /// structure.
130 ///
131 /// # Examples
132 ///
133 /// ```
134 /// use tokio::fs;
135 ///
136 /// # #[tokio::main]
137 /// # async fn main() -> std::io::Result<()> {
138 /// let mut entries = fs::read_dir(".").await?;
139 /// while let Some(entry) = entries.next_entry().await? {
140 /// // Here, `entry` is a `DirEntry`.
141 /// println!("{:?}: {}", entry.file_name(), entry.ino());
142 /// }
143 /// # Ok(())
144 /// # }
145 /// ```
146 pub fn ino(&self) -> u64 {
147 self.as_inner().ino()
148 }
149 }
150 }
151
152 /// Entries returned by the [`ReadDir`] stream.
153 ///
154 /// [`ReadDir`]: struct@ReadDir
155 ///
156 /// This is a specialized version of [`std::fs::DirEntry`] for usage from the
157 /// Tokio runtime.
158 ///
159 /// An instance of `DirEntry` represents an entry inside of a directory on the
160 /// filesystem. Each entry can be inspected via methods to learn about the full
161 /// path or possibly other metadata through per-platform extension traits.
162 #[derive(Debug)]
163 pub struct DirEntry(Arc<std::fs::DirEntry>);
164
165 impl DirEntry {
166 /// Returns the full path to the file that this entry represents.
167 ///
168 /// The full path is created by joining the original path to `read_dir`
169 /// with the filename of this entry.
170 ///
171 /// # Examples
172 ///
173 /// ```no_run
174 /// use tokio::fs;
175 ///
176 /// # async fn dox() -> std::io::Result<()> {
177 /// let mut entries = fs::read_dir(".").await?;
178 ///
179 /// while let Some(entry) = entries.next_entry().await? {
180 /// println!("{:?}", entry.path());
181 /// }
182 /// # Ok(())
183 /// # }
184 /// ```
185 ///
186 /// This prints output like:
187 ///
188 /// ```text
189 /// "./whatever.txt"
190 /// "./foo.html"
191 /// "./hello_world.rs"
192 /// ```
193 ///
194 /// The exact text, of course, depends on what files you have in `.`.
path(&self) -> PathBuf195 pub fn path(&self) -> PathBuf {
196 self.0.path()
197 }
198
199 /// Returns the bare file name of this directory entry without any other
200 /// leading path component.
201 ///
202 /// # Examples
203 ///
204 /// ```
205 /// use tokio::fs;
206 ///
207 /// # async fn dox() -> std::io::Result<()> {
208 /// let mut entries = fs::read_dir(".").await?;
209 ///
210 /// while let Some(entry) = entries.next_entry().await? {
211 /// println!("{:?}", entry.file_name());
212 /// }
213 /// # Ok(())
214 /// # }
215 /// ```
file_name(&self) -> OsString216 pub fn file_name(&self) -> OsString {
217 self.0.file_name()
218 }
219
220 /// Returns the metadata for the file that this entry points at.
221 ///
222 /// This function will not traverse symlinks if this entry points at a
223 /// symlink.
224 ///
225 /// # Platform-specific behavior
226 ///
227 /// On Windows this function is cheap to call (no extra system calls
228 /// needed), but on Unix platforms this function is the equivalent of
229 /// calling `symlink_metadata` on the path.
230 ///
231 /// # Examples
232 ///
233 /// ```
234 /// use tokio::fs;
235 ///
236 /// # async fn dox() -> std::io::Result<()> {
237 /// let mut entries = fs::read_dir(".").await?;
238 ///
239 /// while let Some(entry) = entries.next_entry().await? {
240 /// if let Ok(metadata) = entry.metadata().await {
241 /// // Now let's show our entry's permissions!
242 /// println!("{:?}: {:?}", entry.path(), metadata.permissions());
243 /// } else {
244 /// println!("Couldn't get file type for {:?}", entry.path());
245 /// }
246 /// }
247 /// # Ok(())
248 /// # }
249 /// ```
metadata(&self) -> io::Result<Metadata>250 pub async fn metadata(&self) -> io::Result<Metadata> {
251 let std = self.0.clone();
252 asyncify(move || std.metadata()).await
253 }
254
255 /// Returns the file type for the file that this entry points at.
256 ///
257 /// This function will not traverse symlinks if this entry points at a
258 /// symlink.
259 ///
260 /// # Platform-specific behavior
261 ///
262 /// On Windows and most Unix platforms this function is free (no extra
263 /// system calls needed), but some Unix platforms may require the equivalent
264 /// call to `symlink_metadata` to learn about the target file type.
265 ///
266 /// # Examples
267 ///
268 /// ```
269 /// use tokio::fs;
270 ///
271 /// # async fn dox() -> std::io::Result<()> {
272 /// let mut entries = fs::read_dir(".").await?;
273 ///
274 /// while let Some(entry) = entries.next_entry().await? {
275 /// if let Ok(file_type) = entry.file_type().await {
276 /// // Now let's show our entry's file type!
277 /// println!("{:?}: {:?}", entry.path(), file_type);
278 /// } else {
279 /// println!("Couldn't get file type for {:?}", entry.path());
280 /// }
281 /// }
282 /// # Ok(())
283 /// # }
284 /// ```
file_type(&self) -> io::Result<FileType>285 pub async fn file_type(&self) -> io::Result<FileType> {
286 let std = self.0.clone();
287 asyncify(move || std.file_type()).await
288 }
289
290 /// Returns a reference to the underlying `std::fs::DirEntry`.
291 #[cfg(unix)]
as_inner(&self) -> &std::fs::DirEntry292 pub(super) fn as_inner(&self) -> &std::fs::DirEntry {
293 &self.0
294 }
295 }
296