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