diff --git a/tokio-fs/Cargo.toml b/tokio-fs/Cargo.toml index c35894988a2..64dbb7b4194 100644 --- a/tokio-fs/Cargo.toml +++ b/tokio-fs/Cargo.toml @@ -24,15 +24,16 @@ categories = ["asynchronous", "network-programming", "filesystem"] publish = false [dependencies] -futures-core-preview = "0.3.0-alpha.17" -tokio-threadpool = { version = "0.2.0", path = "../tokio-threadpool" } tokio-io = { version = "0.2.0", path = "../tokio-io" } +tokio-threadpool = { version = "0.2.0", path = "../tokio-threadpool" } + +futures-core-preview = "= 0.3.0-alpha.17" +futures-util-preview = "= 0.3.0-alpha.17" [dev-dependencies] rand = "0.7" tempfile = "3" -tokio-codec = { version = "0.2.0", path = "../tokio-codec" } -tokio = { version = "0.2.0", path = "../tokio" } -futures-channel-preview = "0.3.0-alpha.17" -futures-preview = { version = "0.3.0-alpha.17" } -futures-util-preview = "0.3.0-alpha.17" +# tokio-codec = { version = "0.2.0", path = "../tokio-codec" } +tokio = { version = "*", path = "../tokio" } +# futures-preview = { version = "0.3.0-alpha.17" } +# futures-util-preview = "0.3.0-alpha.17" diff --git a/tokio-fs/src/create_dir.rs b/tokio-fs/src/create_dir.rs index 9f245dc4122..19133c2d98f 100644 --- a/tokio-fs/src/create_dir.rs +++ b/tokio-fs/src/create_dir.rs @@ -1,46 +1,13 @@ -use std::fs; -use std::future::Future; +use crate::asyncify; + use std::io; use std::path::Path; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; /// Creates a new, empty directory at the provided path /// /// This is an async version of [`std::fs::create_dir`][std] /// /// [std]: https://doc.rust-lang.org/std/fs/fn.create_dir.html -pub fn create_dir>(path: P) -> CreateDirFuture

{ - CreateDirFuture::new(path) -} - -/// Future returned by `create_dir`. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct CreateDirFuture

-where - P: AsRef, -{ - path: P, -} - -impl

CreateDirFuture

-where - P: AsRef, -{ - fn new(path: P) -> CreateDirFuture

{ - CreateDirFuture { path } - } -} - -impl

Future for CreateDirFuture

-where - P: AsRef, -{ - type Output = io::Result<()>; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - crate::blocking_io(|| fs::create_dir(&self.path)) - } +pub async fn create_dir>(path: P) -> io::Result<()> { + asyncify(|| std::fs::create_dir(&path)).await } diff --git a/tokio-fs/src/create_dir_all.rs b/tokio-fs/src/create_dir_all.rs index 37e675bee2e..3bbcbabddb1 100644 --- a/tokio-fs/src/create_dir_all.rs +++ b/tokio-fs/src/create_dir_all.rs @@ -1,10 +1,7 @@ -use std::fs; -use std::future::Future; +use crate::asyncify; + use std::io; use std::path::Path; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; /// Recursively create a directory and all of its parent components if they /// are missing. @@ -12,36 +9,6 @@ use std::task::Poll; /// This is an async version of [`std::fs::create_dir_all`][std] /// /// [std]: https://doc.rust-lang.org/std/fs/fn.create_dir_all.html -pub fn create_dir_all>(path: P) -> CreateDirAllFuture

{ - CreateDirAllFuture::new(path) -} - -/// Future returned by `create_dir_all`. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct CreateDirAllFuture

-where - P: AsRef, -{ - path: P, -} - -impl

CreateDirAllFuture

-where - P: AsRef, -{ - fn new(path: P) -> CreateDirAllFuture

{ - CreateDirAllFuture { path } - } -} - -impl

Future for CreateDirAllFuture

-where - P: AsRef, -{ - type Output = io::Result<()>; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - crate::blocking_io(|| fs::create_dir_all(&self.path)) - } +pub async fn create_dir_all>(path: P) -> io::Result<()> { + asyncify(|| std::fs::create_dir_all(&path)).await } diff --git a/tokio-fs/src/file.rs b/tokio-fs/src/file.rs new file mode 100644 index 00000000000..ca1e14a0f3f --- /dev/null +++ b/tokio-fs/src/file.rs @@ -0,0 +1,425 @@ +//! Types for working with [`File`]. +//! +//! [`File`]: file/struct.File.html + +use crate::{asyncify, blocking_io, OpenOptions}; + +use tokio_io::{AsyncRead, AsyncWrite}; + +use std::convert::TryFrom; +use std::fs::{Metadata, Permissions}; +use std::io::{self, Read, Seek, Write}; +use std::path::Path; +use std::pin::Pin; +use std::task::Context; +use std::task::Poll; + +/// A reference to an open file on the filesystem. +/// +/// This is a specialized version of [`std::fs::File`][std] for usage from the +/// Tokio runtime. +/// +/// An instance of a `File` can be read and/or written depending on what options +/// it was opened with. Files also implement Seek to alter the logical cursor +/// that the file contains internally. +/// +/// Files are automatically closed when they go out of scope. +/// +/// [std]: https://doc.rust-lang.org/std/fs/struct.File.html +/// +/// # Examples +/// +/// Create a new file and asynchronously write bytes to it: +/// +/// ```no_run +/// #![feature(async_await)] +/// +/// use tokio::fs::File; +/// use tokio::prelude::*; +/// +/// # async fn dox() -> std::io::Result<()> { +/// let mut file = File::create("foo.txt").await?; +/// file.write_all(b"hello, world!").await?; +/// # Ok(()) +/// # } +/// ``` +/// +/// Read the contents of a file into a buffer +/// +/// ```no_run +/// #![feature(async_await)] +/// +/// use tokio::fs::File; +/// use tokio::prelude::*; +/// +/// # async fn dox() -> std::io::Result<()> { +/// let mut file = File::open("foo.txt").await?; +/// +/// let mut contents = vec![]; +/// file.read_to_end(&mut contents).await?; +/// +/// println!("len = {}", contents.len()); +/// # Ok(()) +/// # } +/// ``` +#[derive(Debug)] +pub struct File { + std: std::fs::File, +} + +impl File { + /// Attempts to open a file in read-only mode. + /// + /// See [`OpenOptions`] for more details. + /// + /// [`OpenOptions`]: struct.OpenOptions.html + /// + /// # Errors + /// + /// This function will return an error if called from outside of the Tokio + /// runtime or if path does not already exist. Other errors may also be + /// returned according to OpenOptions::open. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(async_await)] + /// + /// use tokio::fs::File; + /// use tokio::prelude::*; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let mut file = File::open("foo.txt").await?; + /// + /// let mut contents = vec![]; + /// file.read_to_end(&mut contents).await?; + /// + /// println!("len = {}", contents.len()); + /// # Ok(()) + /// # } + /// ``` + pub async fn open

(path: P) -> io::Result + where + P: AsRef + Send + Unpin + 'static, + { + let mut open_options = OpenOptions::new(); + open_options.read(true); + + open_options.open(path).await + } + + /// Opens a file in write-only mode. + /// + /// This function will create a file if it does not exist, and will truncate + /// it if it does. + /// + /// See [`OpenOptions`] for more details. + /// + /// [`OpenOptions`]: struct.OpenOptions.html + /// + /// # Errors + /// + /// Results in an error if called from outside of the Tokio runtime or if + /// the underlying [`create`] call results in an error. + /// + /// [`create`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.create + /// + /// # Examples + /// + /// ```no_run + /// #![feature(async_await)] + /// + /// use tokio::fs::File; + /// use tokio::prelude::*; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let mut file = File::create("foo.txt").await?; + /// file.write_all(b"hello, world!").await?; + /// # Ok(()) + /// # } + /// ``` + pub async fn create

(path: P) -> io::Result + where + P: AsRef + Send + Unpin + 'static, + { + let std_file = asyncify(|| std::fs::File::create(&path)).await?; + Ok(File::from_std(std_file)) + } + + /// Convert a [`std::fs::File`][std] to a [`tokio_fs::File`][file]. + /// + /// [std]: https://doc.rust-lang.org/std/fs/struct.File.html + /// [file]: struct.File.html + /// + /// # Examples + /// + /// ```no_run + /// // This line could block. It is not recommended to do this on the Tokio + /// // runtime. + /// let std_file = std::fs::File::open("foo.txt").unwrap(); + /// let file = tokio::fs::File::from_std(std_file); + /// ``` + pub fn from_std(std: std::fs::File) -> File { + File { std } + } + + /// Seek to an offset, in bytes, in a stream. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(async_await)] + /// + /// use tokio::fs::File; + /// use tokio::prelude::*; + /// + /// use std::io::SeekFrom; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let mut file = File::open("foo.txt").await?; + /// file.seek(SeekFrom::Start(6)).await?; + /// + /// let mut contents = vec![0u8; 10]; + /// file.read_exact(&mut contents).await?; + /// # Ok(()) + /// # } + /// ``` + #[allow(clippy::needless_lifetimes)] // false positive: https://github.com/rust-lang/rust-clippy/issues/3988 + pub async fn seek(&mut self, pos: io::SeekFrom) -> io::Result { + asyncify(|| self.std.seek(pos)).await + } + + /// Attempts to sync all OS-internal metadata to disk. + /// + /// This function will attempt to ensure that all in-core data reaches the + /// filesystem before returning. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(async_await)] + /// + /// use tokio::fs::File; + /// use tokio::prelude::*; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let mut file = File::create("foo.txt").await?; + /// file.write_all(b"hello, world!").await?; + /// file.sync_all().await?; + /// # Ok(()) + /// # } + /// ``` + #[allow(clippy::needless_lifetimes)] // false positive: https://github.com/rust-lang/rust-clippy/issues/3988 + pub async fn sync_all(&mut self) -> io::Result<()> { + asyncify(|| self.std.sync_all()).await + } + + /// This function is similar to `poll_sync_all`, except that it may not + /// synchronize file metadata to the filesystem. + /// + /// This is intended for use cases that must synchronize content, but don't + /// need the metadata on disk. The goal of this method is to reduce disk + /// operations. + /// + /// Note that some platforms may simply implement this in terms of `poll_sync_all`. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(async_await)] + /// + /// use tokio::fs::File; + /// use tokio::prelude::*; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let mut file = File::create("foo.txt").await?; + /// file.write_all(b"hello, world!").await?; + /// file.sync_data().await?; + /// # Ok(()) + /// # } + /// ``` + #[allow(clippy::needless_lifetimes)] // false positive: https://github.com/rust-lang/rust-clippy/issues/3988 + pub async fn sync_data(&mut self) -> io::Result<()> { + asyncify(|| self.std.sync_data()).await + } + + /// Truncates or extends the underlying file, updating the size of this file to become size. + /// + /// If the size is less than the current file's size, then the file will be + /// shrunk. If it is greater than the current file's size, then the file + /// will be extended to size and have all of the intermediate data filled in + /// with 0s. + /// + /// # Errors + /// + /// This function will return an error if the file is not opened for + /// writing. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(async_await)] + /// + /// use tokio::fs::File; + /// use tokio::prelude::*; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let mut file = File::create("foo.txt").await?; + /// file.write_all(b"hello, world!").await?; + /// file.set_len(10).await?; + /// # Ok(()) + /// # } + /// ``` + #[allow(clippy::needless_lifetimes)] // false positive: https://github.com/rust-lang/rust-clippy/issues/3988 + pub async fn set_len(&mut self, size: u64) -> io::Result<()> { + asyncify(|| self.std.set_len(size)).await + } + + /// Queries metadata about the underlying file. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(async_await)] + /// + /// use tokio::fs::File; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let file = File::open("foo.txt").await?; + /// let metadata = file.metadata().await?; + /// + /// println!("{:?}", metadata); + /// # Ok(()) + /// # } + /// ``` + #[allow(clippy::needless_lifetimes)] // false positive: https://github.com/rust-lang/rust-clippy/issues/3988 + pub async fn metadata(&self) -> io::Result { + asyncify(|| self.std.metadata()).await + } + + /// Create a new `File` instance that shares the same underlying file handle + /// as the existing `File` instance. Reads, writes, and seeks will affect both + /// File instances simultaneously. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(async_await)] + /// + /// use tokio::fs::File; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let file = File::open("foo.txt").await?; + /// let file_clone = file.try_clone().await?; + /// # Ok(()) + /// # } + /// ``` + #[allow(clippy::needless_lifetimes)] // false positive: https://github.com/rust-lang/rust-clippy/issues/3988 + pub async fn try_clone(&self) -> io::Result { + let std_file = asyncify(|| self.std.try_clone()).await?; + Ok(File::from_std(std_file)) + } + + /// Changes the permissions on the underlying file. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `fchmod` function on Unix and + /// the `SetFileInformationByHandle` function on Windows. Note that, this + /// [may change in the future][changes]. + /// + /// [changes]: https://doc.rust-lang.org/std/io/index.html#platform-specific-behavior + /// + /// # Errors + /// + /// This function will return an error if the user lacks permission change + /// attributes on the underlying file. It may also return an error in other + /// os-specific unspecified cases. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(async_await)] + /// + /// use tokio::fs::File; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let file = File::open("foo.txt").await?; + /// let mut perms = file.metadata().await?.permissions(); + /// perms.set_readonly(true); + /// file.set_permissions(perms).await?; + /// # Ok(()) + /// # } + /// ``` + #[allow(clippy::needless_lifetimes)] // false positive: https://github.com/rust-lang/rust-clippy/issues/3988 + pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> { + asyncify(|| self.std.set_permissions(perm)).await + } + + /// Destructures the `tokio_fs::File` into a [`std::fs::File`][std]. + /// + /// # Panics + /// + /// This function will panic if `shutdown` has been called. + /// + /// [std]: https://doc.rust-lang.org/std/fs/struct.File.html + /// + /// # Examples + /// + /// ```no_run + /// #![feature(async_await)] + /// + /// use tokio::fs::File; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let file = File::create("foo.txt").await?; + /// let std_file = file.into_std(); + /// # Ok(()) + /// # } + /// ``` + pub fn into_std(self) -> std::fs::File { + self.std + } +} + +impl AsyncRead for File { + fn poll_read( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + blocking_io(|| (&self.std).read(buf)) + } +} + +impl AsyncWrite for File { + fn poll_write( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + blocking_io(|| (&self.std).write(buf)) + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + blocking_io(|| (&self.std).flush()) + } + + fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} + +impl From for File { + fn from(std: std::fs::File) -> Self { + Self::from_std(std) + } +} + +impl TryFrom for std::fs::File { + type Error = io::Error; + + fn try_from(file: File) -> Result { + Ok(file.std) + } +} diff --git a/tokio-fs/src/file/clone.rs b/tokio-fs/src/file/clone.rs deleted file mode 100644 index bec790338dc..00000000000 --- a/tokio-fs/src/file/clone.rs +++ /dev/null @@ -1,41 +0,0 @@ -use super::File; - -use std::future::Future; -use std::io; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; - -/// Future returned by `File::try_clone`. -/// -/// Clones a file handle into two file handles. -/// -/// # Panics -/// -/// Will panic if polled after returning an item or error. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct CloneFuture { - file: Option, -} - -impl CloneFuture { - pub(crate) fn new(file: File) -> Self { - Self { file: Some(file) } - } -} - -impl Future for CloneFuture { - type Output = Result<(File, File), (File, io::Error)>; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - let inner_self = Pin::get_mut(self); - inner_self - .file - .as_mut() - .expect("Cannot poll `CloneFuture` after it resolves") - .poll_try_clone() - .map(|inner| inner.map(|cloned| (inner_self.file.take().unwrap(), cloned))) - .map_err(|err| (inner_self.file.take().unwrap(), err)) - } -} diff --git a/tokio-fs/src/file/create.rs b/tokio-fs/src/file/create.rs deleted file mode 100644 index 65feef6c00e..00000000000 --- a/tokio-fs/src/file/create.rs +++ /dev/null @@ -1,40 +0,0 @@ -use super::File; - -use futures_core::ready; -use std::fs::File as StdFile; -use std::future::Future; -use std::io; -use std::path::Path; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; - -/// Future returned by `File::create` and resolves to a `File` instance. -#[must_use = "futures do nothing unless you `.await` or poll them"] -#[derive(Debug)] -pub struct CreateFuture

{ - path: P, -} - -impl

CreateFuture

-where - P: AsRef + Send + Unpin + 'static, -{ - pub(crate) fn new(path: P) -> Self { - CreateFuture { path } - } -} - -impl

Future for CreateFuture

-where - P: AsRef + Send + Unpin + 'static, -{ - type Output = io::Result; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - let std = ready!(crate::blocking_io(|| StdFile::create(&self.path)))?; - - let file = File::from_std(std); - Poll::Ready(Ok(file)) - } -} diff --git a/tokio-fs/src/file/metadata.rs b/tokio-fs/src/file/metadata.rs deleted file mode 100644 index a29f17d8c26..00000000000 --- a/tokio-fs/src/file/metadata.rs +++ /dev/null @@ -1,41 +0,0 @@ -use super::File; - -use futures_core::ready; -use std::fs::File as StdFile; -use std::fs::Metadata; -use std::future::Future; -use std::io; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; - -const POLL_AFTER_RESOLVE: &str = "Cannot poll MetadataFuture after it resolves"; - -/// Future returned by `File::metadata` and resolves to a `(File, Metadata)` instance. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct MetadataFuture { - file: Option, -} - -impl MetadataFuture { - pub(crate) fn new(file: File) -> Self { - MetadataFuture { file: Some(file) } - } - - fn std(&mut self) -> &mut StdFile { - self.file.as_mut().expect(POLL_AFTER_RESOLVE).std() - } -} - -impl Future for MetadataFuture { - type Output = io::Result<(File, Metadata)>; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - let inner = Pin::get_mut(self); - let metadata = ready!(crate::blocking_io(|| StdFile::metadata(inner.std())))?; - - let file = inner.file.take().expect(POLL_AFTER_RESOLVE); - Poll::Ready(Ok((file, metadata))) - } -} diff --git a/tokio-fs/src/file/mod.rs b/tokio-fs/src/file/mod.rs deleted file mode 100644 index d3cae8b55ac..00000000000 --- a/tokio-fs/src/file/mod.rs +++ /dev/null @@ -1,555 +0,0 @@ -//! Types for working with [`File`]. -//! -//! [`File`]: file/struct.File.html - -mod clone; -mod create; -mod metadata; -mod open; -mod open_options; -mod seek; - -pub use self::clone::CloneFuture; -pub use self::create::CreateFuture; -pub use self::metadata::MetadataFuture; -pub use self::open::OpenFuture; -pub use self::open_options::OpenOptions; -pub use self::seek::SeekFuture; - -use tokio_io::{AsyncRead, AsyncWrite}; - -use std::convert::TryFrom; -use std::fs::{File as StdFile, Metadata, Permissions}; -use std::io::{self, Read, Seek, Write}; -use std::path::Path; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; - -/// A reference to an open file on the filesystem. -/// -/// This is a specialized version of [`std::fs::File`][std] for usage from the -/// Tokio runtime. -/// -/// An instance of a `File` can be read and/or written depending on what options -/// it was opened with. Files also implement Seek to alter the logical cursor -/// that the file contains internally. -/// -/// Files are automatically closed when they go out of scope. -/// -/// [std]: https://doc.rust-lang.org/std/fs/struct.File.html -/// -/// # Examples -/// -/// Create a new file and asynchronously write bytes to it: -/// -/// ```no_run -/// use tokio::prelude::{AsyncWrite, Future}; -/// -/// let task = tokio::fs::File::create("foo.txt") -/// .and_then(|mut file| file.poll_write(b"hello, world!")) -/// .map(|res| { -/// println!("{:?}", res); -/// }).map_err(|err| eprintln!("IO error: {:?}", err)); -/// -/// tokio::run(task); -/// ``` -/// -/// Read the contents of a file into a buffer -/// -/// ```no_run -/// use tokio::prelude::{AsyncRead, Future}; -/// -/// let task = tokio::fs::File::open("foo.txt") -/// .and_then(|mut file| { -/// let mut contents = vec![]; -/// file.read_buf(&mut contents) -/// .map(|res| { -/// println!("{:?}", res); -/// }) -/// }).map_err(|err| eprintln!("IO error: {:?}", err)); -/// -/// tokio::run(task); -/// ``` -#[derive(Debug)] -pub struct File { - std: Option, -} - -impl File { - /// Attempts to open a file in read-only mode. - /// - /// See [`OpenOptions`] for more details. - /// - /// [`OpenOptions`]: struct.OpenOptions.html - /// - /// # Errors - /// - /// `OpenFuture` results in an error if called from outside of the Tokio - /// runtime or if the underlying [`open`] call results in an error. - /// - /// [`open`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.open - /// - /// # Examples - /// - /// ```no_run - /// use tokio::prelude::Future; - /// - /// let task = tokio::fs::File::open("foo.txt").and_then(|file| { - /// // do something with the file ... - /// file.metadata().map(|md| println!("{:?}", md)) - /// }).map_err(|e| { - /// // handle errors - /// eprintln!("IO error: {:?}", e); - /// }); - /// - /// tokio::run(task); - /// ``` - pub fn open

(path: P) -> OpenFuture

- where - P: AsRef + Send + Unpin + 'static, - { - OpenOptions::new().read(true).open(path) - } - - /// Opens a file in write-only mode. - /// - /// This function will create a file if it does not exist, and will truncate - /// it if it does. - /// - /// See [`OpenOptions`] for more details. - /// - /// [`OpenOptions`]: struct.OpenOptions.html - /// - /// # Errors - /// - /// `CreateFuture` results in an error if called from outside of the Tokio - /// runtime or if the underlying [`create`] call results in an error. - /// - /// [`create`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.create - /// - /// # Examples - /// - /// ```no_run - /// use tokio::prelude::Future; - /// - /// let task = tokio::fs::File::create("foo.txt") - /// .and_then(|file| { - /// // do something with the created file ... - /// file.metadata().map(|md| println!("{:?}", md)) - /// }).map_err(|e| { - /// // handle errors - /// eprintln!("IO error: {:?}", e); - /// }); - /// - /// tokio::run(task); - /// ``` - pub fn create

(path: P) -> CreateFuture

- where - P: AsRef + Send + Unpin + 'static, - { - CreateFuture::new(path) - } - - /// Convert a [`std::fs::File`][std] to a [`tokio_fs::File`][file]. - /// - /// [std]: https://doc.rust-lang.org/std/fs/struct.File.html - /// [file]: struct.File.html - /// - /// Examples - /// ```no_run - /// use std::fs::File; - /// - /// let std_file = File::open("foo.txt").unwrap(); - /// let file = tokio::fs::File::from_std(std_file); - /// ``` - pub fn from_std(std: StdFile) -> File { - File { std: Some(std) } - } - - /// Seek to an offset, in bytes, in a stream. - /// - /// A seek beyond the end of a stream is allowed, but implementation - /// defined. - /// - /// If the seek operation completed successfully, this method returns the - /// new position from the start of the stream. That position can be used - /// later with `SeekFrom::Start`. - /// - /// # Errors - /// - /// Seeking to a negative offset is considered an error. - /// - /// # Examples - /// - /// ```no_run - /// use tokio::prelude::Future; - /// use std::io::SeekFrom; - /// - /// let task = tokio::fs::File::open("foo.txt") - /// // move cursor 6 bytes from the start of the file - /// .and_then(|mut file| file.poll_seek(SeekFrom::Start(6))) - /// .map(|res| { - /// println!("{:?}", res); - /// }).map_err(|err| eprintln!("IO error: {:?}", err)); - /// - /// tokio::run(task); - /// ``` - pub fn poll_seek(&mut self, pos: io::SeekFrom) -> Poll> { - crate::blocking_io(|| self.std().seek(pos)) - } - - /// Seek to an offset, in bytes, in a stream. - /// - /// Similar to `poll_seek`, but returning a `Future`. - /// - /// This method consumes the `File` and returns it back when the future - /// completes. - /// - /// # Examples - /// - /// ```no_run - /// use tokio::prelude::Future; - /// use std::io::SeekFrom; - /// - /// let task = tokio::fs::File::create("foo.txt") - /// .and_then(|file| file.seek(SeekFrom::Start(6))) - /// .map(|file| { - /// // handle returned file .. - /// # println!("{:?}", file); - /// }).map_err(|err| eprintln!("IO error: {:?}", err)); - /// - /// tokio::run(task); - /// ``` - pub fn seek(self, pos: io::SeekFrom) -> SeekFuture { - SeekFuture::new(self, pos) - } - - /// Attempts to sync all OS-internal metadata to disk. - /// - /// This function will attempt to ensure that all in-core data reaches the - /// filesystem before returning. - /// - /// # Examples - /// - /// ```no_run - /// use tokio::prelude::{AsyncWrite, Future}; - /// - /// let task = tokio::fs::File::create("foo.txt") - /// .and_then(|mut file| { - /// file.poll_write(b"hello, world!")?; - /// file.poll_sync_all() - /// }) - /// .map(|res| { - /// // handle returned result .. - /// # println!("{:?}", res); - /// }).map_err(|err| eprintln!("IO error: {:?}", err)); - /// - /// tokio::run(task); - /// ``` - pub fn poll_sync_all(&mut self) -> Poll> { - crate::blocking_io(|| self.std().sync_all()) - } - - /// This function is similar to `poll_sync_all`, except that it may not - /// synchronize file metadata to the filesystem. - /// - /// This is intended for use cases that must synchronize content, but don't - /// need the metadata on disk. The goal of this method is to reduce disk - /// operations. - /// - /// Note that some platforms may simply implement this in terms of `poll_sync_all`. - /// - /// # Examples - /// - /// ```no_run - /// use tokio::prelude::{AsyncWrite, Future}; - /// - /// let task = tokio::fs::File::create("foo.txt") - /// .and_then(|mut file| { - /// file.poll_write(b"hello, world!")?; - /// file.poll_sync_data() - /// }) - /// .map(|res| { - /// // handle returned result .. - /// # println!("{:?}", res); - /// }).map_err(|err| eprintln!("IO error: {:?}", err)); - /// - /// tokio::run(task); - /// ``` - pub fn poll_sync_data(&mut self) -> Poll> { - crate::blocking_io(|| self.std().sync_data()) - } - - /// Truncates or extends the underlying file, updating the size of this file to become size. - /// - /// If the size is less than the current file's size, then the file will be - /// shrunk. If it is greater than the current file's size, then the file - /// will be extended to size and have all of the intermediate data filled in - /// with 0s. - /// - /// # Errors - /// - /// This function will return an error if the file is not opened for - /// writing. - /// - /// # Examples - /// - /// ```no_run - /// use tokio::prelude::Future; - /// - /// let task = tokio::fs::File::create("foo.txt") - /// .and_then(|mut file| { - /// file.poll_set_len(10) - /// }) - /// .map(|res| { - /// // handle returned result .. - /// # println!("{:?}", res); - /// }).map_err(|err| eprintln!("IO error: {:?}", err)); - /// - /// tokio::run(task); - /// ``` - pub fn poll_set_len(&mut self, size: u64) -> Poll> { - crate::blocking_io(|| self.std().set_len(size)) - } - - /// Queries metadata about the underlying file. - /// - /// # Examples - /// - /// ```no_run - /// use tokio::prelude::Future; - /// - /// let task = tokio::fs::File::create("foo.txt") - /// .and_then(|file| file.metadata()) - /// .map(|metadata| { - /// println!("{:?}", metadata); - /// }).map_err(|err| eprintln!("IO error: {:?}", err)); - /// - /// tokio::run(task); - /// ``` - pub fn metadata(self) -> MetadataFuture { - MetadataFuture::new(self) - } - - /// Queries metadata about the underlying file. - /// - /// # Examples - /// - /// ```no_run - /// use tokio::prelude::Future; - /// - /// let task = tokio::fs::File::create("foo.txt") - /// .and_then(|mut file| file.poll_metadata()) - /// .map(|metadata| { - /// // metadata is of type Async::Ready - /// println!("{:?}", metadata); - /// }).map_err(|err| eprintln!("IO error: {:?}", err)); - /// - /// tokio::run(task); - /// ``` - pub fn poll_metadata(&mut self) -> Poll> { - crate::blocking_io(|| self.std().metadata()) - } - - /// Create a new `File` instance that shares the same underlying file handle - /// as the existing `File` instance. Reads, writes, and seeks will affect both - /// File instances simultaneously. - /// - /// # Examples - /// - /// ```no_run - /// use tokio::prelude::Future; - /// - /// let task = tokio::fs::File::create("foo.txt") - /// .and_then(|mut file| file.poll_try_clone()) - /// .map(|clone| { - /// // do something with the clone - /// # println!("{:?}", clone); - /// }).map_err(|err| eprintln!("IO error: {:?}", err)); - /// - /// tokio::run(task); - /// ``` - pub fn poll_try_clone(&mut self) -> Poll> { - crate::blocking_io(|| { - let std = self.std().try_clone()?; - Ok(File::from_std(std)) - }) - } - - /// Create a new `File` instance that shares the same underlying file handle - /// as the existing `File` instance. Reads, writes, and seeks will affect both - /// File instances simultaneously. - /// - /// # Examples - /// - /// ```no_run - /// use tokio::prelude::Future; - /// - /// let task = tokio::fs::File::create("foo.txt") - /// .and_then(|file| { - /// file.try_clone() - /// .map(|(file, clone)| { - /// // do something with the file and the clone - /// # println!("{:?} {:?}", file, clone); - /// }) - /// .map_err(|(file, err)| { - /// // you get the original file back if there's an error - /// # println!("{:?}", file); - /// err - /// }) - /// }) - /// .map_err(|err| eprintln!("IO error: {:?}", err)); - /// - /// tokio::run(task); - /// ``` - pub fn try_clone(self) -> CloneFuture { - CloneFuture::new(self) - } - - /// Changes the permissions on the underlying file. - /// - /// # Platform-specific behavior - /// - /// This function currently corresponds to the `fchmod` function on Unix and - /// the `SetFileInformationByHandle` function on Windows. Note that, this - /// [may change in the future][changes]. - /// - /// [changes]: https://doc.rust-lang.org/std/io/index.html#platform-specific-behavior - /// - /// # Errors - /// - /// This function will return an error if the user lacks permission change - /// attributes on the underlying file. It may also return an error in other - /// os-specific unspecified cases. - /// - /// # Examples - /// - /// ```no_run - /// use tokio::prelude::Future; - /// - /// let task = tokio::fs::File::create("foo.txt") - /// .and_then(|file| file.metadata()) - /// .map(|(mut file, metadata)| { - /// let mut perms = metadata.permissions(); - /// perms.set_readonly(true); - /// match file.poll_set_permissions(perms) { - /// Err(e) => eprintln!("{}", e), - /// _ => println!("permissions set!"), - /// } - /// }).map_err(|err| eprintln!("IO error: {:?}", err)); - /// - /// tokio::run(task); - /// ``` - pub fn poll_set_permissions(&mut self, perm: Permissions) -> Poll> { - crate::blocking_io(|| self.std().set_permissions(perm)) - } - - /// Destructures the `tokio_fs::File` into a [`std::fs::File`][std]. - /// - /// # Panics - /// - /// This function will panic if `shutdown` has been called. - /// - /// [std]: https://doc.rust-lang.org/std/fs/struct.File.html - /// - /// # Examples - /// - /// ```no_run - /// use tokio::prelude::Future; - /// - /// let task = tokio::fs::File::create("foo.txt") - /// .map(|file| { - /// let std_file = file.into_std(); - /// // do something with the std::fs::File - /// # println!("{:?}", std_file); - /// }).map_err(|err| eprintln!("IO error: {:?}", err)); - /// - /// tokio::run(task); - /// ``` - pub fn into_std(mut self) -> StdFile { - self.std.take().expect("`File` instance already shutdown") - } - - fn std(&mut self) -> &mut StdFile { - self.std.as_mut().expect("`File` instance already shutdown") - } -} - -impl Read for File { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - crate::would_block(|| self.std().read(buf)) - } -} - -impl AsyncRead for File { - fn poll_read( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - match Pin::get_mut(self).read(buf) { - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Poll::Pending, - other => Poll::Ready(other), - } - } -} - -impl Write for File { - fn write(&mut self, buf: &[u8]) -> io::Result { - crate::would_block(|| self.std().write(buf)) - } - - fn flush(&mut self) -> io::Result<()> { - crate::would_block(|| self.std().flush()) - } -} - -impl AsyncWrite for File { - fn poll_write( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - match Pin::get_mut(self).write(buf) { - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Poll::Pending, - other => Poll::Ready(other), - } - } - - fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - match Pin::get_mut(self).flush() { - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Poll::Pending, - other => Poll::Ready(other), - } - } - - fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } -} - -impl Drop for File { - fn drop(&mut self) { - if let Some(_std) = self.std.take() { - // This is probably fine as closing a file *shouldn't* be a blocking - // operation. That said, ideally `shutdown` is called first. - } - } -} - -impl From for File { - fn from(std: StdFile) -> Self { - Self::from_std(std) - } -} - -impl TryFrom for StdFile { - type Error = io::Error; - - fn try_from(mut file: File) -> Result { - file.std - .take() - .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "`File` instance already shutdown")) - } -} diff --git a/tokio-fs/src/file/open.rs b/tokio-fs/src/file/open.rs deleted file mode 100644 index d8fa36ccb79..00000000000 --- a/tokio-fs/src/file/open.rs +++ /dev/null @@ -1,41 +0,0 @@ -use super::File; - -use futures_core::ready; -use std::fs::OpenOptions as StdOpenOptions; -use std::future::Future; -use std::io; -use std::path::Path; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; - -/// Future returned by `File::open` and resolves to a `File` instance. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct OpenFuture { - options: StdOpenOptions, - path: P, -} - -impl

OpenFuture

-where - P: AsRef + Send + Unpin + 'static, -{ - pub(crate) fn new(options: StdOpenOptions, path: P) -> Self { - OpenFuture { options, path } - } -} - -impl

Future for OpenFuture

-where - P: AsRef + Send + Unpin + 'static, -{ - type Output = io::Result; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - let std = ready!(crate::blocking_io(|| self.options.open(&self.path)))?; - - let file = File::from_std(std); - Poll::Ready(Ok(file)) - } -} diff --git a/tokio-fs/src/file/seek.rs b/tokio-fs/src/file/seek.rs deleted file mode 100644 index cf99cc373f0..00000000000 --- a/tokio-fs/src/file/seek.rs +++ /dev/null @@ -1,40 +0,0 @@ -use super::File; - -use futures_core::ready; -use std::future::Future; -use std::io; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; - -/// Future returned by `File::seek`. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct SeekFuture { - inner: Option, - pos: io::SeekFrom, -} - -impl SeekFuture { - pub(crate) fn new(file: File, pos: io::SeekFrom) -> Self { - Self { - pos, - inner: Some(file), - } - } -} - -impl Future for SeekFuture { - type Output = io::Result<(File, u64)>; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - let inner_self = Pin::get_mut(self); - let pos = ready!(inner_self - .inner - .as_mut() - .expect("Cannot poll `SeekFuture` after it resolves") - .poll_seek(inner_self.pos))?; - let inner = inner_self.inner.take().unwrap(); - Poll::Ready(Ok((inner, pos))) - } -} diff --git a/tokio-fs/src/hard_link.rs b/tokio-fs/src/hard_link.rs index 39ee90b5936..3d1fe82a406 100644 --- a/tokio-fs/src/hard_link.rs +++ b/tokio-fs/src/hard_link.rs @@ -1,10 +1,7 @@ -use std::fs; -use std::future::Future; +use crate::asyncify; + use std::io; use std::path::Path; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; /// Creates a new hard link on the filesystem. /// @@ -14,40 +11,6 @@ use std::task::Poll; /// This is an async version of [`std::fs::hard_link`][std] /// /// [std]: https://doc.rust-lang.org/std/fs/fn.hard_link.html -pub fn hard_link, Q: AsRef>(src: P, dst: Q) -> HardLinkFuture { - HardLinkFuture::new(src, dst) -} - -/// Future returned by `hard_link`. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct HardLinkFuture -where - P: AsRef, - Q: AsRef, -{ - src: P, - dst: Q, -} - -impl HardLinkFuture -where - P: AsRef, - Q: AsRef, -{ - fn new(src: P, dst: Q) -> HardLinkFuture { - HardLinkFuture { src, dst } - } -} - -impl Future for HardLinkFuture -where - P: AsRef, - Q: AsRef, -{ - type Output = io::Result<()>; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - crate::blocking_io(|| fs::hard_link(&self.src, &self.dst)) - } +pub async fn hard_link, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + asyncify(|| std::fs::hard_link(&src, &dst)).await } diff --git a/tokio-fs/src/lib.rs b/tokio-fs/src/lib.rs index f4376853985..be936adf405 100644 --- a/tokio-fs/src/lib.rs +++ b/tokio-fs/src/lib.rs @@ -2,6 +2,7 @@ #![deny(missing_docs, missing_debug_implementations, rust_2018_idioms)] #![cfg_attr(test, deny(warnings))] #![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))] +#![feature(async_await)] //! Asynchronous file and standard stream adaptation. //! @@ -32,9 +33,10 @@ mod create_dir; mod create_dir_all; -pub mod file; +mod file; mod hard_link; mod metadata; +mod open_options; pub mod os; mod read; mod read_dir; @@ -50,28 +52,28 @@ mod stdout; mod symlink_metadata; mod write; -pub use crate::create_dir::{create_dir, CreateDirFuture}; -pub use crate::create_dir_all::{create_dir_all, CreateDirAllFuture}; +pub use crate::create_dir::create_dir; +pub use crate::create_dir_all::create_dir_all; pub use crate::file::File; -pub use crate::file::OpenOptions; -pub use crate::hard_link::{hard_link, HardLinkFuture}; -pub use crate::metadata::{metadata, MetadataFuture}; -pub use crate::read::{read, ReadFile}; -pub use crate::read_dir::{read_dir, DirEntry, ReadDir, ReadDirFuture}; -pub use crate::read_link::{read_link, ReadLinkFuture}; -pub use crate::remove_dir::{remove_dir, RemoveDirFuture}; -pub use crate::remove_dir_all::{remove_dir_all, RemoveDirAllFuture}; -pub use crate::remove_file::{remove_file, RemoveFileFuture}; -pub use crate::rename::{rename, RenameFuture}; -pub use crate::set_permissions::{set_permissions, SetPermissionsFuture}; +pub use crate::hard_link::hard_link; +pub use crate::metadata::metadata; +pub use crate::open_options::OpenOptions; +pub use crate::read::read; +pub use crate::read_dir::{read_dir, DirEntry, ReadDir}; +pub use crate::read_link::read_link; +pub use crate::remove_dir::remove_dir; +pub use crate::remove_dir_all::remove_dir_all; +pub use crate::remove_file::remove_file; +pub use crate::rename::rename; +pub use crate::set_permissions::set_permissions; pub use crate::stderr::{stderr, Stderr}; pub use crate::stdin::{stdin, Stdin}; pub use crate::stdout::{stdout, Stdout}; -pub use crate::symlink_metadata::{symlink_metadata, SymlinkMetadataFuture}; -pub use crate::write::{write, WriteFile}; +pub use crate::symlink_metadata::symlink_metadata; +pub use crate::write::write; use std::io; -use std::io::ErrorKind::{Other, WouldBlock}; +use std::io::ErrorKind::Other; use std::task::Poll; use std::task::Poll::*; @@ -86,19 +88,14 @@ where } } -fn would_block(f: F) -> io::Result +async fn asyncify(f: F) -> io::Result where F: FnOnce() -> io::Result, { - match tokio_threadpool::blocking(f) { - Ready(Ok(Ok(v))) => Ok(v), - Ready(Ok(Err(err))) => { - debug_assert_ne!(err.kind(), WouldBlock); - Err(err) - } - Ready(Err(_)) => Err(blocking_err()), - Pending => Err(blocking_err()), - } + use futures_util::future::poll_fn; + + let mut f = Some(f); + poll_fn(move |_| blocking_io(|| f.take().unwrap()())).await } fn blocking_err() -> io::Error { diff --git a/tokio-fs/src/metadata.rs b/tokio-fs/src/metadata.rs index 7d05f299c07..145c92e0c50 100644 --- a/tokio-fs/src/metadata.rs +++ b/tokio-fs/src/metadata.rs @@ -1,47 +1,13 @@ -use super::blocking_io; +use crate::asyncify; -use std::fs::{self, Metadata}; -use std::future::Future; +use std::fs::Metadata; use std::io; use std::path::Path; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; /// Queries the file system metadata for a path. -pub fn metadata

(path: P) -> MetadataFuture

+pub async fn metadata

(path: P) -> io::Result where P: AsRef + Send + 'static, { - MetadataFuture::new(path) -} - -/// Future returned by `metadata`. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct MetadataFuture

-where - P: AsRef + Send + 'static, -{ - path: P, -} - -impl

MetadataFuture

-where - P: AsRef + Send + 'static, -{ - pub(crate) fn new(path: P) -> Self { - Self { path } - } -} - -impl

Future for MetadataFuture

-where - P: AsRef + Send + 'static, -{ - type Output = io::Result; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - blocking_io(|| fs::metadata(&self.path)) - } + asyncify(|| std::fs::metadata(&path)).await } diff --git a/tokio-fs/src/file/open_options.rs b/tokio-fs/src/open_options.rs similarity index 84% rename from tokio-fs/src/file/open_options.rs rename to tokio-fs/src/open_options.rs index 1a4f63c51c8..ff979772d05 100644 --- a/tokio-fs/src/file/open_options.rs +++ b/tokio-fs/src/open_options.rs @@ -1,7 +1,7 @@ -use super::OpenFuture; +use super::File; -use std::convert::From; -use std::fs::OpenOptions as StdOpenOptions; +use futures_util::future::poll_fn; +use std::io; use std::path::Path; /// Options and flags which can be used to configure how a file is opened. @@ -14,7 +14,7 @@ use std::path::Path; /// /// [`std::fs::OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html #[derive(Clone, Debug)] -pub struct OpenOptions(StdOpenOptions); +pub struct OpenOptions(std::fs::OpenOptions); impl OpenOptions { /// Creates a blank new set of options ready for configuration. @@ -30,7 +30,7 @@ impl OpenOptions { /// let future = options.read(true).open("foo.txt"); /// ``` pub fn new() -> OpenOptions { - OpenOptions(StdOpenOptions::new()) + OpenOptions(std::fs::OpenOptions::new()) } /// See the underlying [`read`] call for details. @@ -89,16 +89,18 @@ impl OpenOptions { /// Tokio runtime or if the underlying [`open`] call results in an error. /// /// [`open`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.open - pub fn open

(&self, path: P) -> OpenFuture

+ #[allow(clippy::needless_lifetimes)] // false positive: https://github.com/rust-lang/rust-clippy/issues/3988 + pub async fn open

(&self, path: P) -> io::Result where P: AsRef + Send + Unpin + 'static, { - OpenFuture::new(self.0.clone(), path) + let std_file = poll_fn(|_| crate::blocking_io(|| self.0.open(&path))).await?; + Ok(File::from_std(std_file)) } } -impl From for OpenOptions { - fn from(options: StdOpenOptions) -> OpenOptions { +impl From for OpenOptions { + fn from(options: std::fs::OpenOptions) -> OpenOptions { OpenOptions(options) } } diff --git a/tokio-fs/src/os/mod.rs b/tokio-fs/src/os/mod.rs index ae57c484761..f4b8bfb617d 100644 --- a/tokio-fs/src/os/mod.rs +++ b/tokio-fs/src/os/mod.rs @@ -2,5 +2,6 @@ #[cfg(unix)] pub mod unix; + #[cfg(windows)] pub mod windows; diff --git a/tokio-fs/src/os/unix.rs b/tokio-fs/src/os/unix.rs deleted file mode 100644 index e8e222f278e..00000000000 --- a/tokio-fs/src/os/unix.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! Unix-specific extensions to primitives in the `tokio_fs` module. - -use std::future::Future; -use std::io; -use std::os::unix::fs; -use std::path::Path; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; - -/// Creates a new symbolic link on the filesystem. -/// -/// The `dst` path will be a symbolic link pointing to the `src` path. -/// -/// This is an async version of [`std::os::unix::fs::symlink`][std] -/// -/// [std]: https://doc.rust-lang.org/std/os/unix/fs/fn.symlink.html -pub fn symlink, Q: AsRef>(src: P, dst: Q) -> SymlinkFuture { - SymlinkFuture::new(src, dst) -} - -/// Future returned by `symlink`. -#[derive(Debug)] -pub struct SymlinkFuture -where - P: AsRef, - Q: AsRef, -{ - src: P, - dst: Q, -} - -impl SymlinkFuture -where - P: AsRef, - Q: AsRef, -{ - fn new(src: P, dst: Q) -> SymlinkFuture { - SymlinkFuture { src, dst } - } -} - -impl Future for SymlinkFuture -where - P: AsRef, - Q: AsRef, -{ - type Output = io::Result<()>; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - crate::blocking_io(|| fs::symlink(&self.src, &self.dst)) - } -} diff --git a/tokio-fs/src/os/unix/mod.rs b/tokio-fs/src/os/unix/mod.rs new file mode 100644 index 00000000000..c3bb562fef4 --- /dev/null +++ b/tokio-fs/src/os/unix/mod.rs @@ -0,0 +1,5 @@ +//! Unix-specific extensions to primitives in the `tokio_fs` module. + +mod symlink; + +pub use self::symlink::symlink; diff --git a/tokio-fs/src/os/unix/symlink.rs b/tokio-fs/src/os/unix/symlink.rs new file mode 100644 index 00000000000..bfc8a6cfd14 --- /dev/null +++ b/tokio-fs/src/os/unix/symlink.rs @@ -0,0 +1,15 @@ +use crate::asyncify; + +use std::io; +use std::path::Path; + +/// Creates a new symbolic link on the filesystem. +/// +/// The `dst` path will be a symbolic link pointing to the `src` path. +/// +/// This is an async version of [`std::os::unix::fs::symlink`][std] +/// +/// [std]: https://doc.rust-lang.org/std/os/unix/fs/fn.symlink.html +pub async fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + asyncify(|| std::os::unix::fs::symlink(&src, &dst)).await +} diff --git a/tokio-fs/src/os/windows/mod.rs b/tokio-fs/src/os/windows/mod.rs index eaeed043eb3..12ed8458a4b 100644 --- a/tokio-fs/src/os/windows/mod.rs +++ b/tokio-fs/src/os/windows/mod.rs @@ -3,5 +3,5 @@ mod symlink_dir; mod symlink_file; -pub use self::symlink_dir::{symlink_dir, SymlinkDirFuture}; -pub use self::symlink_file::{symlink_file, SymlinkFileFuture}; +pub use self::symlink_dir::symlink_dir; +pub use self::symlink_file::symlink_file; diff --git a/tokio-fs/src/os/windows/symlink_dir.rs b/tokio-fs/src/os/windows/symlink_dir.rs index a9181396f62..d5f72ad3da1 100644 --- a/tokio-fs/src/os/windows/symlink_dir.rs +++ b/tokio-fs/src/os/windows/symlink_dir.rs @@ -1,10 +1,7 @@ -use std::future::Future; +use crate::asyncify; + use std::io; -use std::os::windows::fs; use std::path::Path; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; /// Creates a new directory symlink on the filesystem. /// @@ -14,39 +11,6 @@ use std::task::Poll; /// This is an async version of [`std::os::windows::fs::symlink_dir`][std] /// /// [std]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html -pub fn symlink_dir, Q: AsRef>(src: P, dst: Q) -> SymlinkDirFuture { - SymlinkDirFuture::new(src, dst) -} - -/// Future returned by `symlink_dir`. -#[derive(Debug)] -pub struct SymlinkDirFuture -where - P: AsRef, - Q: AsRef, -{ - src: P, - dst: Q, -} - -impl SymlinkDirFuture -where - P: AsRef, - Q: AsRef, -{ - fn new(src: P, dst: Q) -> SymlinkDirFuture { - SymlinkDirFuture { src, dst } - } -} - -impl Future for SymlinkDirFuture -where - P: AsRef, - Q: AsRef, -{ - type Output = io::Result<()>; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - crate::blocking_io(|| fs::symlink_dir(&self.src, &self.dst)) - } +pub async fn symlink_dir, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + asyncify(|| std::os::windows::fs::symlink_dir(&src, &dst)).await } diff --git a/tokio-fs/src/os/windows/symlink_file.rs b/tokio-fs/src/os/windows/symlink_file.rs index 42d1563a5f2..7373355df75 100644 --- a/tokio-fs/src/os/windows/symlink_file.rs +++ b/tokio-fs/src/os/windows/symlink_file.rs @@ -1,10 +1,7 @@ -use std::future::Future; +use crate::asyncify; + use std::io; -use std::os::windows::fs; use std::path::Path; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; /// Creates a new file symbolic link on the filesystem. /// @@ -14,39 +11,6 @@ use std::task::Poll; /// This is an async version of [`std::os::windows::fs::symlink_file`][std] /// /// [std]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html -pub fn symlink_file, Q: AsRef>(src: P, dst: Q) -> SymlinkFileFuture { - SymlinkFileFuture::new(src, dst) -} - -/// Future returned by `symlink_file`. -#[derive(Debug)] -pub struct SymlinkFileFuture -where - P: AsRef, - Q: AsRef, -{ - src: P, - dst: Q, -} - -impl SymlinkFileFuture -where - P: AsRef, - Q: AsRef, -{ - fn new(src: P, dst: Q) -> SymlinkFileFuture { - SymlinkFileFuture { src, dst } - } -} - -impl Future for SymlinkFileFuture -where - P: AsRef, - Q: AsRef, -{ - type Output = io::Result<()>; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - crate::blocking_io(|| fs::symlink_file(&self.src, &self.dst)) - } +pub async fn symlink_file, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + asyncify(|| std::os::windows::fs::symlink_file(&src, &dst)).await } diff --git a/tokio-fs/src/read.rs b/tokio-fs/src/read.rs index 1baed103ba3..4b2cf05a79a 100644 --- a/tokio-fs/src/read.rs +++ b/tokio-fs/src/read.rs @@ -1,13 +1,8 @@ -use crate::{file, File}; +use crate::File; -use tokio_io::AsyncRead; +use tokio_io::AsyncReadExt; -use futures_core::ready; -use std::future::Future; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; -use std::{io, mem, path::Path}; +use std::{io, path::Path}; /// Creates a future which will open a file for reading and read the entire /// contents into a buffer and return said buffer. @@ -17,74 +12,24 @@ use std::{io, mem, path::Path}; /// # Examples /// /// ```no_run -/// use tokio::prelude::Future; +/// #![feature(async_await)] /// -/// let task = tokio::fs::read("foo.txt").map(|data| { -/// // do something with the contents of the file ... -/// println!("foo.txt contains {} bytes", data.len()); -/// }).map_err(|e| { -/// // handle errors -/// eprintln!("IO error: {:?}", e); -/// }); +/// use tokio::fs; /// -/// tokio::run(task); +/// # async fn dox() -> std::io::Result<()> { +/// let contents = fs::read("foo.txt").await?; +/// println!("foo.txt contains {} bytes", contents.len()); +/// # Ok(()) +/// # } /// ``` -pub fn read

(path: P) -> ReadFile

+pub async fn read

(path: P) -> io::Result> where P: AsRef + Send + Unpin + 'static, { - ReadFile { - state: State::Open(File::open(path)), - } -} - -/// A future used to open a file and read its entire contents into a buffer. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct ReadFile + Send + Unpin + 'static> { - state: State

, -} - -#[derive(Debug)] -enum State + Send + Unpin + 'static> { - Open(file::OpenFuture

), - Metadata(file::MetadataFuture), - Reading(Vec, usize, File), - Empty, -} - -impl + Send + Unpin + 'static> Future for ReadFile

{ - type Output = io::Result>; + let mut file = File::open(path).await?; + let metadata = file.metadata().await?; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let inner = Pin::get_mut(self); - match &mut inner.state { - State::Open(ref mut open_file) => { - let file = ready!(Pin::new(open_file).poll(cx))?; - let new_state = State::Metadata(file.metadata()); - mem::replace(&mut inner.state, new_state); - Pin::new(inner).poll(cx) - } - State::Metadata(read_metadata) => { - let (file, metadata) = ready!(Pin::new(read_metadata).poll(cx))?; - let buf = Vec::with_capacity(metadata.len() as usize + 1); - let new_state = State::Reading(buf, 0, file); - mem::replace(&mut inner.state, new_state); - Pin::new(inner).poll(cx) - } - State::Reading(buf, ref mut pos, file) => { - let n = ready!(Pin::new(file).poll_read_buf(cx, buf))?; - *pos += n; - if *pos >= buf.len() { - match mem::replace(&mut inner.state, State::Empty) { - State::Reading(buf, _, _) => Poll::Ready(Ok(buf)), - _ => panic!(), - } - } else { - Poll::Pending - } - } - State::Empty => panic!("poll a WriteFile after it's done"), - } - } + let mut contents = Vec::with_capacity(metadata.len() as usize + 1); + file.read_to_end(&mut contents).await?; + Ok(contents) } diff --git a/tokio-fs/src/read_dir.rs b/tokio-fs/src/read_dir.rs index cf93903e50d..33209e7524b 100644 --- a/tokio-fs/src/read_dir.rs +++ b/tokio-fs/src/read_dir.rs @@ -1,7 +1,8 @@ +use crate::{asyncify, blocking_io}; + use futures_core::stream::Stream; use std::ffi::OsString; -use std::fs::{self, DirEntry as StdDirEntry, FileType, Metadata, ReadDir as StdReadDir}; -use std::future::Future; +use std::fs::{DirEntry as StdDirEntry, FileType, Metadata}; use std::io; #[cfg(unix)] use std::os::unix::fs::DirEntryExt; @@ -15,41 +16,12 @@ use std::task::Poll; /// 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

(path: P) -> ReadDirFuture

-where - P: AsRef + Send + 'static, -{ - ReadDirFuture::new(path) -} - -/// Future returned by `read_dir`. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct ReadDirFuture

-where - P: AsRef + Send + 'static, -{ - path: P, -} - -impl

ReadDirFuture

-where - P: AsRef + Send + 'static, -{ - fn new(path: P) -> ReadDirFuture

{ - ReadDirFuture { path } - } -} - -impl

Future for ReadDirFuture

+pub async fn read_dir

(path: P) -> io::Result where P: AsRef + Send + 'static, { - type Output = io::Result; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - crate::blocking_io(|| Ok(ReadDir(fs::read_dir(&self.path)?))) - } + let std = asyncify(|| std::fs::read_dir(&path)).await?; + Ok(ReadDir(std)) } /// Stream of the entries in a directory. @@ -70,18 +42,19 @@ where /// [`Err`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err #[derive(Debug)] #[must_use = "streams do nothing unless polled"] -pub struct ReadDir(StdReadDir); +pub struct ReadDir(std::fs::ReadDir); impl Stream for ReadDir { type Item = io::Result; - fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - let inner = Pin::get_mut(self); - match crate::blocking_io(|| match inner.0.next() { + fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + let res = blocking_io(|| match self.0.next() { Some(Err(err)) => Err(err), Some(Ok(item)) => Ok(Some(Ok(DirEntry(item)))), None => Ok(None), - }) { + }); + + match res { Poll::Ready(Err(err)) => Poll::Ready(Some(Err(err))), Poll::Ready(Ok(v)) => Poll::Ready(v), Poll::Pending => Poll::Pending, @@ -119,15 +92,21 @@ impl DirEntry { /// /// # Examples /// - /// ``` - /// use futures::{Future, Stream}; + /// ```no_run + /// #![feature(async_await)] + /// + /// use tokio::fs; + /// use tokio::prelude::*; /// - /// let fut = tokio_fs::read_dir(".").flatten_stream().for_each(|dir| { - /// println!("{:?}", dir.path()); - /// Ok(()) - /// }).map_err(|err| { eprintln!("Error: {:?}", err); () }); + /// # async fn dox() -> std::io::Result<()> { + /// let mut entries = fs::read_dir(".").await?; /// - /// tokio::run(fut); + /// while let Some(res) = entries.next().await { + /// let entry = res?; + /// println!("{:?}", entry.path()); + /// } + /// # Ok(()) + /// # } /// ``` /// /// This prints output like: @@ -149,15 +128,20 @@ impl DirEntry { /// # Examples /// /// ``` - /// use futures::{Future, Stream}; + /// #![feature(async_await)] /// - /// 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); () }); + /// use tokio::fs; + /// use tokio::prelude::*; /// - /// tokio::run(fut); + /// # async fn dox() -> std::io::Result<()> { + /// let mut entries = fs::read_dir(".").await?; + /// + /// while let Some(res) = entries.next().await { + /// let entry = res?; + /// println!("{:?}", entry.file_name()); + /// } + /// # Ok(()) + /// # } /// ``` pub fn file_name(&self) -> OsString { self.0.file_name() @@ -177,21 +161,30 @@ impl DirEntry { /// # Examples /// /// ``` - /// use futures::{Future, Stream}; - /// use futures::future::poll_fn; - /// - /// 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); + /// #![feature(async_await)] + /// + /// use tokio::fs; + /// use tokio::prelude::*; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let mut entries = fs::read_dir(".").await?; + /// + /// while let Some(res) = entries.next().await { + /// let entry = res?; + /// + /// if let Ok(metadata) = entry.metadata().await { + /// // Now let's show our entry's permissions! + /// println!("{:?}: {:?}", entry.path(), metadata.permissions()); + /// } else { + /// println!("Couldn't get file type for {:?}", entry.path()); + /// } + /// } + /// # Ok(()) + /// # } /// ``` - pub fn poll_metadata(&self) -> Poll> { - crate::blocking_io(|| self.0.metadata()) + #[allow(clippy::needless_lifetimes)] // false positive: https://github.com/rust-lang/rust-clippy/issues/3988 + pub async fn metadata(&self) -> io::Result { + asyncify(|| self.0.metadata()).await } /// Return the file type for the file that this entry points at. @@ -208,22 +201,30 @@ impl DirEntry { /// # Examples /// /// ``` - /// use futures::{Future, Stream}; - /// use futures::future::poll_fn; + /// #![feature(async_await)] /// - /// 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); () }); + /// use tokio::fs; + /// use tokio::prelude::*; /// - /// tokio::run(fut); + /// # async fn dox() -> std::io::Result<()> { + /// let mut entries = fs::read_dir(".").await?; + /// + /// while let Some(res) = entries.next().await { + /// let entry = res?; + /// + /// if let Ok(file_type) = entry.file_type().await { + /// // Now let's show our entry's file type! + /// println!("{:?}: {:?}", entry.path(), file_type); + /// } else { + /// println!("Couldn't get file type for {:?}", entry.path()); + /// } + /// } + /// # Ok(()) + /// # } /// ``` - pub fn poll_file_type(&self) -> Poll> { - crate::blocking_io(|| self.0.file_type()) + #[allow(clippy::needless_lifetimes)] // false positive: https://github.com/rust-lang/rust-clippy/issues/3988 + pub async fn file_type(&self) -> io::Result { + asyncify(|| self.0.file_type()).await } } diff --git a/tokio-fs/src/read_link.rs b/tokio-fs/src/read_link.rs index 3898267f4d8..ac070abe0b8 100644 --- a/tokio-fs/src/read_link.rs +++ b/tokio-fs/src/read_link.rs @@ -1,46 +1,13 @@ -use std::fs; -use std::future::Future; +use crate::asyncify; + use std::io; use std::path::{Path, PathBuf}; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; /// Reads a symbolic link, returning the file that the link points to. /// /// This is an async version of [`std::fs::read_link`][std] /// /// [std]: https://doc.rust-lang.org/std/fs/fn.read_link.html -pub fn read_link>(path: P) -> ReadLinkFuture

{ - ReadLinkFuture::new(path) -} - -/// Future returned by `read_link`. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct ReadLinkFuture

-where - P: AsRef, -{ - path: P, -} - -impl

ReadLinkFuture

-where - P: AsRef, -{ - fn new(path: P) -> ReadLinkFuture

{ - ReadLinkFuture { path } - } -} - -impl

Future for ReadLinkFuture

-where - P: AsRef, -{ - type Output = io::Result; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - crate::blocking_io(|| fs::read_link(&self.path)) - } +pub async fn read_link>(path: P) -> io::Result { + asyncify(|| std::fs::read_link(&path)).await } diff --git a/tokio-fs/src/remove_dir.rs b/tokio-fs/src/remove_dir.rs index 0281ba918ac..0de7e33b645 100644 --- a/tokio-fs/src/remove_dir.rs +++ b/tokio-fs/src/remove_dir.rs @@ -1,46 +1,13 @@ -use std::fs; -use std::future::Future; +use crate::asyncify; + use std::io; use std::path::Path; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; /// Removes an existing, empty directory. /// /// This is an async version of [`std::fs::remove_dir`][std] /// /// [std]: https://doc.rust-lang.org/std/fs/fn.remove_dir.html -pub fn remove_dir>(path: P) -> RemoveDirFuture

{ - RemoveDirFuture::new(path) -} - -/// Future returned by `remove_dir`. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct RemoveDirFuture

-where - P: AsRef, -{ - path: P, -} - -impl

RemoveDirFuture

-where - P: AsRef, -{ - fn new(path: P) -> RemoveDirFuture

{ - RemoveDirFuture { path } - } -} - -impl

Future for RemoveDirFuture

-where - P: AsRef, -{ - type Output = io::Result<()>; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - crate::blocking_io(|| fs::remove_dir(&self.path)) - } +pub async fn remove_dir>(path: P) -> io::Result<()> { + asyncify(|| std::fs::remove_dir(&path)).await } diff --git a/tokio-fs/src/remove_dir_all.rs b/tokio-fs/src/remove_dir_all.rs index d2eab6491f2..bd50697e15c 100644 --- a/tokio-fs/src/remove_dir_all.rs +++ b/tokio-fs/src/remove_dir_all.rs @@ -1,46 +1,13 @@ -use std::fs; -use std::future::Future; +use crate::asyncify; + use std::io; use std::path::Path; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; /// Removes a directory at this path, after removing all its contents. Use carefully! /// /// This is an async version of [`std::fs::remove_dir_all`][std] /// /// [std]: https://doc.rust-lang.org/std/fs/fn.remove_dir_all.html -pub fn remove_dir_all>(path: P) -> RemoveDirAllFuture

{ - RemoveDirAllFuture::new(path) -} - -/// Future returned by `remove_dir_all`. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct RemoveDirAllFuture

-where - P: AsRef, -{ - path: P, -} - -impl

RemoveDirAllFuture

-where - P: AsRef, -{ - fn new(path: P) -> RemoveDirAllFuture

{ - RemoveDirAllFuture { path } - } -} - -impl

Future for RemoveDirAllFuture

-where - P: AsRef, -{ - type Output = io::Result<()>; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - crate::blocking_io(|| fs::remove_dir_all(&self.path)) - } +pub async fn remove_dir_all>(path: P) -> io::Result<()> { + asyncify(|| std::fs::remove_dir_all(&path)).await } diff --git a/tokio-fs/src/remove_file.rs b/tokio-fs/src/remove_file.rs index 9eaa792985e..b5c2216c50f 100644 --- a/tokio-fs/src/remove_file.rs +++ b/tokio-fs/src/remove_file.rs @@ -1,10 +1,7 @@ -use std::fs; -use std::future::Future; +use crate::asyncify; + use std::io; use std::path::Path; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; /// Removes a file from the filesystem. /// @@ -15,36 +12,6 @@ use std::task::Poll; /// This is an async version of [`std::fs::remove_file`][std] /// /// [std]: https://doc.rust-lang.org/std/fs/fn.remove_file.html -pub fn remove_file>(path: P) -> RemoveFileFuture

{ - RemoveFileFuture::new(path) -} - -/// Future returned by `remove_file`. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct RemoveFileFuture

-where - P: AsRef, -{ - path: P, -} - -impl

RemoveFileFuture

-where - P: AsRef, -{ - fn new(path: P) -> RemoveFileFuture

{ - RemoveFileFuture { path } - } -} - -impl

Future for RemoveFileFuture

-where - P: AsRef, -{ - type Output = io::Result<()>; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - crate::blocking_io(|| fs::remove_file(&self.path)) - } +pub async fn remove_file>(path: P) -> io::Result<()> { + asyncify(|| std::fs::remove_file(&path)).await } diff --git a/tokio-fs/src/rename.rs b/tokio-fs/src/rename.rs index e766a826de3..e0f138aab62 100644 --- a/tokio-fs/src/rename.rs +++ b/tokio-fs/src/rename.rs @@ -1,10 +1,7 @@ -use std::fs; -use std::future::Future; +use crate::asyncify; + use std::io; use std::path::Path; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; /// Rename a file or directory to a new name, replacing the original file if /// `to` already exists. @@ -14,40 +11,6 @@ use std::task::Poll; /// This is an async version of [`std::fs::rename`][std] /// /// [std]: https://doc.rust-lang.org/std/fs/fn.rename.html -pub fn rename, Q: AsRef>(from: P, to: Q) -> RenameFuture { - RenameFuture::new(from, to) -} - -/// Future returned by `rename`. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct RenameFuture -where - P: AsRef, - Q: AsRef, -{ - from: P, - to: Q, -} - -impl RenameFuture -where - P: AsRef, - Q: AsRef, -{ - fn new(from: P, to: Q) -> RenameFuture { - RenameFuture { from, to } - } -} - -impl Future for RenameFuture -where - P: AsRef, - Q: AsRef, -{ - type Output = io::Result<()>; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - crate::blocking_io(|| fs::rename(&self.from, &self.to)) - } +pub async fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> { + asyncify(|| std::fs::rename(&from, &to)).await } diff --git a/tokio-fs/src/set_permissions.rs b/tokio-fs/src/set_permissions.rs index fb8e97a3546..cd11d2d70ce 100644 --- a/tokio-fs/src/set_permissions.rs +++ b/tokio-fs/src/set_permissions.rs @@ -1,47 +1,14 @@ -use std::fs; -use std::future::Future; +use crate::asyncify; + +use std::fs::Permissions; use std::io; use std::path::Path; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; /// Changes the permissions found on a file or a directory. /// /// This is an async version of [`std::fs::set_permissions`][std] /// /// [std]: https://doc.rust-lang.org/std/fs/fn.set_permissions.html -pub fn set_permissions>(path: P, perm: fs::Permissions) -> SetPermissionsFuture

{ - SetPermissionsFuture::new(path, perm) -} - -/// Future returned by `set_permissions`. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct SetPermissionsFuture

-where - P: AsRef, -{ - path: P, - perm: fs::Permissions, -} - -impl

SetPermissionsFuture

-where - P: AsRef, -{ - fn new(path: P, perm: fs::Permissions) -> SetPermissionsFuture

{ - SetPermissionsFuture { path, perm } - } -} - -impl

Future for SetPermissionsFuture

-where - P: AsRef, -{ - type Output = io::Result<()>; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - crate::blocking_io(|| fs::set_permissions(&self.path, self.perm.clone())) - } +pub async fn set_permissions>(path: P, perm: Permissions) -> io::Result<()> { + asyncify(|| std::fs::set_permissions(&path, perm)).await } diff --git a/tokio-fs/src/stderr.rs b/tokio-fs/src/stderr.rs index e8654b28e48..d16132b6e7e 100644 --- a/tokio-fs/src/stderr.rs +++ b/tokio-fs/src/stderr.rs @@ -1,6 +1,8 @@ +use crate::blocking_io; + use tokio_io::AsyncWrite; -use std::io::{self, Stderr as StdStderr, Write}; +use std::io::{self, Write}; use std::pin::Pin; use std::task::Context; use std::task::Poll; @@ -16,7 +18,7 @@ use std::task::Poll; /// [`AsyncWrite`]: trait.AsyncWrite.html #[derive(Debug)] pub struct Stderr { - std: StdStderr, + std: std::io::Stderr, } /// Constructs a new handle to the standard error of the current process. @@ -28,33 +30,17 @@ pub fn stderr() -> Stderr { Stderr { std } } -impl Write for Stderr { - fn write(&mut self, buf: &[u8]) -> io::Result { - crate::would_block(|| self.std.write(buf)) - } - - fn flush(&mut self) -> io::Result<()> { - crate::would_block(|| self.std.flush()) - } -} - impl AsyncWrite for Stderr { fn poll_write( - self: Pin<&mut Self>, + mut self: Pin<&mut Self>, _cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - match Pin::get_mut(self).write(buf) { - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Poll::Pending, - other => Poll::Ready(other), - } + blocking_io(|| (&mut self.std).write(buf)) } - fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - match Pin::get_mut(self).flush() { - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Poll::Pending, - other => Poll::Ready(other), - } + fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + blocking_io(|| (&mut self.std).flush()) } fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { diff --git a/tokio-fs/src/stdin.rs b/tokio-fs/src/stdin.rs index 41ce7a2aafe..aa09bd9aa67 100644 --- a/tokio-fs/src/stdin.rs +++ b/tokio-fs/src/stdin.rs @@ -1,6 +1,8 @@ +use crate::blocking_io; + use tokio_io::AsyncRead; -use std::io::{self, Read, Stdin as StdStdin}; +use std::io::{self, Read}; use std::pin::Pin; use std::task::Context; use std::task::Poll; @@ -22,7 +24,7 @@ use std::task::Poll; /// [`AsyncRead`]: trait.AsyncRead.html #[derive(Debug)] pub struct Stdin { - std: StdStdin, + std: std::io::Stdin, } /// Constructs a new handle to the standard input of the current process. @@ -34,21 +36,12 @@ pub fn stdin() -> Stdin { Stdin { std } } -impl Read for Stdin { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - crate::would_block(|| self.std.read(buf)) - } -} - impl AsyncRead for Stdin { fn poll_read( - self: Pin<&mut Self>, + mut self: Pin<&mut Self>, _cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - match Pin::get_mut(self).read(buf) { - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Poll::Pending, - other => Poll::Ready(other), - } + blocking_io(|| (&mut self.std).read(buf)) } } diff --git a/tokio-fs/src/stdout.rs b/tokio-fs/src/stdout.rs index be9e0c69389..0c96e960f8a 100644 --- a/tokio-fs/src/stdout.rs +++ b/tokio-fs/src/stdout.rs @@ -1,6 +1,8 @@ +use crate::blocking_io; + use tokio_io::AsyncWrite; -use std::io::{self, Stdout as StdStdout, Write}; +use std::io::{self, Write}; use std::pin::Pin; use std::task::Context; use std::task::Poll; @@ -16,7 +18,7 @@ use std::task::Poll; /// [`AsyncWrite`]: trait.AsyncWrite.html #[derive(Debug)] pub struct Stdout { - std: StdStdout, + std: std::io::Stdout, } /// Constructs a new handle to the standard output of the current process. @@ -28,33 +30,17 @@ pub fn stdout() -> Stdout { Stdout { std } } -impl Write for Stdout { - fn write(&mut self, buf: &[u8]) -> io::Result { - crate::would_block(|| self.std.write(buf)) - } - - fn flush(&mut self) -> io::Result<()> { - crate::would_block(|| self.std.flush()) - } -} - impl AsyncWrite for Stdout { fn poll_write( - self: Pin<&mut Self>, + mut self: Pin<&mut Self>, _cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - match Pin::get_mut(self).write(buf) { - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Poll::Pending, - other => Poll::Ready(other), - } + blocking_io(|| (&mut self.std).write(buf)) } - fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - match Pin::get_mut(self).flush() { - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Poll::Pending, - other => Poll::Ready(other), - } + fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + blocking_io(|| (&mut self.std).flush()) } fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { diff --git a/tokio-fs/src/symlink_metadata.rs b/tokio-fs/src/symlink_metadata.rs index bd880417b31..73fd910f90b 100644 --- a/tokio-fs/src/symlink_metadata.rs +++ b/tokio-fs/src/symlink_metadata.rs @@ -1,51 +1,17 @@ -use super::blocking_io; +use super::asyncify; -use std::fs::{self, Metadata}; -use std::future::Future; +use std::fs::Metadata; use std::io; use std::path::Path; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; /// Queries the file system metadata for a path. /// /// This is an async version of [`std::fs::symlink_metadata`][std] /// /// [std]: https://doc.rust-lang.org/std/fs/fn.symlink_metadata.html -pub fn symlink_metadata

(path: P) -> SymlinkMetadataFuture

+pub async fn symlink_metadata

(path: P) -> io::Result where P: AsRef + Send + 'static, { - SymlinkMetadataFuture::new(path) -} - -/// Future returned by `symlink_metadata`. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct SymlinkMetadataFuture

-where - P: AsRef + Send + 'static, -{ - path: P, -} - -impl

SymlinkMetadataFuture

-where - P: AsRef + Send + 'static, -{ - pub(crate) fn new(path: P) -> Self { - Self { path } - } -} - -impl

Future for SymlinkMetadataFuture

-where - P: AsRef + Send + 'static, -{ - type Output = io::Result; - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - blocking_io(|| fs::symlink_metadata(&self.path)) - } + asyncify(|| std::fs::symlink_metadata(&path)).await } diff --git a/tokio-fs/src/write.rs b/tokio-fs/src/write.rs index 701d2d3b990..1db4a0a368e 100644 --- a/tokio-fs/src/write.rs +++ b/tokio-fs/src/write.rs @@ -1,13 +1,8 @@ -use crate::{file, File}; +use crate::File; -use tokio_io::AsyncWrite; +use tokio_io::AsyncWriteExt; -use futures_core::ready; -use std::future::Future; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; -use std::{fmt, io, mem, path::Path}; +use std::{io, path::Path}; /// Creates a future that will open a file for writing and write the entire /// contents of `contents` to it. @@ -17,89 +12,21 @@ use std::{fmt, io, mem, path::Path}; /// # Examples /// /// ```no_run -/// use tokio::prelude::Future; +/// #![feature(async_await)] /// -/// let buffer = b"Hello world!"; -/// let task = tokio::fs::write("foo.txt", buffer).map(|data| { -/// // `data` has now been written to foo.txt. The buffer is being -/// // returned so it can be used for other things. -/// println!("foo.txt now had {} bytes written to it", data.len()); -/// }).map_err(|e| { -/// // handle errors -/// eprintln!("IO error: {:?}", e); -/// }); +/// use tokio::fs; /// -/// tokio::run(task); +/// # async fn dox() -> std::io::Result<()> { +/// fs::write("foo.txt", b"Hello world!").await?; +/// # Ok(()) +/// # } /// ``` -pub fn write + Unpin>(path: P, contents: C) -> WriteFile +pub async fn write + Unpin>(path: P, contents: C) -> io::Result<()> where P: AsRef + Send + Unpin + 'static, { - WriteFile { - state: State::Create(File::create(path), Some(contents)), - } -} - -/// A future used to open a file for writing and write the entire contents -/// of some data to it. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct WriteFile + Send + Unpin + 'static, C: AsRef<[u8]> + Unpin> { - state: State, -} - -#[derive(Debug)] -enum State + Send + Unpin + 'static, C: AsRef<[u8]> + Unpin> { - Create(file::CreateFuture

, Option), - Writing { f: File, buf: C, pos: usize }, - Empty, -} - -fn zero_write() -> io::Error { - io::Error::new(io::ErrorKind::WriteZero, "zero-length write") -} - -impl + Send + Unpin + 'static, C: AsRef<[u8]> + Unpin + fmt::Debug> Future - for WriteFile -{ - type Output = io::Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let inner = Pin::get_mut(self); - match &mut inner.state { - State::Create(create_file, contents) => { - let file = ready!(Pin::new(create_file).poll(cx))?; - let contents = contents.take().unwrap(); - let new_state = State::Writing { - f: file, - buf: contents, - pos: 0, - }; - mem::replace(&mut inner.state, new_state); - // We just entered the Write state, need to poll it before returning. - return Pin::new(inner).poll(cx); - } - State::Empty => panic!("poll a WriteFile after it's done"), - _ => {} - } + let mut file = File::create(path).await?; + file.write_all(contents.as_ref()).await?; - match mem::replace(&mut inner.state, State::Empty) { - State::Writing { - mut f, - buf, - mut pos, - } => { - let buf_ref = buf.as_ref(); - while pos < buf_ref.len() { - let n = ready!(Pin::new(&mut f).poll_write(cx, &buf_ref[pos..]))?; - pos += n; - if n == 0 { - return Poll::Ready(Err(zero_write())); - } - } - Poll::Ready(Ok(buf)) - } - _ => panic!(), - } - } + Ok(()) } diff --git a/tokio-fs/tests/file.rs b/tokio-fs/tests/file.rs index e2e02865452..480053d4f38 100644 --- a/tokio-fs/tests/file.rs +++ b/tokio-fs/tests/file.rs @@ -1,7 +1,6 @@ #![deny(warnings, rust_2018_idioms)] #![feature(async_await)] -use futures_util::future::poll_fn; use rand::{distributions, thread_rng, Rng}; use std::fs; use std::io::SeekFrom; @@ -31,11 +30,11 @@ fn read_write() { let contents_2 = contents.clone(); pool::run(async move { - let file = File::create(file_path).await?; - let (mut file, metadata) = file.metadata().await?; + let mut file = File::create(file_path).await?; + let metadata = file.metadata().await?; assert!(metadata.is_file()); file.write(&contents).await?; - poll_fn(move |_cx| file.poll_sync_all()).await?; + file.sync_all().await?; Ok(()) }); @@ -109,19 +108,18 @@ fn seek() { let file_path = dir.path().join("seek.txt"); pool::run(async move { - let mut file = OpenOptions::new() - .create(true) - .read(true) - .write(true) - .open(file_path) - .await - .unwrap(); + let mut options = OpenOptions::new(); + + options.create(true).read(true).write(true); + + let mut file = options.open(file_path).await.unwrap(); + assert!(file.write(b"Hello, world!").await.is_ok()); - let mut file = file.seek(SeekFrom::End(-6)).await.unwrap().0; + file.seek(SeekFrom::End(-6)).await.unwrap(); let mut buf = vec![0; 5]; assert!(file.read(buf.as_mut()).await.is_ok()); assert_eq!(buf, b"world"); - let mut file = file.seek(SeekFrom::Start(0)).await.unwrap().0; + file.seek(SeekFrom::Start(0)).await.unwrap(); let mut buf = vec![0; 5]; assert!(file.read(buf.as_mut()).await.is_ok()); assert_eq!(buf, b"Hello"); @@ -141,8 +139,8 @@ fn clone() { let file_path_2 = file_path.clone(); pool::run(async move { - let file = File::create(file_path.clone()).await.unwrap(); - let (mut file, mut clone) = file.try_clone().await.unwrap(); + let mut file = File::create(file_path.clone()).await.unwrap(); + let mut clone = file.try_clone().await.unwrap(); assert!(AsyncWriteExt::write(&mut file, b"clone ").await.is_ok()); assert!(AsyncWriteExt::write(&mut clone, b"successful") .await diff --git a/tokio/src/fs.rs b/tokio/src/fs.rs index 4831b93e2c5..75f493aae0e 100644 --- a/tokio/src/fs.rs +++ b/tokio/src/fs.rs @@ -7,11 +7,8 @@ //! the context of the Tokio runtime as they require Tokio specific features to //! function. -pub use tokio_fs::OpenOptions; pub use tokio_fs::{ - create_dir, create_dir_all, file, hard_link, metadata, os, read_dir, read_link, -}; -pub use tokio_fs::{read, write, ReadFile, WriteFile}; -pub use tokio_fs::{ - remove_dir, remove_dir_all, remove_file, rename, set_permissions, symlink_metadata, File, + create_dir, create_dir_all, hard_link, metadata, os, read, read_dir, read_link, remove_dir, + remove_dir_all, remove_file, rename, set_permissions, symlink_metadata, write, File, + OpenOptions, };