1 use crate::fs::{asyncify, sys};
2 
3 use std::ffi::OsString;
4 use std::fs::{FileType, Metadata};
5 use std::future::Future;
6 use std::io;
7 #[cfg(unix)]
8 use std::os::unix::fs::DirEntryExt;
9 use std::path::{Path, PathBuf};
10 use std::pin::Pin;
11 use std::sync::Arc;
12 use std::task::Context;
13 use std::task::Poll;
14 
15 /// Returns a stream over the entries within a directory.
16 ///
17 /// This is an async version of [`std::fs::read_dir`](std::fs::read_dir)
read_dir(path: impl AsRef<Path>) -> io::Result<ReadDir>18 pub async fn read_dir(path: impl AsRef<Path>) -> io::Result<ReadDir> {
19     let path = path.as_ref().to_owned();
20     let std = asyncify(|| std::fs::read_dir(path)).await?;
21 
22     Ok(ReadDir(State::Idle(Some(std))))
23 }
24 
25 /// Stream of the entries in a directory.
26 ///
27 /// This stream is returned from the [`read_dir`] function of this module and
28 /// will yield instances of [`DirEntry`]. Through a [`DirEntry`]
29 /// information like the entry's path and possibly other metadata can be
30 /// learned.
31 ///
32 /// # Errors
33 ///
34 /// This [`Stream`] will return an [`Err`] if there's some sort of intermittent
35 /// IO error during iteration.
36 ///
37 /// [`read_dir`]: read_dir
38 /// [`DirEntry`]: DirEntry
39 /// [`Stream`]: crate::stream::Stream
40 /// [`Err`]: std::result::Result::Err
41 #[derive(Debug)]
42 #[must_use = "streams do nothing unless polled"]
43 pub struct ReadDir(State);
44 
45 #[derive(Debug)]
46 enum State {
47     Idle(Option<std::fs::ReadDir>),
48     Pending(sys::Blocking<(Option<io::Result<std::fs::DirEntry>>, std::fs::ReadDir)>),
49 }
50 
51 impl ReadDir {
52     /// Returns the next entry in the directory stream.
next_entry(&mut self) -> io::Result<Option<DirEntry>>53     pub async fn next_entry(&mut self) -> io::Result<Option<DirEntry>> {
54         use crate::future::poll_fn;
55         poll_fn(|cx| self.poll_next_entry(cx)).await
56     }
57 
58     #[doc(hidden)]
poll_next_entry(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<Option<DirEntry>>>59     pub fn poll_next_entry(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<Option<DirEntry>>> {
60         loop {
61             match self.0 {
62                 State::Idle(ref mut std) => {
63                     let mut std = std.take().unwrap();
64 
65                     self.0 = State::Pending(sys::run(move || {
66                         let ret = std.next();
67                         (ret, std)
68                     }));
69                 }
70                 State::Pending(ref mut rx) => {
71                     let (ret, std) = ready!(Pin::new(rx).poll(cx))?;
72                     self.0 = State::Idle(Some(std));
73 
74                     let ret = match ret {
75                         Some(Ok(std)) => Ok(Some(DirEntry(Arc::new(std)))),
76                         Some(Err(e)) => Err(e),
77                         None => Ok(None),
78                     };
79 
80                     return Poll::Ready(ret);
81                 }
82             }
83         }
84     }
85 }
86 
87 #[cfg(feature = "stream")]
88 impl crate::stream::Stream for ReadDir {
89     type Item = io::Result<DirEntry>;
90 
poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>91     fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
92         Poll::Ready(match ready!(self.poll_next_entry(cx)) {
93             Ok(Some(entry)) => Some(Ok(entry)),
94             Ok(None) => None,
95             Err(err) => Some(Err(err)),
96         })
97     }
98 }
99 
100 /// Entries returned by the [`ReadDir`] stream.
101 ///
102 /// [`ReadDir`]: struct@ReadDir
103 ///
104 /// This is a specialized version of [`std::fs::DirEntry`] for usage from the
105 /// Tokio runtime.
106 ///
107 /// An instance of `DirEntry` represents an entry inside of a directory on the
108 /// filesystem. Each entry can be inspected via methods to learn about the full
109 /// path or possibly other metadata through per-platform extension traits.
110 #[derive(Debug)]
111 pub struct DirEntry(Arc<std::fs::DirEntry>);
112 
113 impl DirEntry {
114     /// Returns the full path to the file that this entry represents.
115     ///
116     /// The full path is created by joining the original path to `read_dir`
117     /// with the filename of this entry.
118     ///
119     /// # Examples
120     ///
121     /// ```no_run
122     /// use tokio::fs;
123     ///
124     /// # async fn dox() -> std::io::Result<()> {
125     /// let mut entries = fs::read_dir(".").await?;
126     ///
127     /// while let Some(entry) = entries.next_entry().await? {
128     ///     println!("{:?}", entry.path());
129     /// }
130     /// # Ok(())
131     /// # }
132     /// ```
133     ///
134     /// This prints output like:
135     ///
136     /// ```text
137     /// "./whatever.txt"
138     /// "./foo.html"
139     /// "./hello_world.rs"
140     /// ```
141     ///
142     /// The exact text, of course, depends on what files you have in `.`.
path(&self) -> PathBuf143     pub fn path(&self) -> PathBuf {
144         self.0.path()
145     }
146 
147     /// Returns the bare file name of this directory entry without any other
148     /// leading path component.
149     ///
150     /// # Examples
151     ///
152     /// ```
153     /// use tokio::fs;
154     ///
155     /// # async fn dox() -> std::io::Result<()> {
156     /// let mut entries = fs::read_dir(".").await?;
157     ///
158     /// while let Some(entry) = entries.next_entry().await? {
159     ///     println!("{:?}", entry.file_name());
160     /// }
161     /// # Ok(())
162     /// # }
163     /// ```
file_name(&self) -> OsString164     pub fn file_name(&self) -> OsString {
165         self.0.file_name()
166     }
167 
168     /// Returns the metadata for the file that this entry points at.
169     ///
170     /// This function will not traverse symlinks if this entry points at a
171     /// symlink.
172     ///
173     /// # Platform-specific behavior
174     ///
175     /// On Windows this function is cheap to call (no extra system calls
176     /// needed), but on Unix platforms this function is the equivalent of
177     /// calling `symlink_metadata` on the path.
178     ///
179     /// # Examples
180     ///
181     /// ```
182     /// use tokio::fs;
183     ///
184     /// # async fn dox() -> std::io::Result<()> {
185     /// let mut entries = fs::read_dir(".").await?;
186     ///
187     /// while let Some(entry) = entries.next_entry().await? {
188     ///     if let Ok(metadata) = entry.metadata().await {
189     ///         // Now let's show our entry's permissions!
190     ///         println!("{:?}: {:?}", entry.path(), metadata.permissions());
191     ///     } else {
192     ///         println!("Couldn't get file type for {:?}", entry.path());
193     ///     }
194     /// }
195     /// # Ok(())
196     /// # }
197     /// ```
metadata(&self) -> io::Result<Metadata>198     pub async fn metadata(&self) -> io::Result<Metadata> {
199         let std = self.0.clone();
200         asyncify(move || std.metadata()).await
201     }
202 
203     /// Returns the file type for the file that this entry points at.
204     ///
205     /// This function will not traverse symlinks if this entry points at a
206     /// symlink.
207     ///
208     /// # Platform-specific behavior
209     ///
210     /// On Windows and most Unix platforms this function is free (no extra
211     /// system calls needed), but some Unix platforms may require the equivalent
212     /// call to `symlink_metadata` to learn about the target file type.
213     ///
214     /// # Examples
215     ///
216     /// ```
217     /// use tokio::fs;
218     ///
219     /// # async fn dox() -> std::io::Result<()> {
220     /// let mut entries = fs::read_dir(".").await?;
221     ///
222     /// while let Some(entry) = entries.next_entry().await? {
223     ///     if let Ok(file_type) = entry.file_type().await {
224     ///         // Now let's show our entry's file type!
225     ///         println!("{:?}: {:?}", entry.path(), file_type);
226     ///     } else {
227     ///         println!("Couldn't get file type for {:?}", entry.path());
228     ///     }
229     /// }
230     /// # Ok(())
231     /// # }
232     /// ```
file_type(&self) -> io::Result<FileType>233     pub async fn file_type(&self) -> io::Result<FileType> {
234         let std = self.0.clone();
235         asyncify(move || std.file_type()).await
236     }
237 }
238 
239 #[cfg(unix)]
240 impl DirEntryExt for DirEntry {
ino(&self) -> u64241     fn ino(&self) -> u64 {
242         self.0.ino()
243     }
244 }
245