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