mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-07 00:00:09 +02:00
fs: use async fn instead of custom futures (#1381)
Also update all the doc examples.
This commit is contained in:
+8
-7
@@ -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"
|
||||
|
||||
@@ -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<P: AsRef<Path>>(path: P) -> CreateDirFuture<P> {
|
||||
CreateDirFuture::new(path)
|
||||
}
|
||||
|
||||
/// Future returned by `create_dir`.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct CreateDirFuture<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> CreateDirFuture<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
fn new(path: P) -> CreateDirFuture<P> {
|
||||
CreateDirFuture { path }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for CreateDirFuture<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
type Output = io::Result<()>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
crate::blocking_io(|| fs::create_dir(&self.path))
|
||||
}
|
||||
pub async fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
||||
asyncify(|| std::fs::create_dir(&path)).await
|
||||
}
|
||||
|
||||
@@ -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<P: AsRef<Path>>(path: P) -> CreateDirAllFuture<P> {
|
||||
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<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> CreateDirAllFuture<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
fn new(path: P) -> CreateDirAllFuture<P> {
|
||||
CreateDirAllFuture { path }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for CreateDirAllFuture<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
type Output = io::Result<()>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
crate::blocking_io(|| fs::create_dir_all(&self.path))
|
||||
}
|
||||
pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
||||
asyncify(|| std::fs::create_dir_all(&path)).await
|
||||
}
|
||||
|
||||
@@ -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<P>(path: P) -> io::Result<File>
|
||||
where
|
||||
P: AsRef<Path> + 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<P>(path: P) -> io::Result<File>
|
||||
where
|
||||
P: AsRef<Path> + 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<u64> {
|
||||
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<Metadata> {
|
||||
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<File> {
|
||||
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<io::Result<usize>> {
|
||||
blocking_io(|| (&self.std).read(buf))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for File {
|
||||
fn poll_write(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
blocking_io(|| (&self.std).write(buf))
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||
blocking_io(|| (&self.std).flush())
|
||||
}
|
||||
|
||||
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::fs::File> for File {
|
||||
fn from(std: std::fs::File) -> Self {
|
||||
Self::from_std(std)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<File> for std::fs::File {
|
||||
type Error = io::Error;
|
||||
|
||||
fn try_from(file: File) -> Result<Self, Self::Error> {
|
||||
Ok(file.std)
|
||||
}
|
||||
}
|
||||
@@ -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<File>,
|
||||
}
|
||||
|
||||
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<Self::Output> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
@@ -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<P> {
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> CreateFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + Unpin + 'static,
|
||||
{
|
||||
pub(crate) fn new(path: P) -> Self {
|
||||
CreateFuture { path }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for CreateFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + Unpin + 'static,
|
||||
{
|
||||
type Output = io::Result<File>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let std = ready!(crate::blocking_io(|| StdFile::create(&self.path)))?;
|
||||
|
||||
let file = File::from_std(std);
|
||||
Poll::Ready(Ok(file))
|
||||
}
|
||||
}
|
||||
@@ -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<File>,
|
||||
}
|
||||
|
||||
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<Self::Output> {
|
||||
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)))
|
||||
}
|
||||
}
|
||||
@@ -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<StdFile>,
|
||||
}
|
||||
|
||||
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<P>(path: P) -> OpenFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + 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<P>(path: P) -> CreateFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + 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<io::Result<u64>> {
|
||||
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<io::Result<()>> {
|
||||
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<io::Result<()>> {
|
||||
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<io::Result<()>> {
|
||||
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<Metadata>
|
||||
/// println!("{:?}", metadata);
|
||||
/// }).map_err(|err| eprintln!("IO error: {:?}", err));
|
||||
///
|
||||
/// tokio::run(task);
|
||||
/// ```
|
||||
pub fn poll_metadata(&mut self) -> Poll<io::Result<Metadata>> {
|
||||
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<io::Result<File>> {
|
||||
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<io::Result<()>> {
|
||||
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<usize> {
|
||||
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<io::Result<usize>> {
|
||||
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<usize> {
|
||||
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<io::Result<usize>> {
|
||||
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<Result<(), io::Error>> {
|
||||
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<Result<(), io::Error>> {
|
||||
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<StdFile> for File {
|
||||
fn from(std: StdFile) -> Self {
|
||||
Self::from_std(std)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<File> for StdFile {
|
||||
type Error = io::Error;
|
||||
|
||||
fn try_from(mut file: File) -> Result<Self, Self::Error> {
|
||||
file.std
|
||||
.take()
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "`File` instance already shutdown"))
|
||||
}
|
||||
}
|
||||
@@ -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<P: Unpin> {
|
||||
options: StdOpenOptions,
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> OpenFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + Unpin + 'static,
|
||||
{
|
||||
pub(crate) fn new(options: StdOpenOptions, path: P) -> Self {
|
||||
OpenFuture { options, path }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for OpenFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + Unpin + 'static,
|
||||
{
|
||||
type Output = io::Result<File>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let std = ready!(crate::blocking_io(|| self.options.open(&self.path)))?;
|
||||
|
||||
let file = File::from_std(std);
|
||||
Poll::Ready(Ok(file))
|
||||
}
|
||||
}
|
||||
@@ -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<File>,
|
||||
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<Self::Output> {
|
||||
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)))
|
||||
}
|
||||
}
|
||||
@@ -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<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> HardLinkFuture<P, Q> {
|
||||
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<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>,
|
||||
{
|
||||
src: P,
|
||||
dst: Q,
|
||||
}
|
||||
|
||||
impl<P, Q> HardLinkFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>,
|
||||
{
|
||||
fn new(src: P, dst: Q) -> HardLinkFuture<P, Q> {
|
||||
HardLinkFuture { src, dst }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, Q> Future for HardLinkFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>,
|
||||
{
|
||||
type Output = io::Result<()>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
crate::blocking_io(|| fs::hard_link(&self.src, &self.dst))
|
||||
}
|
||||
pub async fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
|
||||
asyncify(|| std::fs::hard_link(&src, &dst)).await
|
||||
}
|
||||
|
||||
+24
-27
@@ -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, T>(f: F) -> io::Result<T>
|
||||
async fn asyncify<F, T>(f: F) -> io::Result<T>
|
||||
where
|
||||
F: FnOnce() -> io::Result<T>,
|
||||
{
|
||||
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 {
|
||||
|
||||
@@ -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<P>(path: P) -> MetadataFuture<P>
|
||||
pub async fn metadata<P>(path: P) -> io::Result<Metadata>
|
||||
where
|
||||
P: AsRef<Path> + 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<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> MetadataFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
pub(crate) fn new(path: P) -> Self {
|
||||
Self { path }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for MetadataFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
type Output = io::Result<Metadata>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
blocking_io(|| fs::metadata(&self.path))
|
||||
}
|
||||
asyncify(|| std::fs::metadata(&path)).await
|
||||
}
|
||||
|
||||
@@ -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<P>(&self, path: P) -> OpenFuture<P>
|
||||
#[allow(clippy::needless_lifetimes)] // false positive: https://github.com/rust-lang/rust-clippy/issues/3988
|
||||
pub async fn open<P>(&self, path: P) -> io::Result<File>
|
||||
where
|
||||
P: AsRef<Path> + 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<StdOpenOptions> for OpenOptions {
|
||||
fn from(options: StdOpenOptions) -> OpenOptions {
|
||||
impl From<std::fs::OpenOptions> for OpenOptions {
|
||||
fn from(options: std::fs::OpenOptions) -> OpenOptions {
|
||||
OpenOptions(options)
|
||||
}
|
||||
}
|
||||
@@ -2,5 +2,6 @@
|
||||
|
||||
#[cfg(unix)]
|
||||
pub mod unix;
|
||||
|
||||
#[cfg(windows)]
|
||||
pub mod windows;
|
||||
|
||||
@@ -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<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> SymlinkFuture<P, Q> {
|
||||
SymlinkFuture::new(src, dst)
|
||||
}
|
||||
|
||||
/// Future returned by `symlink`.
|
||||
#[derive(Debug)]
|
||||
pub struct SymlinkFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>,
|
||||
{
|
||||
src: P,
|
||||
dst: Q,
|
||||
}
|
||||
|
||||
impl<P, Q> SymlinkFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>,
|
||||
{
|
||||
fn new(src: P, dst: Q) -> SymlinkFuture<P, Q> {
|
||||
SymlinkFuture { src, dst }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, Q> Future for SymlinkFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>,
|
||||
{
|
||||
type Output = io::Result<()>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
crate::blocking_io(|| fs::symlink(&self.src, &self.dst))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
//! Unix-specific extensions to primitives in the `tokio_fs` module.
|
||||
|
||||
mod symlink;
|
||||
|
||||
pub use self::symlink::symlink;
|
||||
@@ -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<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
|
||||
asyncify(|| std::os::unix::fs::symlink(&src, &dst)).await
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> SymlinkDirFuture<P, Q> {
|
||||
SymlinkDirFuture::new(src, dst)
|
||||
}
|
||||
|
||||
/// Future returned by `symlink_dir`.
|
||||
#[derive(Debug)]
|
||||
pub struct SymlinkDirFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>,
|
||||
{
|
||||
src: P,
|
||||
dst: Q,
|
||||
}
|
||||
|
||||
impl<P, Q> SymlinkDirFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>,
|
||||
{
|
||||
fn new(src: P, dst: Q) -> SymlinkDirFuture<P, Q> {
|
||||
SymlinkDirFuture { src, dst }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, Q> Future for SymlinkDirFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>,
|
||||
{
|
||||
type Output = io::Result<()>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
crate::blocking_io(|| fs::symlink_dir(&self.src, &self.dst))
|
||||
}
|
||||
pub async fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
|
||||
asyncify(|| std::os::windows::fs::symlink_dir(&src, &dst)).await
|
||||
}
|
||||
|
||||
@@ -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<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> SymlinkFileFuture<P, Q> {
|
||||
SymlinkFileFuture::new(src, dst)
|
||||
}
|
||||
|
||||
/// Future returned by `symlink_file`.
|
||||
#[derive(Debug)]
|
||||
pub struct SymlinkFileFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>,
|
||||
{
|
||||
src: P,
|
||||
dst: Q,
|
||||
}
|
||||
|
||||
impl<P, Q> SymlinkFileFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>,
|
||||
{
|
||||
fn new(src: P, dst: Q) -> SymlinkFileFuture<P, Q> {
|
||||
SymlinkFileFuture { src, dst }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, Q> Future for SymlinkFileFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>,
|
||||
{
|
||||
type Output = io::Result<()>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
crate::blocking_io(|| fs::symlink_file(&self.src, &self.dst))
|
||||
}
|
||||
pub async fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
|
||||
asyncify(|| std::os::windows::fs::symlink_file(&src, &dst)).await
|
||||
}
|
||||
|
||||
+16
-71
@@ -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<P>(path: P) -> ReadFile<P>
|
||||
pub async fn read<P>(path: P) -> io::Result<Vec<u8>>
|
||||
where
|
||||
P: AsRef<Path> + Send + Unpin + 'static,
|
||||
{
|
||||
ReadFile {
|
||||
state: State::Open(File::open(path)),
|
||||
}
|
||||
}
|
||||
let mut file = File::open(path).await?;
|
||||
let metadata = file.metadata().await?;
|
||||
|
||||
/// 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<P: AsRef<Path> + Send + Unpin + 'static> {
|
||||
state: State<P>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum State<P: AsRef<Path> + Send + Unpin + 'static> {
|
||||
Open(file::OpenFuture<P>),
|
||||
Metadata(file::MetadataFuture),
|
||||
Reading(Vec<u8>, usize, File),
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl<P: AsRef<Path> + Send + Unpin + 'static> Future for ReadFile<P> {
|
||||
type Output = io::Result<Vec<u8>>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
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)
|
||||
}
|
||||
|
||||
+79
-78
@@ -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<P>(path: P) -> ReadDirFuture<P>
|
||||
pub async fn read_dir<P>(path: P) -> io::Result<ReadDir>
|
||||
where
|
||||
P: AsRef<Path> + 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<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 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for ReadDirFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
type Output = io::Result<ReadDir>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
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<DirEntry>;
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
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<Option<Self::Item>> {
|
||||
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)]
|
||||
///
|
||||
/// let fut = tokio_fs::read_dir(".").flatten_stream().for_each(|dir| {
|
||||
/// println!("{:?}", dir.path());
|
||||
/// 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.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;
|
||||
/// #![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_metadata()).map(move |metadata| {
|
||||
/// println!("{:?}: {:?}", path, metadata.permissions());
|
||||
/// })
|
||||
/// }).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(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<io::Result<Metadata>> {
|
||||
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<Metadata> {
|
||||
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| {
|
||||
/// 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(file_type) = entry.file_type().await {
|
||||
/// // Now let's show our entry's file type!
|
||||
/// println!("{:?}: {:?}", path, file_type);
|
||||
/// })
|
||||
/// }).map_err(|err| { eprintln!("Error: {:?}", err); () });
|
||||
///
|
||||
/// tokio::run(fut);
|
||||
/// println!("{:?}: {:?}", entry.path(), file_type);
|
||||
/// } else {
|
||||
/// println!("Couldn't get file type for {:?}", entry.path());
|
||||
/// }
|
||||
/// }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn poll_file_type(&self) -> Poll<io::Result<FileType>> {
|
||||
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<FileType> {
|
||||
asyncify(|| self.0.file_type()).await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<P: AsRef<Path>>(path: P) -> ReadLinkFuture<P> {
|
||||
ReadLinkFuture::new(path)
|
||||
}
|
||||
|
||||
/// Future returned by `read_link`.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct ReadLinkFuture<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> ReadLinkFuture<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
fn new(path: P) -> ReadLinkFuture<P> {
|
||||
ReadLinkFuture { path }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for ReadLinkFuture<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
type Output = io::Result<PathBuf>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
crate::blocking_io(|| fs::read_link(&self.path))
|
||||
}
|
||||
pub async fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
|
||||
asyncify(|| std::fs::read_link(&path)).await
|
||||
}
|
||||
|
||||
@@ -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<P: AsRef<Path>>(path: P) -> RemoveDirFuture<P> {
|
||||
RemoveDirFuture::new(path)
|
||||
}
|
||||
|
||||
/// Future returned by `remove_dir`.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct RemoveDirFuture<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> RemoveDirFuture<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
fn new(path: P) -> RemoveDirFuture<P> {
|
||||
RemoveDirFuture { path }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for RemoveDirFuture<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
type Output = io::Result<()>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
crate::blocking_io(|| fs::remove_dir(&self.path))
|
||||
}
|
||||
pub async fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
||||
asyncify(|| std::fs::remove_dir(&path)).await
|
||||
}
|
||||
|
||||
@@ -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<P: AsRef<Path>>(path: P) -> RemoveDirAllFuture<P> {
|
||||
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<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> RemoveDirAllFuture<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
fn new(path: P) -> RemoveDirAllFuture<P> {
|
||||
RemoveDirAllFuture { path }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for RemoveDirAllFuture<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
type Output = io::Result<()>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
crate::blocking_io(|| fs::remove_dir_all(&self.path))
|
||||
}
|
||||
pub async fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
||||
asyncify(|| std::fs::remove_dir_all(&path)).await
|
||||
}
|
||||
|
||||
@@ -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<P: AsRef<Path>>(path: P) -> RemoveFileFuture<P> {
|
||||
RemoveFileFuture::new(path)
|
||||
}
|
||||
|
||||
/// Future returned by `remove_file`.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct RemoveFileFuture<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> RemoveFileFuture<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
fn new(path: P) -> RemoveFileFuture<P> {
|
||||
RemoveFileFuture { path }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for RemoveFileFuture<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
type Output = io::Result<()>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
crate::blocking_io(|| fs::remove_file(&self.path))
|
||||
}
|
||||
pub async fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
||||
asyncify(|| std::fs::remove_file(&path)).await
|
||||
}
|
||||
|
||||
+4
-41
@@ -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<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> RenameFuture<P, Q> {
|
||||
RenameFuture::new(from, to)
|
||||
}
|
||||
|
||||
/// Future returned by `rename`.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct RenameFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>,
|
||||
{
|
||||
from: P,
|
||||
to: Q,
|
||||
}
|
||||
|
||||
impl<P, Q> RenameFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>,
|
||||
{
|
||||
fn new(from: P, to: Q) -> RenameFuture<P, Q> {
|
||||
RenameFuture { from, to }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, Q> Future for RenameFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>,
|
||||
{
|
||||
type Output = io::Result<()>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
crate::blocking_io(|| fs::rename(&self.from, &self.to))
|
||||
}
|
||||
pub async fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
|
||||
asyncify(|| std::fs::rename(&from, &to)).await
|
||||
}
|
||||
|
||||
@@ -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<P: AsRef<Path>>(path: P, perm: fs::Permissions) -> SetPermissionsFuture<P> {
|
||||
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<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
path: P,
|
||||
perm: fs::Permissions,
|
||||
}
|
||||
|
||||
impl<P> SetPermissionsFuture<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
fn new(path: P, perm: fs::Permissions) -> SetPermissionsFuture<P> {
|
||||
SetPermissionsFuture { path, perm }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for SetPermissionsFuture<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
type Output = io::Result<()>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
crate::blocking_io(|| fs::set_permissions(&self.path, self.perm.clone()))
|
||||
}
|
||||
pub async fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> {
|
||||
asyncify(|| std::fs::set_permissions(&path, perm)).await
|
||||
}
|
||||
|
||||
+8
-22
@@ -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<usize> {
|
||||
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<io::Result<usize>> {
|
||||
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<Result<(), io::Error>> {
|
||||
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<Result<(), io::Error>> {
|
||||
blocking_io(|| (&mut self.std).flush())
|
||||
}
|
||||
|
||||
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||
|
||||
+6
-13
@@ -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<usize> {
|
||||
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<io::Result<usize>> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
+8
-22
@@ -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<usize> {
|
||||
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<io::Result<usize>> {
|
||||
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<Result<(), io::Error>> {
|
||||
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<Result<(), io::Error>> {
|
||||
blocking_io(|| (&mut self.std).flush())
|
||||
}
|
||||
|
||||
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||
|
||||
@@ -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<P>(path: P) -> SymlinkMetadataFuture<P>
|
||||
pub async fn symlink_metadata<P>(path: P) -> io::Result<Metadata>
|
||||
where
|
||||
P: AsRef<Path> + 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<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> SymlinkMetadataFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
pub(crate) fn new(path: P) -> Self {
|
||||
Self { path }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for SymlinkMetadataFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
type Output = io::Result<Metadata>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
blocking_io(|| fs::symlink_metadata(&self.path))
|
||||
}
|
||||
asyncify(|| std::fs::symlink_metadata(&path)).await
|
||||
}
|
||||
|
||||
+14
-87
@@ -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<P, C: AsRef<[u8]> + Unpin>(path: P, contents: C) -> WriteFile<P, C>
|
||||
pub async fn write<P, C: AsRef<[u8]> + Unpin>(path: P, contents: C) -> io::Result<()>
|
||||
where
|
||||
P: AsRef<Path> + 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<P: AsRef<Path> + Send + Unpin + 'static, C: AsRef<[u8]> + Unpin> {
|
||||
state: State<P, C>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum State<P: AsRef<Path> + Send + Unpin + 'static, C: AsRef<[u8]> + Unpin> {
|
||||
Create(file::CreateFuture<P>, Option<C>),
|
||||
Writing { f: File, buf: C, pos: usize },
|
||||
Empty,
|
||||
}
|
||||
|
||||
fn zero_write() -> io::Error {
|
||||
io::Error::new(io::ErrorKind::WriteZero, "zero-length write")
|
||||
}
|
||||
|
||||
impl<P: AsRef<Path> + Send + Unpin + 'static, C: AsRef<[u8]> + Unpin + fmt::Debug> Future
|
||||
for WriteFile<P, C>
|
||||
{
|
||||
type Output = io::Result<C>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
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"),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
let mut file = File::create(path).await?;
|
||||
file.write_all(contents.as_ref()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+13
-15
@@ -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
|
||||
|
||||
+3
-6
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user