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}