| use std::ffi::OsString; |
| use std::fs::{self, DirEntry as StdDirEntry, FileType, Metadata, ReadDir as StdReadDir}; |
| use std::io; |
| #[cfg(unix)] |
| use std::os::unix::fs::DirEntryExt; |
| use std::path::{Path, PathBuf}; |
| |
| use futures::{Future, Poll, Stream}; |
| |
| /// Returns a stream over the entries within a directory. |
| /// |
| /// This is an async version of [`std::fs::read_dir`][std] |
| /// |
| /// [std]: https://doc.rust-lang.org/std/fs/fn.read_dir.html |
| pub fn read_dir<P>(path: P) -> ReadDirFuture<P> |
| where |
| P: AsRef<Path> + Send + 'static, |
| { |
| ReadDirFuture::new(path) |
| } |
| |
| /// Future returned by `read_dir`. |
| #[derive(Debug)] |
| pub struct ReadDirFuture<P> |
| where |
| P: AsRef<Path> + Send + 'static, |
| { |
| path: P, |
| } |
| |
| impl<P> ReadDirFuture<P> |
| where |
| P: AsRef<Path> + Send + 'static, |
| { |
| fn new(path: P) -> ReadDirFuture<P> { |
| ReadDirFuture { path: path } |
| } |
| } |
| |
| impl<P> Future for ReadDirFuture<P> |
| where |
| P: AsRef<Path> + Send + 'static, |
| { |
| type Item = ReadDir; |
| type Error = io::Error; |
| |
| fn poll(&mut self) -> Poll<Self::Item, io::Error> { |
| ::blocking_io(|| Ok(ReadDir(fs::read_dir(&self.path)?))) |
| } |
| } |
| |
| /// Stream of the entries in a directory. |
| /// |
| /// This stream is returned from the [`read_dir`] function of this module and |
| /// will yield instances of [`DirEntry`]. Through a [`DirEntry`] |
| /// information like the entry's path and possibly other metadata can be |
| /// learned. |
| /// |
| /// # Errors |
| /// |
| /// This [`Stream`] will return an [`Err`] if there's some sort of intermittent |
| /// IO error during iteration. |
| /// |
| /// [`read_dir`]: fn.read_dir.html |
| /// [`DirEntry`]: struct.DirEntry.html |
| /// [`Stream`]: ../futures/stream/trait.Stream.html |
| /// [`Err`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err |
| #[derive(Debug)] |
| pub struct ReadDir(StdReadDir); |
| |
| impl Stream for ReadDir { |
| type Item = DirEntry; |
| type Error = io::Error; |
| |
| fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { |
| ::blocking_io(|| match self.0.next() { |
| Some(Err(err)) => Err(err), |
| Some(Ok(item)) => Ok(Some(DirEntry(item))), |
| None => Ok(None), |
| }) |
| } |
| } |
| |
| /// Entries returned by the [`ReadDir`] stream. |
| /// |
| /// [`ReadDir`]: struct.ReadDir.html |
| /// |
| /// This is a specialized version of [`std::fs::DirEntry`][std] for usage from the |
| /// Tokio runtime. |
| /// |
| /// An instance of `DirEntry` represents an entry inside of a directory on the |
| /// filesystem. Each entry can be inspected via methods to learn about the full |
| /// path or possibly other metadata through per-platform extension traits. |
| /// |
| /// [std]: https://doc.rust-lang.org/std/fs/struct.DirEntry.html |
| #[derive(Debug)] |
| pub struct DirEntry(StdDirEntry); |
| |
| impl DirEntry { |
| /// Destructures the `tokio_fs::DirEntry` into a [`std::fs::DirEntry`][std]. |
| /// |
| /// [std]: https://doc.rust-lang.org/std/fs/struct.DirEntry.html |
| pub fn into_std(self) -> StdDirEntry { |
| self.0 |
| } |
| |
| /// Returns the full path to the file that this entry represents. |
| /// |
| /// The full path is created by joining the original path to `read_dir` |
| /// with the filename of this entry. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// # extern crate futures; |
| /// # extern crate tokio; |
| /// # extern crate tokio_fs; |
| /// use futures::{Future, Stream}; |
| /// |
| /// fn main() { |
| /// let fut = tokio_fs::read_dir(".").flatten_stream().for_each(|dir| { |
| /// println!("{:?}", dir.path()); |
| /// Ok(()) |
| /// }).map_err(|err| { eprintln!("Error: {:?}", err); () }); |
| /// tokio::run(fut); |
| /// } |
| /// ``` |
| /// |
| /// This prints output like: |
| /// |
| /// ```text |
| /// "./whatever.txt" |
| /// "./foo.html" |
| /// "./hello_world.rs" |
| /// ``` |
| /// |
| /// The exact text, of course, depends on what files you have in `.`. |
| pub fn path(&self) -> PathBuf { |
| self.0.path() |
| } |
| |
| /// Returns the bare file name of this directory entry without any other |
| /// leading path component. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// # extern crate futures; |
| /// # extern crate tokio; |
| /// # extern crate tokio_fs; |
| /// use futures::{Future, Stream}; |
| /// |
| /// fn main() { |
| /// let fut = tokio_fs::read_dir(".").flatten_stream().for_each(|dir| { |
| /// // Here, `dir` is a `DirEntry`. |
| /// println!("{:?}", dir.file_name()); |
| /// Ok(()) |
| /// }).map_err(|err| { eprintln!("Error: {:?}", err); () }); |
| /// tokio::run(fut); |
| /// } |
| /// ``` |
| pub fn file_name(&self) -> OsString { |
| self.0.file_name() |
| } |
| |
| /// Return the metadata for the file that this entry points at. |
| /// |
| /// This function will not traverse symlinks if this entry points at a |
| /// symlink. |
| /// |
| /// # Platform-specific behavior |
| /// |
| /// On Windows this function is cheap to call (no extra system calls |
| /// needed), but on Unix platforms this function is the equivalent of |
| /// calling `symlink_metadata` on the path. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// # extern crate futures; |
| /// # extern crate tokio; |
| /// # extern crate tokio_fs; |
| /// use futures::{Future, Stream}; |
| /// use futures::future::poll_fn; |
| /// |
| /// fn main() { |
| /// let fut = tokio_fs::read_dir(".").flatten_stream().for_each(|dir| { |
| /// // Here, `dir` is a `DirEntry`. |
| /// let path = dir.path(); |
| /// poll_fn(move || dir.poll_metadata()).map(move |metadata| { |
| /// println!("{:?}: {:?}", path, metadata.permissions()); |
| /// }) |
| /// }).map_err(|err| { eprintln!("Error: {:?}", err); () }); |
| /// tokio::run(fut); |
| /// } |
| /// ``` |
| pub fn poll_metadata(&self) -> Poll<Metadata, io::Error> { |
| ::blocking_io(|| self.0.metadata()) |
| } |
| |
| /// Return the file type for the file that this entry points at. |
| /// |
| /// This function will not traverse symlinks if this entry points at a |
| /// symlink. |
| /// |
| /// # Platform-specific behavior |
| /// |
| /// On Windows and most Unix platforms this function is free (no extra |
| /// system calls needed), but some Unix platforms may require the equivalent |
| /// call to `symlink_metadata` to learn about the target file type. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// # extern crate futures; |
| /// # extern crate tokio; |
| /// # extern crate tokio_fs; |
| /// use futures::{Future, Stream}; |
| /// use futures::future::poll_fn; |
| /// |
| /// fn main() { |
| /// let fut = tokio_fs::read_dir(".").flatten_stream().for_each(|dir| { |
| /// // Here, `dir` is a `DirEntry`. |
| /// let path = dir.path(); |
| /// poll_fn(move || dir.poll_file_type()).map(move |file_type| { |
| /// // Now let's show our entry's file type! |
| /// println!("{:?}: {:?}", path, file_type); |
| /// }) |
| /// }).map_err(|err| { eprintln!("Error: {:?}", err); () }); |
| /// tokio::run(fut); |
| /// } |
| /// ``` |
| pub fn poll_file_type(&self) -> Poll<FileType, io::Error> { |
| ::blocking_io(|| self.0.file_type()) |
| } |
| } |
| |
| #[cfg(unix)] |
| impl DirEntryExt for DirEntry { |
| fn ino(&self) -> u64 { |
| self.0.ino() |
| } |
| } |