diff --git a/Cargo.toml b/Cargo.toml index 2d026fa4..0229582e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,8 @@ name = "async-std" version = "0.99.5" authors = [ "Stjepan Glavina ", - "The async-std Project Developers", + "Yoshua Wuyts ", + "Contributors to async-std", ] edition = "2018" license = "Apache-2.0/MIT" diff --git a/README.md b/README.md index eb0160f4..6d6e0c16 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Async version of Rust's standard library +# Async version of the Rust standard library [![Build Status](https://travis-ci.com/async-rs/async-std.svg?branch=master)](https://travis-ci.com/async-rs/async-std) [![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](https://github.com/async-rs/async-std) diff --git a/examples/list-dir.rs b/examples/list-dir.rs index 814004aa..e4b62fc3 100644 --- a/examples/list-dir.rs +++ b/examples/list-dir.rs @@ -13,8 +13,9 @@ fn main() -> io::Result<()> { task::block_on(async { let mut dir = fs::read_dir(&path).await?; - while let Some(entry) = dir.next().await { - println!("{}", entry?.file_name().to_string_lossy()); + while let Some(res) = dir.next().await { + let entry = res?; + println!("{}", entry.file_name().to_string_lossy()); } Ok(()) diff --git a/src/fs/canonicalize.rs b/src/fs/canonicalize.rs index 572a6570..c484aeeb 100644 --- a/src/fs/canonicalize.rs +++ b/src/fs/canonicalize.rs @@ -1,4 +1,3 @@ -use std::fs; use std::path::{Path, PathBuf}; use crate::io; @@ -15,10 +14,11 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` does not exist. -/// * A non-final component in path is not a directory. +/// * `path` does not point to an existing file or directory. +/// * A non-final component in `path` is not a directory. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -33,5 +33,5 @@ use crate::task::blocking; /// ``` pub async fn canonicalize>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::canonicalize(path) }).await + blocking::spawn(async move { std::fs::canonicalize(path) }).await } diff --git a/src/fs/copy.rs b/src/fs/copy.rs index 56fd84be..fc3fd3e6 100644 --- a/src/fs/copy.rs +++ b/src/fs/copy.rs @@ -1,28 +1,32 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Copies the contents and permissions of one file to another. +/// Copies the contents and permissions of a file to a new location. /// -/// On success, the total number of bytes copied is returned and equals the length of the `from` -/// file. +/// On success, the total number of bytes copied is returned and equals the length of the `to` file +/// after this operation. /// /// The old contents of `to` will be overwritten. If `from` and `to` both point to the same file, -/// then the file will likely get truncated by this operation. +/// then the file will likely get truncated as a result of this operation. +/// +/// If you're working with open [`File`]s and want to copy contents through those types, use the +/// [`io::copy`] function. /// /// This function is an async version of [`std::fs::copy`]. /// +/// [`File`]: struct.File.html +/// [`io::copy`]: ../io/fn.copy.html /// [`std::fs::copy`]: https://doc.rust-lang.org/std/fs/fn.copy.html /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * The `from` path is not a file. -/// * The `from` file does not exist. -/// * The current process lacks permissions to access `from` or write `to`. +/// * `from` does not point to an existing file. +/// * The current process lacks permissions to read `from` or write `to`. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -31,12 +35,12 @@ use crate::task::blocking; /// # /// use async_std::fs; /// -/// let bytes_copied = fs::copy("a.txt", "b.txt").await?; +/// let num_bytes = fs::copy("a.txt", "b.txt").await?; /// # /// # Ok(()) }) } /// ``` pub async fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { let from = from.as_ref().to_owned(); let to = to.as_ref().to_owned(); - blocking::spawn(async move { fs::copy(&from, &to) }).await + blocking::spawn(async move { std::fs::copy(&from, &to) }).await } diff --git a/src/fs/create_dir.rs b/src/fs/create_dir.rs index 9532eaf1..aa2d5724 100644 --- a/src/fs/create_dir.rs +++ b/src/fs/create_dir.rs @@ -1,22 +1,26 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Creates a new, empty directory. +/// Creates a new directory. +/// +/// Note that this function will only create the final directory in `path`. If you want to create +/// all of its missing parent directories too, use the [`create_dir_all`] function instead. /// /// This function is an async version of [`std::fs::create_dir`]. /// +/// [`create_dir_all`]: fn.create_dir_all.html /// [`std::fs::create_dir`]: https://doc.rust-lang.org/std/fs/fn.create_dir.html /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` already exists. -/// * A parent of the given path does not exist. -/// * The current process lacks permissions to create directory at `path`. +/// * `path` already points to an existing file or directory. +/// * A parent directory in `path` does not exist. +/// * The current process lacks permissions to create the directory. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -25,11 +29,11 @@ use crate::task::blocking; /// # /// use async_std::fs; /// -/// fs::create_dir("./some/dir").await?; +/// fs::create_dir("./some/directory").await?; /// # /// # Ok(()) }) } /// ``` pub async fn create_dir>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::create_dir(path) }).await + blocking::spawn(async move { std::fs::create_dir(path) }).await } diff --git a/src/fs/create_dir_all.rs b/src/fs/create_dir_all.rs index b63c362f..33176f75 100644 --- a/src/fs/create_dir_all.rs +++ b/src/fs/create_dir_all.rs @@ -1,10 +1,9 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Creates a new, empty directory and all of its parents if they are missing. +/// Creates a new directory and all of its parents if they are missing. /// /// This function is an async version of [`std::fs::create_dir_all`]. /// @@ -12,10 +11,11 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * The parent directories do not exists and couldn't be created. -/// * The current process lacks permissions to create directory at `path`. +/// * `path` already points to an existing file or directory. +/// * The current process lacks permissions to create the directory or its missing parents. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -24,11 +24,11 @@ use crate::task::blocking; /// # /// use async_std::fs; /// -/// fs::create_dir_all("./some/dir").await?; +/// fs::create_dir_all("./some/directory").await?; /// # /// # Ok(()) }) } /// ``` pub async fn create_dir_all>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::create_dir_all(path) }).await + blocking::spawn(async move { std::fs::create_dir_all(path) }).await } diff --git a/src/fs/dir_builder.rs b/src/fs/dir_builder.rs index 21e2999a..0de230db 100644 --- a/src/fs/dir_builder.rs +++ b/src/fs/dir_builder.rs @@ -1,4 +1,3 @@ -use std::fs; use std::path::Path; use cfg_if::cfg_if; @@ -7,21 +6,28 @@ use crate::future::Future; use crate::io; use crate::task::blocking; -/// A builder for creating directories in various manners. +/// A builder for creating directories with configurable options. +/// +/// For Unix-specific options, import the [`os::unix::fs::DirBuilderExt`] trait. /// /// This type is an async version of [`std::fs::DirBuilder`]. /// +/// [`os::unix::fs::DirBuilderExt`]: ../os/unix/fs/trait.DirBuilderExt.html /// [`std::fs::DirBuilder`]: https://doc.rust-lang.org/std/fs/struct.DirBuilder.html #[derive(Debug)] pub struct DirBuilder { + /// Set to `true` if non-existent parent directories should be created. recursive: bool, + /// Unix mode for newly created directories. #[cfg(unix)] mode: Option, } impl DirBuilder { - /// Creates a new builder with [`recursive`] set to `false`. + /// Creates a blank set of options. + /// + /// The [`recursive`] option is initially set to `false`. /// /// [`recursive`]: #method.recursive /// @@ -33,25 +39,24 @@ impl DirBuilder { /// let builder = DirBuilder::new(); /// ``` pub fn new() -> DirBuilder { + #[cfg(not(unix))] + let builder = DirBuilder { recursive: false }; + #[cfg(unix)] let builder = DirBuilder { recursive: false, mode: None, }; - #[cfg(windows)] - let builder = DirBuilder { recursive: false }; - builder } /// Sets the option for recursive mode. /// - /// This option, when `true`, means that all parent directories should be created recursively - /// if they don't exist. Parents are created with the same security settings and permissions as - /// the final directory. + /// When set to `true`, this option means all parent directories should be created recursively + /// if they don't exist. Parents are created with the same permissions as the final directory. /// - /// This option defaults to `false`. + /// This option is initially set to `false`. /// /// # Examples /// @@ -70,6 +75,14 @@ impl DirBuilder { /// /// It is considered an error if the directory already exists unless recursive mode is enabled. /// + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * `path` already points to an existing file or directory. + /// * The current process lacks permissions to create the directory or its missing parents. + /// * Some other I/O error occurred. + /// /// # Examples /// /// ```no_run @@ -79,13 +92,13 @@ impl DirBuilder { /// /// DirBuilder::new() /// .recursive(true) - /// .create("/tmp/foo/bar/baz") + /// .create("./some/directory") /// .await?; /// # /// # Ok(()) }) } /// ``` pub fn create>(&self, path: P) -> impl Future> { - let mut builder = fs::DirBuilder::new(); + let mut builder = std::fs::DirBuilder::new(); builder.recursive(self.recursive); #[cfg(unix)] diff --git a/src/fs/dir_entry.rs b/src/fs/dir_entry.rs index c7928ebd..66b3cb7c 100644 --- a/src/fs/dir_entry.rs +++ b/src/fs/dir_entry.rs @@ -1,45 +1,28 @@ use std::ffi::OsString; -use std::fs; +use std::fmt; use std::path::PathBuf; use std::sync::Arc; use cfg_if::cfg_if; +use crate::fs::{FileType, Metadata}; use crate::io; use crate::task::blocking; -/// An entry inside a directory. +/// An entry in a directory. /// -/// An instance of `DirEntry` represents an entry inside a directory on the filesystem. Each entry -/// carriers additional information like the full path or metadata. +/// A stream of entries in a directory is returned by [`read_dir`]. /// /// This type is an async version of [`std::fs::DirEntry`]. /// +/// [`read_dir`]: fn.read_dir.html /// [`std::fs::DirEntry`]: https://doc.rust-lang.org/std/fs/struct.DirEntry.html -#[derive(Debug)] -pub struct DirEntry { - /// The inner synchronous `DirEntry`. - inner: Arc, - - #[cfg(unix)] - ino: u64, -} +pub struct DirEntry(Arc); impl DirEntry { - /// Creates an asynchronous `DirEntry` from a synchronous handle. - pub(crate) fn new(inner: fs::DirEntry) -> DirEntry { - #[cfg(unix)] - let dir_entry = DirEntry { - ino: inner.ino(), - inner: Arc::new(inner), - }; - - #[cfg(windows)] - let dir_entry = DirEntry { - inner: Arc::new(inner), - }; - - dir_entry + /// Creates an asynchronous `DirEntry` from a synchronous one. + pub(crate) fn new(inner: std::fs::DirEntry) -> DirEntry { + DirEntry(Arc::new(inner)) } /// Returns the full path to this entry. @@ -59,20 +42,33 @@ impl DirEntry { /// /// let mut dir = fs::read_dir(".").await?; /// - /// while let Some(entry) = dir.next().await { - /// let entry = entry?; + /// while let Some(res) = dir.next().await { + /// let entry = res?; /// println!("{:?}", entry.path()); /// } /// # /// # Ok(()) }) } /// ``` pub fn path(&self) -> PathBuf { - self.inner.path() + self.0.path() } - /// Returns the metadata for this entry. + /// Reads the metadata for this entry. + /// + /// This function will traverse symbolic links to read the metadata. + /// + /// If you want to read metadata without following symbolic links, use [`symlink_metadata`] + /// instead. + /// + /// [`symlink_metadata`]: fn.symlink_metadata.html + /// + /// # Errors + /// + /// An error will be returned in the following situations: /// - /// This function will not traverse symlinks if this entry points at a symlink. + /// * This entry does not point to an existing file or directory anymore. + /// * The current process lacks permissions to read the metadata. + /// * Some other I/O error occurred. /// /// # Examples /// @@ -84,21 +80,33 @@ impl DirEntry { /// /// let mut dir = fs::read_dir(".").await?; /// - /// while let Some(entry) = dir.next().await { - /// let entry = entry?; + /// while let Some(res) = dir.next().await { + /// let entry = res?; /// println!("{:?}", entry.metadata().await?); /// } /// # /// # Ok(()) }) } /// ``` - pub async fn metadata(&self) -> io::Result { - let inner = self.inner.clone(); + pub async fn metadata(&self) -> io::Result { + let inner = self.0.clone(); blocking::spawn(async move { inner.metadata() }).await } - /// Returns the file type for this entry. + /// Reads the file type for this entry. /// - /// This function will not traverse symlinks if this entry points at a symlink. + /// This function will not traverse symbolic links if this entry points at one. + /// + /// If you want to read metadata with following symbolic links, use [`metadata`] instead. + /// + /// [`metadata`]: #method.metadata + /// + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * This entry does not point to an existing file or directory anymore. + /// * The current process lacks permissions to read this entry's metadata. + /// * Some other I/O error occurred. /// /// # Examples /// @@ -110,15 +118,15 @@ impl DirEntry { /// /// let mut dir = fs::read_dir(".").await?; /// - /// while let Some(entry) = dir.next().await { - /// let entry = entry?; + /// while let Some(res) = dir.next().await { + /// let entry = res?; /// println!("{:?}", entry.file_type().await?); /// } /// # /// # Ok(()) }) } /// ``` - pub async fn file_type(&self) -> io::Result { - let inner = self.inner.clone(); + pub async fn file_type(&self) -> io::Result { + let inner = self.0.clone(); blocking::spawn(async move { inner.file_type() }).await } @@ -134,15 +142,21 @@ impl DirEntry { /// /// let mut dir = fs::read_dir(".").await?; /// - /// while let Some(entry) = dir.next().await { - /// let entry = entry?; - /// println!("{:?}", entry.file_name()); + /// while let Some(res) = dir.next().await { + /// let entry = res?; + /// println!("{}", entry.file_name().to_string_lossy()); /// } /// # /// # Ok(()) }) } /// ``` pub fn file_name(&self) -> OsString { - self.inner.file_name() + self.0.file_name() + } +} + +impl fmt::Debug for DirEntry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("DirEntry").field(&self.path()).finish() } } @@ -159,7 +173,7 @@ cfg_if! { if #[cfg(any(unix, feature = "docs"))] { impl DirEntryExt for DirEntry { fn ino(&self) -> u64 { - self.ino + self.0.ino() } } } diff --git a/src/fs/file.rs b/src/fs/file.rs index 3ee49796..33022bcb 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -1,9 +1,7 @@ -//! Async file implementation. - use std::cell::UnsafeCell; use std::cmp; -use std::fs; -use std::io::{Read as _, Seek, SeekFrom, Write as _}; +use std::fmt; +use std::io::{Read as _, Seek as _, Write as _}; use std::ops::{Deref, DerefMut}; use std::path::Path; use std::pin::Pin; @@ -13,22 +11,22 @@ use std::sync::{Arc, Mutex}; use cfg_if::cfg_if; use futures_io::{AsyncRead, AsyncSeek, AsyncWrite, Initializer}; +use crate::fs::{Metadata, Permissions}; use crate::future; -use crate::io::{self, Write}; +use crate::io::{self, SeekFrom, Write}; use crate::task::{self, blocking, Context, Poll, Waker}; -/// A reference to a file on the filesystem. +/// An open file on the filesystem. /// -/// An instance of a `File` can be read and/or written depending on what options it was opened -/// with. +/// Depending on what options the file was opened with, this type can be used for reading and/or +/// writing. /// -/// Files are automatically closed when they go out of scope. Errors detected on closing are -/// ignored by the implementation of `Drop`. Use the method [`sync_all`] if these errors must be -/// manually handled. +/// Files are automatically closed when they get dropped and any errors detected on closing are +/// ignored. Use the [`sync_all`] method before dropping a file if such errors need to be handled. /// /// This type is an async version of [`std::fs::File`]. /// -/// [`sync_all`]: struct.File.html#method.sync_all +/// [`sync_all`]: #method.sync_all /// [`std::fs::File`]: https://doc.rust-lang.org/std/fs/struct.File.html /// /// # Examples @@ -47,7 +45,7 @@ use crate::task::{self, blocking, Context, Poll, Waker}; /// # Ok(()) }) } /// ``` /// -/// Read the contents of a file into a `Vec`: +/// Read the contents of a file into a vector of bytes: /// /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { @@ -61,23 +59,30 @@ use crate::task::{self, blocking, Context, Poll, Waker}; /// # /// # Ok(()) }) } /// ``` -#[derive(Debug)] pub struct File { - file: Arc, + /// A reference to the inner file. + file: Arc, + + /// The state of the file protected by an async lock. lock: Lock, } impl File { /// Opens a file in read-only mode. /// - /// See the [`OpenOptions::open`] method for more details. + /// See the [`OpenOptions::open`] function for more options. /// /// # Errors /// - /// This function will return an error if `path` does not already exist. - /// Other errors may also be returned according to [`OpenOptions::open`]. + /// An error will be returned in the following situations: + /// + /// * `path` does not point to an existing file. + /// * The current process lacks permissions to read the file. + /// * Some other I/O error occurred. + /// + /// For more details, see the list of errors documented by [`OpenOptions::open`]. /// - /// [`OpenOptions::open`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html + /// [`OpenOptions::open`]: struct.OpenOptions.html#method.open /// /// # Examples /// @@ -92,7 +97,7 @@ impl File { /// ``` pub async fn open>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let file = blocking::spawn(async move { fs::File::open(&path) }).await?; + let file = blocking::spawn(async move { std::fs::File::open(&path) }).await?; Ok(file.into()) } @@ -100,9 +105,19 @@ impl File { /// /// This function will create a file if it does not exist, and will truncate it if it does. /// - /// See the [`OpenOptions::open`] function for more details. + /// See the [`OpenOptions::open`] function for more options. + /// + /// # Errors + /// + /// An error will be returned in the following situations: /// - /// [`OpenOptions::open`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html + /// * The file's parent directory does not exist. + /// * The current process lacks permissions to write to the file. + /// * Some other I/O error occurred. + /// + /// For more details, see the list of errors documented by [`OpenOptions::open`]. + /// + /// [`OpenOptions::open`]: struct.OpenOptions.html#method.open /// /// # Examples /// @@ -117,17 +132,16 @@ impl File { /// ``` pub async fn create>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let file = blocking::spawn(async move { fs::File::create(&path) }).await?; + let file = blocking::spawn(async move { std::fs::File::create(&path) }).await?; Ok(file.into()) } - /// Attempts to synchronize all OS-internal metadata to disk. + /// Synchronizes OS-internal buffered contents and metadata to disk. /// - /// This function will attempt to ensure that all in-memory data reaches the filesystem before - /// returning. + /// This function will ensure that all in-memory data reaches the filesystem. /// - /// This can be used to handle errors that would otherwise only be caught when the `File` is - /// closed. Dropping a file will ignore errors in synchronizing this in-memory data. + /// This can be used to handle errors that would otherwise only be caught when the file is + /// closed. When a file is dropped, errors in synchronizing this in-memory data are ignored. /// /// # Examples /// @@ -154,14 +168,16 @@ impl File { blocking::spawn(async move { state.file.sync_all() }).await } - /// Similar to [`sync_all`], except that it may not synchronize file metadata. + /// Synchronizes OS-internal buffered contents to disk. + /// + /// This is similar to [`sync_all`], except that file metadata may not be synchronized. /// - /// 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. + /// This is intended for use cases that must synchronize the contents of the file, but don't + /// need the file metadata synchronized to disk. /// /// Note that some platforms may simply implement this in terms of [`sync_all`]. /// - /// [`sync_all`]: struct.File.html#method.sync_all + /// [`sync_all`]: #method.sync_all /// /// # Examples /// @@ -188,18 +204,14 @@ impl File { blocking::spawn(async move { state.file.sync_data() }).await } - /// Truncates or extends the underlying file. + /// Truncates or extends the file. /// - /// If the `size` is less than the current file's size, then the file will be truncated. 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 zeros. - /// - /// The file's cursor isn't changed. In particular, if the cursor was at the end and the file - /// is truncated using this operation, the cursor will now be past the end. - /// - /// # Errors + /// If `size` is less than the current file size, then the file will be truncated. If it is + /// greater than the current file size, then the file will be extended to `size` and have all + /// intermediate data filled with zeros. /// - /// This function will return an error if the file is not opened for writing. + /// The file's cursor stays at the same position, even if the cursor ends up being past the end + /// of the file after this operation. /// /// # Examples /// @@ -225,7 +237,7 @@ impl File { blocking::spawn(async move { state.file.set_len(size) }).await } - /// Queries metadata about the file. + /// Reads the file's metadata. /// /// # Examples /// @@ -239,17 +251,19 @@ impl File { /// # /// # Ok(()) }) } /// ``` - pub async fn metadata(&self) -> io::Result { + pub async fn metadata(&self) -> io::Result { let file = self.file.clone(); blocking::spawn(async move { file.metadata() }).await } - /// Changes the permissions on the underlying file. + /// Changes the permissions on the file. /// /// # Errors /// - /// This function will return an error if the user lacks permission to change attributes on the - /// underlying file, but may also return an error in other OS-specific cases. + /// An error will be returned in the following situations: + /// + /// * The current process lacks permissions to change attributes on the file. + /// * Some other I/O error occurred. /// /// # Examples /// @@ -266,7 +280,7 @@ impl File { /// # /// # Ok(()) }) } /// ``` - pub async fn set_permissions(&self, perm: fs::Permissions) -> io::Result<()> { + pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> { let file = self.file.clone(); blocking::spawn(async move { file.set_permissions(perm) }).await } @@ -282,6 +296,12 @@ impl Drop for File { } } +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.file.fmt(f) + } +} + impl AsyncRead for File { fn poll_read( self: Pin<&mut Self>, @@ -374,9 +394,9 @@ impl AsyncSeek for &File { } impl From for File { - /// Converts a `std::fs::File` into its asynchronous equivalent. - fn from(file: fs::File) -> File { + fn from(file: std::fs::File) -> File { let file = Arc::new(file); + File { file: file.clone(), lock: Lock::new(State { @@ -413,7 +433,7 @@ cfg_if! { impl FromRawFd for File { unsafe fn from_raw_fd(fd: RawFd) -> File { - fs::File::from_raw_fd(fd).into() + std::fs::File::from_raw_fd(fd).into() } } @@ -422,7 +442,7 @@ cfg_if! { let file = self.file.clone(); drop(self); Arc::try_unwrap(file) - .expect("cannot acquire ownership of file handle after drop") + .expect("cannot acquire ownership of the file handle after drop") .into_raw_fd() } } @@ -440,7 +460,7 @@ cfg_if! { impl FromRawHandle for File { unsafe fn from_raw_handle(handle: RawHandle) -> File { - fs::File::from_raw_handle(handle).into() + std::fs::File::from_raw_handle(handle).into() } } @@ -449,7 +469,7 @@ cfg_if! { let file = self.file.clone(); drop(self); Arc::try_unwrap(file) - .expect("cannot acquire ownership of file handle after drop") + .expect("cannot acquire ownership of the file handle after drop") .into_raw_handle() } } @@ -457,13 +477,11 @@ cfg_if! { } /// An async mutex with non-borrowing lock guards. -#[derive(Debug)] struct Lock(Arc>); unsafe impl Send for Lock {} unsafe impl Sync for Lock {} -#[derive(Debug)] /// The state of a lock. struct LockState { /// Set to `true` when locked. @@ -472,12 +490,12 @@ struct LockState { /// The inner value. value: UnsafeCell, - /// A list of tasks interested in locking. + /// A list of tasks interested in acquiring the lock. wakers: Mutex>, } impl Lock { - /// Creates a new lock with the given value. + /// Creates a new lock initialized with `value`. fn new(value: T) -> Lock { Lock(Arc::new(LockState { locked: AtomicBool::new(false), @@ -511,14 +529,13 @@ impl Lock { /// A lock guard. /// /// When dropped, ownership of the inner value is returned back to the lock. -#[derive(Debug)] struct LockGuard(Arc>); unsafe impl Send for LockGuard {} unsafe impl Sync for LockGuard {} impl LockGuard { - /// Registers a task interested in locking. + /// Registers a task interested in acquiring the lock. /// /// When this lock guard gets dropped, all registered tasks will be woken up. fn register(&self, cx: &Context<'_>) { @@ -532,8 +549,10 @@ impl LockGuard { impl Drop for LockGuard { fn drop(&mut self) { + // Release the lock. self.0.locked.store(false, Ordering::Release); + // Wake up all registered tasks interested in acquiring the lock. for w in self.0.wakers.lock().unwrap().drain(..) { w.wake(); } @@ -557,14 +576,13 @@ impl DerefMut for LockGuard { /// Modes a file can be in. /// /// The file can either be in idle mode, reading mode, or writing mode. -#[derive(Debug)] enum Mode { /// The cache is empty. Idle, /// The cache contains data read from the inner file. /// - /// This `usize` represents how many bytes from the beginning of cache have been consumed. + /// The `usize` represents how many bytes from the beginning of cache have been consumed. Reading(usize), /// The cache contains data that needs to be written to the inner file. @@ -573,14 +591,13 @@ enum Mode { /// The current state of a file. /// -/// The `File` struct puts this state behind a lock. +/// The `File` struct protects this state behind a lock. /// -/// Filesystem operations that get spawned as blocking tasks will take ownership of the state and -/// return it back once the operation completes. -#[derive(Debug)] +/// Filesystem operations that get spawned as blocking tasks will acquire the lock, take ownership +/// of the state and return it back once the operation completes. struct State { /// The inner file. - file: Arc, + file: Arc, /// The current mode (idle, reading, or writing). mode: Mode, @@ -588,10 +605,10 @@ struct State { /// The read/write cache. /// /// If in reading mode, the cache contains a chunk of data that has been read from the file. - /// If in writing mode, the cache contains data that will eventually be written into the file. + /// If in writing mode, the cache contains data that will eventually be written to the file. cache: Vec, - /// `true` if the file is flushed. + /// Set to `true` if the file is flushed. /// /// When a file is flushed, the write cache and the inner file's buffer are empty. is_flushed: bool, @@ -607,17 +624,45 @@ impl LockGuard { /// Seeks to a new position in the file. fn poll_seek(mut self, cx: &mut Context<'_>, pos: SeekFrom) -> Poll> { // If this operation doesn't move the cursor, then poll the current position inside the - // file. This call will hopefully not block. + // file. This call should not block because it doesn't touch the actual file on disk. if pos == SeekFrom::Current(0) { - return Poll::Ready((&*self.file).seek(pos)); + // Poll the internal file cursor. + let internal = (&*self.file).seek(SeekFrom::Current(0))?; + + // Factor in the difference caused by caching. + let actual = match self.mode { + Mode::Idle => internal, + Mode::Reading(start) => internal - self.cache.len() as u64 + start as u64, + Mode::Writing => internal + self.cache.len() as u64, + }; + return Poll::Ready(Ok(actual)); + } + + // If the file is in reading mode and the cache will stay valid after seeking, then adjust + // the current position in the read cache without invaliding it. + if let Mode::Reading(start) = self.mode { + if let SeekFrom::Current(diff) = pos { + if let Some(new) = (start as i64).checked_add(diff) { + if 0 <= new && new <= self.cache.len() as i64 { + // Poll the internal file cursor. + let internal = (&*self.file).seek(SeekFrom::Current(0))?; + + // Adjust the current position in the read cache. + self.mode = Mode::Reading(new as usize); + + // Factor in the difference caused by caching. + return Poll::Ready(Ok(internal - self.cache.len() as u64 + new as u64)); + } + } + } } // Invalidate the read cache and flush the write cache before calling `seek()`. self = futures_core::ready!(self.poll_unread(cx))?; self = futures_core::ready!(self.poll_flush(cx))?; - // Seek to the new position. This call is hopefully not blocking because it should just - // change the internal offset into the file and not touch the actual file. + // Seek to the new position. This call should not block because it only changes the + // internal offset into the file and doesn't touch the actual file on disk. Poll::Ready((&*self.file).seek(pos)) } @@ -702,13 +747,12 @@ impl LockGuard { match self.mode { Mode::Idle | Mode::Writing => Poll::Ready(Ok(self)), Mode::Reading(start) => { - // Number of unconsumed bytes in the read cache. + // The number of unconsumed bytes in the read cache. let n = self.cache.len() - start; if n > 0 { - // Seek `n` bytes backwards. This call is hopefully not blocking because it - // should just change the internal offset into the file and not touch the - // actual file. + // Seek `n` bytes backwards. This call should not block because it only changes + // the internal offset into the file and doesn't touch the actual file on disk. (&*self.file).seek(SeekFrom::Current(-(n as i64)))?; } @@ -731,7 +775,7 @@ impl LockGuard { // If we're in reading mode, invalidate the read buffer. self = futures_core::ready!(self.poll_unread(cx))?; - // Make the cache have as much capacity as `buf`. + // If necessary, grow the cache to have as much capacity as `buf`. if self.cache.capacity() < buf.len() { let diff = buf.len() - self.cache.capacity(); self.cache.reserve(diff); @@ -740,7 +784,7 @@ impl LockGuard { // How many bytes can be written into the cache before filling up. let available = self.cache.capacity() - self.cache.len(); - // If there is available space in the cache or if the buffer is empty, we can write data + // If there is space available in the cache or if the buffer is empty, we can write data // into the cache. if available > 0 || buf.is_empty() { let n = cmp::min(available, buf.len()); diff --git a/src/fs/file_type.rs b/src/fs/file_type.rs new file mode 100644 index 00000000..a1627984 --- /dev/null +++ b/src/fs/file_type.rs @@ -0,0 +1,86 @@ +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(feature = "docs")] { + /// The type of a file or directory. + /// + /// A file type is returned by [`Metadata::file_type`]. + /// + /// Note that file types are mutually exclusive, i.e. at most one of methods [`is_dir`], + /// [`is_file`], and [`is_symlink`] can return `true`. + /// + /// This type is a re-export of [`std::fs::FileType`]. + /// + /// [`Metadata::file_type`]: struct.Metadata.html#method.file_type + /// [`is_dir`]: #method.is_dir + /// [`is_file`]: #method.is_file + /// [`is_symlink`]: #method.is_symlink + /// [`std::fs::FileType`]: https://doc.rust-lang.org/std/fs/struct.FileType.html + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub struct FileType { + _private: (), + } + + impl FileType { + /// Returns `true` if this file type represents a regular directory. + /// + /// If this file type represents a symbolic link, this method returns `false`. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let file_type = fs::metadata(".").await?.file_type(); + /// println!("{:?}", file_type.is_dir()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_dir(&self) -> bool { + unimplemented!() + } + + /// Returns `true` if this file type represents a regular file. + /// + /// If this file type represents a symbolic link, this method returns `false`. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let file_type = fs::metadata("a.txt").await?.file_type(); + /// println!("{:?}", file_type.is_file()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_file(&self) -> bool { + unimplemented!() + } + + /// Returns `true` if this file type represents a symbolic link. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let file_type = fs::metadata("a.txt").await?.file_type(); + /// println!("{:?}", file_type.is_symlink()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_symlink(&self) -> bool { + unimplemented!() + } + } + } else { + pub use std::fs::FileType; + } +} diff --git a/src/fs/hard_link.rs b/src/fs/hard_link.rs index 8427e8f9..5f950cde 100644 --- a/src/fs/hard_link.rs +++ b/src/fs/hard_link.rs @@ -1,13 +1,12 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Creates a new hard link on the filesystem. +/// Creates a hard link on the filesystem. /// -/// The `dst` path will be a link pointing to the `src` path. Note that systems often require these -/// two paths to both be located on the same filesystem. +/// The `dst` path will be a link pointing to the `src` path. Note that operating systems often +/// require these two paths to be located on the same filesystem. /// /// This function is an async version of [`std::fs::hard_link`]. /// @@ -15,9 +14,10 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * The `src` path is not a file or doesn't exist. +/// * `src` does not point to an existing file. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -33,5 +33,5 @@ use crate::task::blocking; pub async fn hard_link, Q: AsRef>(from: P, to: Q) -> io::Result<()> { let from = from.as_ref().to_owned(); let to = to.as_ref().to_owned(); - blocking::spawn(async move { fs::hard_link(&from, &to) }).await + blocking::spawn(async move { std::fs::hard_link(&from, &to) }).await } diff --git a/src/fs/metadata.rs b/src/fs/metadata.rs index 032fe24f..6bb99936 100644 --- a/src/fs/metadata.rs +++ b/src/fs/metadata.rs @@ -1,23 +1,28 @@ -use std::fs::{self, Metadata}; use std::path::Path; +use cfg_if::cfg_if; + use crate::io; use crate::task::blocking; -/// Queries the metadata for a path. +/// Reads metadata for a path. /// -/// This function will traverse symbolic links to query information about the file or directory. +/// This function will traverse symbolic links to read metadata for the target file or directory. +/// If you want to read metadata without following symbolic links, use [`symlink_metadata`] +/// instead. /// /// This function is an async version of [`std::fs::metadata`]. /// +/// [`symlink_metadata`]: fn.symlink_metadata.html /// [`std::fs::metadata`]: https://doc.rust-lang.org/std/fs/fn.metadata.html /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` does not exist. -/// * The current process lacks permissions to query metadata for `path`. +/// * `path` does not point to an existing file or directory. +/// * The current process lacks permissions to read metadata for the path. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -32,5 +37,196 @@ use crate::task::blocking; /// ``` pub async fn metadata>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::metadata(path) }).await + blocking::spawn(async move { std::fs::metadata(path) }).await +} + +cfg_if! { + if #[cfg(feature = "docs")] { + use std::time::SystemTime; + + use crate::fs::{FileType, Permissions}; + + /// Metadata for a file or directory. + /// + /// Metadata is returned by [`metadata`] and [`symlink_metadata`]. + /// + /// This type is a re-export of [`std::fs::Metadata`]. + /// + /// [`metadata`]: fn.metadata.html + /// [`symlink_metadata`]: fn.symlink_metadata.html + /// [`is_dir`]: #method.is_dir + /// [`is_file`]: #method.is_file + /// [`std::fs::Metadata`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html + #[derive(Clone, Debug)] + pub struct Metadata { + _private: (), + } + + impl Metadata { + /// Returns the file type from this metadata. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.file_type()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn file_type(&self) -> FileType { + unimplemented!() + } + + /// Returns `true` if this metadata is for a regular directory. + /// + /// If this metadata is for a symbolic link, this method returns `false`. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use std::fs; + /// + /// let metadata = fs::metadata(".").await?; + /// println!("{:?}", metadata.is_dir()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_dir(&self) -> bool { + unimplemented!() + } + + /// Returns `true` if this metadata is for a regular file. + /// + /// If this metadata is for a symbolic link, this method returns `false`. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.is_file()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_file(&self) -> bool { + unimplemented!() + } + + /// Returns the file size in bytes. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{}", metadata.len()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn len(&self) -> u64 { + unimplemented!() + } + + /// Returns the permissions from this metadata. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.permissions()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn permissions(&self) -> Permissions { + unimplemented!() + } + + /// Returns the last modification time. + /// + /// # Errors + /// + /// This data may not be available on all platforms, in which case an error will be + /// returned. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.modified()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn modified(&self) -> io::Result { + unimplemented!() + } + + /// Returns the last access time. + /// + /// # Errors + /// + /// This data may not be available on all platforms, in which case an error will be + /// returned. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.accessed()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn accessed(&self) -> io::Result { + unimplemented!() + } + + /// Returns the creation time. + /// + /// # Errors + /// + /// This data may not be available on all platforms, in which case an error will be + /// returned. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.created()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn created(&self) -> io::Result { + unimplemented!() + } + } + } else { + pub use std::fs::Metadata; + } } diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 8d983302..5612bb82 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -2,8 +2,13 @@ //! //! This module is an async version of [`std::fs`]. //! +//! [`os::unix::fs`]: ../os/unix/fs/index.html //! [`std::fs`]: https://doc.rust-lang.org/std/fs/index.html //! +//! # Platform-specific extensions +//! +//! * Unix: use the [`os::unix::fs`] module. +//! //! # Examples //! //! Create a new file and write some bytes to it: @@ -23,12 +28,12 @@ pub use dir_builder::DirBuilder; pub use dir_entry::DirEntry; pub use file::File; +pub use file_type::FileType; pub use open_options::OpenOptions; +pub use metadata::Metadata; +pub use permissions::Permissions; pub use read_dir::ReadDir; -#[doc(inline)] -pub use std::fs::{FileType, Metadata, Permissions}; - pub use canonicalize::canonicalize; pub use copy::copy; pub use create_dir::create_dir; @@ -54,9 +59,11 @@ mod create_dir_all; mod dir_builder; mod dir_entry; mod file; +mod file_type; mod hard_link; mod metadata; mod open_options; +mod permissions; mod read; mod read_dir; mod read_link; diff --git a/src/fs/open_options.rs b/src/fs/open_options.rs index 54a6f760..c6cc74a0 100644 --- a/src/fs/open_options.rs +++ b/src/fs/open_options.rs @@ -1,35 +1,35 @@ -use std::fs; -use std::io; use std::path::Path; use cfg_if::cfg_if; -use super::File; +use crate::fs::File; use crate::future::Future; +use crate::io; use crate::task::blocking; -/// Options and flags which for configuring how a file is opened. +/// A builder for opening files with configurable options. /// -/// This builder exposes the ability to configure how a [`File`] is opened and what operations are -/// permitted on the open file. The [`File::open`] and [`File::create`] methods are aliases for -/// commonly used options with this builder. +/// Files can be opened in [`read`] and/or [`write`] mode. /// -/// Generally speaking, when using `OpenOptions`, you'll first call [`new`], then chain calls to -/// methods to set each option, then call [`open`], passing the path of the file you're trying to -/// open. This will give you a [`File`] inside that you can further operate on. +/// The [`append`] option opens files in a special writing mode that moves the file cursor to the +/// end of file before every write operation. +/// +/// It is also possible to [`truncate`] the file right after opening, to [`create`] a file if it +/// doesn't exist yet, or to always create a new file with [`create_new`]. /// /// This type is an async version of [`std::fs::OpenOptions`]. /// -/// [`new`]: struct.OpenOptions.html#method.new -/// [`open`]: struct.OpenOptions.html#method.open -/// [`File`]: struct.File.html -/// [`File::open`]: struct.File.html#method.open -/// [`File::create`]: struct.File.html#method.create +/// [`read`]: #method.read +/// [`write`]: #method.write +/// [`append`]: #method.append +/// [`truncate`]: #method.truncate +/// [`create`]: #method.create +/// [`create_new`]: #method.create_new /// [`std::fs::OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html /// /// # Examples /// -/// Opening a file for reading: +/// Open a file for reading: /// /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { @@ -44,7 +44,7 @@ use crate::task::blocking; /// # Ok(()) }) } /// ``` /// -/// Opening a file for both reading and writing, creating it if it doesn't exist: +/// Open a file for both reading and writing, and create it if it doesn't exist yet: /// /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { @@ -61,10 +61,10 @@ use crate::task::blocking; /// # Ok(()) }) } /// ``` #[derive(Clone, Debug)] -pub struct OpenOptions(fs::OpenOptions); +pub struct OpenOptions(std::fs::OpenOptions); impl OpenOptions { - /// Creates a blank new set of options. + /// Creates a blank set of options. /// /// All options are initially set to `false`. /// @@ -83,12 +83,12 @@ impl OpenOptions { /// # Ok(()) }) } /// ``` pub fn new() -> OpenOptions { - OpenOptions(fs::OpenOptions::new()) + OpenOptions(std::fs::OpenOptions::new()) } - /// Sets the option for read access. + /// Configures the option for read mode. /// - /// This option, when `true`, will indicate that the file should be readable if opened. + /// When set to `true`, this option means the file will be readable after opening. /// /// # Examples /// @@ -109,11 +109,11 @@ impl OpenOptions { self } - /// Sets the option for write access. + /// Configures the option for write mode. /// - /// This option, when `true`, will indicate that the file should be writable if opened. + /// When set to `true`, this option means the file will be writable after opening. /// - /// If the file already exists, any write calls on it will overwrite its contents, without + /// If the file already exists, write calls on it will overwrite the previous contents without /// truncating it. /// /// # Examples @@ -135,31 +135,10 @@ impl OpenOptions { self } - /// Sets the option for append mode. - /// - /// This option, when `true`, means that writes will append to a file instead of overwriting - /// previous contents. Note that setting `.write(true).append(true)` has the same effect as - /// setting only `.append(true)`. - /// - /// For most filesystems, the operating system guarantees that all writes are atomic: no writes - /// get mangled because another process writes at the same time. - /// - /// One maybe obvious note when using append mode: make sure that all data that belongs - /// together is written to the file in one operation. This can be done by concatenating strings - /// before writing them, or using a buffered writer (with a buffer of adequate size), and - /// flushing when the message is complete. - /// - /// If a file is opened with both read and append access, beware that after opening and after - /// every write, the position for reading may be set at the end of the file. So, before - /// writing, save the current position by seeking with a zero offset, and restore it before the - /// next read. - /// - /// ## Note + /// Configures the option for append mode. /// - /// This function doesn't create the file if it doesn't exist. Use the [`create`] method to do - /// so. - /// - /// [`create`]: #method.create + /// When set to `true`, this option means the file will be writable after opening and the file + /// cursor will be moved to the end of file before every write operaiton. /// /// # Examples /// @@ -180,12 +159,14 @@ impl OpenOptions { self } - /// Sets the option for truncating a previous file. + /// Configures the option for truncating the previous file. + /// + /// When set to `true`, the file will be truncated to the length of 0 bytes. /// - /// If a file is successfully opened with this option set, it will truncate the file to 0 - /// length if it already exists. + /// The file must be opened in [`write`] or [`append`] mode for truncation to work. /// - /// The file must be opened with write access for truncation to work. + /// [`write`]: #method.write + /// [`append`]: #method.append /// /// # Examples /// @@ -207,11 +188,11 @@ impl OpenOptions { self } - /// Sets the option for creating a new file. + /// Configures the option for creating a new file if it doesn't exist. /// - /// This option indicates whether a new file will be created if the file does not yet exist. + /// When set to `true`, this option means a new file will be created if it doesn't exist. /// - /// In order for the file to be created, [`write`] or [`append`] access must be used. + /// The file must be opened in [`write`] or [`append`] mode for file creation to work. /// /// [`write`]: #method.write /// [`append`]: #method.append @@ -236,21 +217,15 @@ impl OpenOptions { self } - /// Sets the option to always create a new file. - /// - /// This option indicates whether a new file will be created. No file is allowed to exist at - /// the target location, also no (dangling) symlink. + /// Configures the option for creating a new file or failing if it already exists. /// - /// This option is useful because it is atomic. Otherwise, between checking whether a file - /// exists and creating a new one, the file may have been created by another process (a TOCTOU - /// race condition / attack). + /// When set to `true`, this option means a new file will be created, or the open operation + /// will fail if the file already exists. /// - /// If `.create_new(true)` is set, [`.create()`] and [`.truncate()`] are ignored. + /// The file must be opened in [`write`] or [`append`] mode for file creation to work. /// - /// The file must be opened with write or append access in order to create a new file. - /// - /// [`.create()`]: #method.create - /// [`.truncate()`]: #method.truncate + /// [`write`]: #method.write + /// [`append`]: #method.append /// /// # Examples /// @@ -272,37 +247,27 @@ impl OpenOptions { self } - /// Opens a file at specified path with the configured options. + /// Opens a file with the configured options. /// /// # Errors /// - /// This function will return an error under a number of different circumstances. Some of these - /// error conditions are listed here, together with their [`ErrorKind`]. The mapping to - /// [`ErrorKind`]s is not part of the compatibility contract of the function, especially the - /// `Other` kind might change to more specific kinds in the future. - /// - /// * [`NotFound`]: The specified file does not exist and neither `create` or `create_new` is - /// set. - /// * [`NotFound`]: One of the directory components of the file path does not exist. - /// * [`PermissionDenied`]: The user lacks permission to get the specified access rights for - /// the file. - /// * [`PermissionDenied`]: The user lacks permission to open one of the directory components - /// of the specified path. - /// * [`AlreadyExists`]: `create_new` was specified and the file already exists. - /// * [`InvalidInput`]: Invalid combinations of open options (truncate without write access, no - /// access mode set, etc.). - /// * [`Other`]: One of the directory components of the specified file path was not, in fact, a - /// directory. - /// * [`Other`]: Filesystem-level errors: full disk, write permission requested on a read-only - /// file system, exceeded disk quota, too many open files, too long filename, too many - /// symbolic links in the specified path (Unix-like systems only), etc. - /// - /// [`ErrorKind`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html - /// [`AlreadyExists`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.AlreadyExists - /// [`InvalidInput`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.InvalidInput - /// [`NotFound`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.NotFound - /// [`Other`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.Other - /// [`PermissionDenied`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.PermissionDenied + /// An error will be returned in the following situations: + /// + /// * The file does not exist and neither [`create`] nor [`create_new`] were set. + /// * The file's parent directory does not exist. + /// * The current process lacks permissions to open the file in the configured mode. + /// * The file already exists and [`create_new`] was set. + /// * Invalid combination of options was used, like [`truncate`] was set but [`write`] wasn't, + /// or none of [`read`], [`write`], and [`append`] modes was set. + /// * An OS-level occurred, like too many files are open or the file name is too long. + /// * Some other I/O error occurred. + /// + /// [`read`]: #method.read + /// [`write`]: #method.write + /// [`append`]: #method.append + /// [`truncate`]: #method.truncate + /// [`create`]: #method.create + /// [`create_new`]: #method.create_new /// /// # Examples /// @@ -311,7 +276,10 @@ impl OpenOptions { /// # /// use async_std::fs::OpenOptions; /// - /// let file = OpenOptions::new().open("a.txt").await?; + /// let file = OpenOptions::new() + /// .read(true) + /// .open("a.txt") + /// .await?; /// # /// # Ok(()) }) } /// ``` diff --git a/src/fs/permissions.rs b/src/fs/permissions.rs new file mode 100644 index 00000000..628dd392 --- /dev/null +++ b/src/fs/permissions.rs @@ -0,0 +1,58 @@ +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(feature = "docs")] { + /// A set of permissions on a file or directory. + /// + /// This type is a re-export of [`std::fs::Permissions`]. + /// + /// [`std::fs::Permissions`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html + #[derive(Clone, PartialEq, Eq, Debug)] + pub struct Permissions { + _private: (), + } + + impl Permissions { + /// Returns the read-only flag. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let perm = fs::metadata("a.txt").await?.permissions(); + /// println!("{:?}", perm.readonly()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn readonly(&self) -> bool { + unimplemented!() + } + + /// Configures the read-only flag. + /// + /// [`fs::set_permissions`]: fn.set_permissions.html + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let mut perm = fs::metadata("a.txt").await?.permissions(); + /// perm.set_readonly(true); + /// fs::set_permissions("a.txt", perm).await?; + /// # + /// # Ok(()) }) } + /// ``` + pub fn set_readonly(&mut self, readonly: bool) { + unimplemented!() + } + } + } else { + pub use std::fs::Permissions; + } +} diff --git a/src/fs/read.rs b/src/fs/read.rs index 105ae79c..6b3560db 100644 --- a/src/fs/read.rs +++ b/src/fs/read.rs @@ -1,25 +1,28 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Read the entire contents of a file into a bytes vector. +/// Reads the entire contents of a file as raw bytes. /// /// This is a convenience function for reading entire files. It pre-allocates a buffer based on the -/// file size when available, so it is generally faster than manually opening a file and reading -/// into a `Vec`. +/// file size when available, so it is typically faster than manually opening a file and reading +/// from it. +/// +/// If you want to read the contents as a string, use [`read_to_string`] instead. /// /// This function is an async version of [`std::fs::read`]. /// +/// [`read_to_string`]: fn.read_to_string.html /// [`std::fs::read`]: https://doc.rust-lang.org/std/fs/fn.read.html /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` does not exist. -/// * The current process lacks permissions to read `path`. +/// * `path` does not point to an existing file. +/// * The current process lacks permissions to read the file. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -34,5 +37,5 @@ use crate::task::blocking; /// ``` pub async fn read>(path: P) -> io::Result> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::read(path) }).await + blocking::spawn(async move { std::fs::read(path) }).await } diff --git a/src/fs/read_dir.rs b/src/fs/read_dir.rs index b3771610..ea0caec1 100644 --- a/src/fs/read_dir.rs +++ b/src/fs/read_dir.rs @@ -1,30 +1,29 @@ -use std::fs; use std::path::Path; use std::pin::Pin; -use super::DirEntry; +use crate::fs::DirEntry; use crate::future::Future; use crate::io; use crate::task::{blocking, Context, Poll}; -/// Returns a stream over the entries within a directory. +/// Returns a stream of entries in a directory. /// -/// The stream yields items of type [`io::Result`]`<`[`DirEntry`]`>`. New errors may be encountered -/// after a stream is initially constructed. +/// The stream yields items of type [`io::Result`]`<`[`DirEntry`]`>`. Note that I/O errors can +/// occur while reading from the stream. /// /// This function is an async version of [`std::fs::read_dir`]. /// -/// [`io::Result`]: https://doc.rust-lang.org/std/io/type.Result.html +/// [`io::Result`]: ../io/type.Result.html /// [`DirEntry`]: struct.DirEntry.html /// [`std::fs::read_dir`]: https://doc.rust-lang.org/std/fs/fn.read_dir.html /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` does not exist. -/// * `path` does not point at a directory. -/// * The current process lacks permissions to view the contents of `path`. +/// * `path` does not point to an existing directory. +/// * The current process lacks permissions to read the contents of the directory. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -34,23 +33,23 @@ use crate::task::{blocking, Context, Poll}; /// use async_std::fs; /// use async_std::prelude::*; /// -/// let mut dir = fs::read_dir(".").await?; +/// let mut entries = fs::read_dir(".").await?; /// -/// while let Some(entry) = dir.next().await { -/// let entry = entry?; -/// println!("{:?}", entry.file_name()); +/// while let Some(res) = entries.next().await { +/// let entry = res?; +/// println!("{}", entry.file_name().to_string_lossy()); /// } /// # /// # Ok(()) }) } /// ``` pub async fn read_dir>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::read_dir(path) }) + blocking::spawn(async move { std::fs::read_dir(path) }) .await .map(ReadDir::new) } -/// A stream over entries in a directory. +/// A stream of entries in a directory. /// /// This stream is returned by [`read_dir`] and yields items of type /// [`io::Result`]`<`[`DirEntry`]`>`. Each [`DirEntry`] can then retrieve information like entry's @@ -59,7 +58,7 @@ pub async fn read_dir>(path: P) -> io::Result { /// This type is an async version of [`std::fs::ReadDir`]. /// /// [`read_dir`]: fn.read_dir.html -/// [`io::Result`]: https://doc.rust-lang.org/std/io/type.Result.html +/// [`io::Result`]: ../io/type.Result.html /// [`DirEntry`]: struct.DirEntry.html /// [`std::fs::ReadDir`]: https://doc.rust-lang.org/std/fs/struct.ReadDir.html #[derive(Debug)] @@ -70,13 +69,13 @@ pub struct ReadDir(State); /// The `ReadDir` can be either idle or busy performing an asynchronous operation. #[derive(Debug)] enum State { - Idle(Option), - Busy(blocking::JoinHandle<(fs::ReadDir, Option>)>), + Idle(Option), + Busy(blocking::JoinHandle<(std::fs::ReadDir, Option>)>), } impl ReadDir { /// Creates an asynchronous `ReadDir` from a synchronous handle. - pub(crate) fn new(inner: fs::ReadDir) -> ReadDir { + pub(crate) fn new(inner: std::fs::ReadDir) -> ReadDir { ReadDir(State::Idle(Some(inner))) } } diff --git a/src/fs/read_link.rs b/src/fs/read_link.rs index 9ab87e58..aede99bc 100644 --- a/src/fs/read_link.rs +++ b/src/fs/read_link.rs @@ -1,10 +1,9 @@ -use std::fs; use std::path::{Path, PathBuf}; use crate::io; use crate::task::blocking; -/// Reads a symbolic link, returning the path it points to. +/// Reads a symbolic link and returns the path it points to. /// /// This function is an async version of [`std::fs::read_link`]. /// @@ -12,10 +11,10 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` is not a symbolic link. -/// * `path` does not exist. +/// * `path` does not point to an existing link. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -30,5 +29,5 @@ use crate::task::blocking; /// ``` pub async fn read_link>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::read_link(path) }).await + blocking::spawn(async move { std::fs::read_link(path) }).await } diff --git a/src/fs/read_to_string.rs b/src/fs/read_to_string.rs index c5ce36bb..345f76ef 100644 --- a/src/fs/read_to_string.rs +++ b/src/fs/read_to_string.rs @@ -1,21 +1,29 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Read the entire contents of a file into a string. +/// Reads the entire contents of a file as a string. +/// +/// This is a convenience function for reading entire files. It pre-allocates a string based on the +/// file size when available, so it is typically faster than manually opening a file and reading +/// from it. +/// +/// If you want to read the contents as raw bytes, use [`read`] instead. /// /// This function is an async version of [`std::fs::read_to_string`]. /// +/// [`read`]: fn.read.html /// [`std::fs::read_to_string`]: https://doc.rust-lang.org/std/fs/fn.read_to_string.html /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` is not a file. -/// * The current process lacks permissions to read `path`. +/// * `path` does not point to an existing file. +/// * The current process lacks permissions to read the file. +/// * The contents of the file cannot be read as a UTF-8 string. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -30,5 +38,5 @@ use crate::task::blocking; /// ``` pub async fn read_to_string>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::read_to_string(path) }).await + blocking::spawn(async move { std::fs::read_to_string(path) }).await } diff --git a/src/fs/remove_dir.rs b/src/fs/remove_dir.rs index 275e7994..a176edc8 100644 --- a/src/fs/remove_dir.rs +++ b/src/fs/remove_dir.rs @@ -1,10 +1,9 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Removes an existing, empty directory. +/// Removes an empty directory. /// /// This function is an async version of [`std::fs::remove_dir`]. /// @@ -12,10 +11,11 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` is not an empty directory. -/// * The current process lacks permissions to remove directory at `path`. +/// * `path` is not an existing and empty directory. +/// * The current process lacks permissions to remove the directory. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -24,11 +24,11 @@ use crate::task::blocking; /// # /// use async_std::fs; /// -/// fs::remove_dir("./some/dir").await?; +/// fs::remove_dir("./some/directory").await?; /// # /// # Ok(()) }) } /// ``` pub async fn remove_dir>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::remove_dir(path) }).await + blocking::spawn(async move { std::fs::remove_dir(path) }).await } diff --git a/src/fs/remove_dir_all.rs b/src/fs/remove_dir_all.rs index 0be81df5..9db0c31f 100644 --- a/src/fs/remove_dir_all.rs +++ b/src/fs/remove_dir_all.rs @@ -1,10 +1,9 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Removes an directory and all of its contents. +/// Removes a directory and all of its contents. /// /// This function is an async version of [`std::fs::remove_dir_all`]. /// @@ -12,10 +11,11 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` is not a directory. -/// * The current process lacks permissions to remove directory at `path`. +/// * `path` is not an existing and empty directory. +/// * The current process lacks permissions to remove the directory. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -24,11 +24,11 @@ use crate::task::blocking; /// # /// use async_std::fs; /// -/// fs::remove_dir_all("./some/dir").await?; +/// fs::remove_dir_all("./some/directory").await?; /// # /// # Ok(()) }) } /// ``` pub async fn remove_dir_all>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::remove_dir_all(path) }).await + blocking::spawn(async move { std::fs::remove_dir_all(path) }).await } diff --git a/src/fs/remove_file.rs b/src/fs/remove_file.rs index 09bd07ee..cc0eeb25 100644 --- a/src/fs/remove_file.rs +++ b/src/fs/remove_file.rs @@ -1,10 +1,9 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Removes a file from the filesystem. +/// Removes a file. /// /// This function is an async version of [`std::fs::remove_file`]. /// @@ -12,10 +11,11 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` is not a file. -/// * The current process lacks permissions to remove file at `path`. +/// * `path` does not point to an existing file. +/// * The current process lacks permissions to remove the file. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -30,5 +30,5 @@ use crate::task::blocking; /// ``` pub async fn remove_file>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::remove_file(path) }).await + blocking::spawn(async move { std::fs::remove_file(path) }).await } diff --git a/src/fs/rename.rs b/src/fs/rename.rs index 05f755a4..72cd227f 100644 --- a/src/fs/rename.rs +++ b/src/fs/rename.rs @@ -1,10 +1,12 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Renames a file or directory to a new name, replacing the original if it already exists. +/// Renames a file or directory to a new location. +/// +/// If a file or directory already exists at the target location, it will be overwritten by this +/// operation. /// /// This function is an async version of [`std::fs::rename`]. /// @@ -12,11 +14,12 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `from` does not exist. +/// * `from` does not point to an existing file or directory. /// * `from` and `to` are on different filesystems. -/// * The current process lacks permissions to rename `from` to `to`. +/// * The current process lacks permissions to do the rename operation. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -32,5 +35,5 @@ use crate::task::blocking; pub async fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> { let from = from.as_ref().to_owned(); let to = to.as_ref().to_owned(); - blocking::spawn(async move { fs::rename(&from, &to) }).await + blocking::spawn(async move { std::fs::rename(&from, &to) }).await } diff --git a/src/fs/set_permissions.rs b/src/fs/set_permissions.rs index 05e5bdab..41f92b26 100644 --- a/src/fs/set_permissions.rs +++ b/src/fs/set_permissions.rs @@ -1,10 +1,10 @@ -use std::fs; use std::path::Path; use crate::io; +use crate::fs::Permissions; use crate::task::blocking; -/// Changes the permissions on a file or directory. +/// Changes the permissions of a file or directory. /// /// This function is an async version of [`std::fs::set_permissions`]. /// @@ -12,10 +12,11 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` does not exist. -/// * The current process lacks permissions to change attributes of `path`. +/// * `path` does not point to an existing file or directory. +/// * The current process lacks permissions to change attributes on the file or directory. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -30,7 +31,7 @@ use crate::task::blocking; /// # /// # Ok(()) }) } /// ``` -pub async fn set_permissions>(path: P, perm: fs::Permissions) -> io::Result<()> { +pub async fn set_permissions>(path: P, perm: Permissions) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::set_permissions(path, perm) }).await + blocking::spawn(async move { std::fs::set_permissions(path, perm) }).await } diff --git a/src/fs/symlink_metadata.rs b/src/fs/symlink_metadata.rs index 78c95bea..6f1b9d50 100644 --- a/src/fs/symlink_metadata.rs +++ b/src/fs/symlink_metadata.rs @@ -1,21 +1,26 @@ -use std::fs::{self, Metadata}; use std::path::Path; +use crate::fs::Metadata; use crate::io; use crate::task::blocking; -/// Queries the metadata for a path without following symlinks. +/// Reads metadata for a path without following symbolic links. +/// +/// If you want to follow symbolic links before reading metadata of the target file or directory, +/// use [`metadata`] instead. /// /// This function is an async version of [`std::fs::symlink_metadata`]. /// +/// [`metadata`]: fn.metadata.html /// [`std::fs::symlink_metadata`]: https://doc.rust-lang.org/std/fs/fn.symlink_metadata.html /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` does not exist. -/// * The current process lacks permissions to query metadata for `path`. +/// * `path` does not point to an existing file or directory. +/// * The current process lacks permissions to read metadata for the path. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -30,5 +35,5 @@ use crate::task::blocking; /// ``` pub async fn symlink_metadata>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::symlink_metadata(path) }).await + blocking::spawn(async move { std::fs::symlink_metadata(path) }).await } diff --git a/src/fs/write.rs b/src/fs/write.rs index ceaf0fcd..b0d7abcc 100644 --- a/src/fs/write.rs +++ b/src/fs/write.rs @@ -1,10 +1,9 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Writes a slice of bytes as the entire contents of a file. +/// Writes a slice of bytes as the new contents of a file. /// /// This function will create a file if it does not exist, and will entirely replace its contents /// if it does. @@ -15,9 +14,11 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * The current process lacks permissions to write into `path`. +/// * The file's parent directory does not exist. +/// * The current process lacks permissions to write to the file. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -26,12 +27,12 @@ use crate::task::blocking; /// # /// use async_std::fs; /// -/// fs::write("a.txt", b"Lorem ipsum").await?; +/// fs::write("a.txt", b"Hello world!").await?; /// # /// # Ok(()) }) } /// ``` pub async fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { let path = path.as_ref().to_owned(); let contents = contents.as_ref().to_owned(); - blocking::spawn(async move { fs::write(path, contents) }).await + blocking::spawn(async move { std::fs::write(path, contents) }).await } diff --git a/src/future/timeout.rs b/src/future/timeout.rs index eb7d8ba1..e433bf18 100644 --- a/src/future/timeout.rs +++ b/src/future/timeout.rs @@ -60,7 +60,7 @@ impl Future for TimeoutFuture { match self.as_mut().future().poll(cx) { Poll::Ready(v) => Poll::Ready(Ok(v)), Poll::Pending => match self.delay().poll(cx) { - Poll::Ready(_) => Poll::Ready(Err(TimeoutError { _priv: () })), + Poll::Ready(_) => Poll::Ready(Err(TimeoutError { _private: () })), Poll::Pending => Poll::Pending, }, } @@ -71,7 +71,7 @@ impl Future for TimeoutFuture { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct TimeoutError { - _priv: (), + _private: (), } impl Error for TimeoutError {} diff --git a/src/io/buf_read/lines.rs b/src/io/buf_read/lines.rs index 17ec447c..0536086c 100644 --- a/src/io/buf_read/lines.rs +++ b/src/io/buf_read/lines.rs @@ -16,7 +16,7 @@ use crate::task::{Context, Poll}; /// /// [`lines`]: trait.BufRead.html#method.lines /// [`BufRead`]: trait.BufRead.html -/// [`std::io::Lines`]: https://doc.rust-lang.org/nightly/std/io/struct.Lines.html +/// [`std::io::Lines`]: https://doc.rust-lang.org/std/io/struct.Lines.html #[derive(Debug)] pub struct Lines { pub(crate) reader: R, diff --git a/src/io/buf_reader.rs b/src/io/buf_reader.rs index f38307a9..6329a1ce 100644 --- a/src/io/buf_reader.rs +++ b/src/io/buf_reader.rs @@ -37,10 +37,10 @@ const DEFAULT_CAPACITY: usize = 8 * 1024; /// use async_std::io::BufReader; /// use async_std::prelude::*; /// -/// let mut f = BufReader::new(File::open("a.txt").await?); +/// let mut file = BufReader::new(File::open("a.txt").await?); /// /// let mut line = String::new(); -/// f.read_line(&mut line).await?; +/// file.read_line(&mut line).await?; /// # /// # Ok(()) }) } /// ``` @@ -134,8 +134,8 @@ impl BufReader { /// use async_std::fs::File; /// use async_std::io::BufReader; /// - /// let mut f = BufReader::new(File::open("a.txt").await?); - /// let inner = f.get_mut(); + /// let mut file = BufReader::new(File::open("a.txt").await?); + /// let inner = file.get_mut(); /// # /// # Ok(()) }) } /// ``` diff --git a/src/io/empty.rs b/src/io/empty.rs index 35ed732b..2668dcc7 100644 --- a/src/io/empty.rs +++ b/src/io/empty.rs @@ -25,7 +25,7 @@ use crate::task::{Context, Poll}; /// # Ok(()) }) } /// ``` pub fn empty() -> Empty { - Empty { _priv: () } + Empty { _private: () } } /// A reader that contains no data. @@ -34,7 +34,7 @@ pub fn empty() -> Empty { /// /// [`sink`]: fn.sink.html pub struct Empty { - _priv: (), + _private: (), } impl fmt::Debug for Empty { diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index 819f26e6..bc6671cc 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -63,10 +63,10 @@ pub trait Read { /// use async_std::fs::File; /// use async_std::prelude::*; /// - /// let mut f = File::open("a.txt").await?; + /// let mut file = File::open("a.txt").await?; /// /// let mut buf = vec![0; 1024]; - /// let n = f.read(&mut buf).await?; + /// let n = file.read(&mut buf).await?; /// # /// # Ok(()) }) } /// ``` @@ -112,10 +112,10 @@ pub trait Read { /// use async_std::fs::File; /// use async_std::prelude::*; /// - /// let mut f = File::open("a.txt").await?; + /// let mut file = File::open("a.txt").await?; /// /// let mut buf = Vec::new(); - /// f.read_to_end(&mut buf).await?; + /// file.read_to_end(&mut buf).await?; /// # /// # Ok(()) }) } /// ``` @@ -149,10 +149,10 @@ pub trait Read { /// use async_std::fs::File; /// use async_std::prelude::*; /// - /// let mut f = File::open("a.txt").await?; + /// let mut file = File::open("a.txt").await?; /// /// let mut buf = String::new(); - /// f.read_to_string(&mut buf).await?; + /// file.read_to_string(&mut buf).await?; /// # /// # Ok(()) }) } /// ``` @@ -201,10 +201,10 @@ pub trait Read { /// use async_std::fs::File; /// use async_std::prelude::*; /// - /// let mut f = File::open("a.txt").await?; + /// let mut file = File::open("a.txt").await?; /// /// let mut buf = vec![0; 10]; - /// f.read_exact(&mut buf).await?; + /// file.read_exact(&mut buf).await?; /// # /// # Ok(()) }) } /// ``` diff --git a/src/io/seek.rs b/src/io/seek.rs index 61a5d9c5..b16da75f 100644 --- a/src/io/seek.rs +++ b/src/io/seek.rs @@ -49,9 +49,9 @@ pub trait Seek { /// use async_std::io::SeekFrom; /// use async_std::prelude::*; /// - /// let mut f = File::open("a.txt").await?; + /// let mut file = File::open("a.txt").await?; /// - /// let file_len = f.seek(SeekFrom::End(0)).await?; + /// let file_len = file.seek(SeekFrom::End(0)).await?; /// # /// # Ok(()) }) } /// ``` diff --git a/src/io/sink.rs b/src/io/sink.rs index fba56334..071f6ed6 100644 --- a/src/io/sink.rs +++ b/src/io/sink.rs @@ -22,7 +22,7 @@ use crate::task::{Context, Poll}; /// # Ok(()) }) } /// ``` pub fn sink() -> Sink { - Sink { _priv: () } + Sink { _private: () } } /// A writer that consumes and drops all data. @@ -31,7 +31,7 @@ pub fn sink() -> Sink { /// /// [`sink`]: fn.sink.html pub struct Sink { - _priv: (), + _private: (), } impl fmt::Debug for Sink { diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index 04cff74f..63a3cd82 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -56,9 +56,9 @@ pub trait Write { /// use async_std::fs::File; /// use async_std::prelude::*; /// - /// let mut f = File::create("a.txt").await?; + /// let mut file = File::create("a.txt").await?; /// - /// let n = f.write(b"hello world").await?; + /// let n = file.write(b"hello world").await?; /// # /// # Ok(()) }) } /// ``` @@ -76,10 +76,10 @@ pub trait Write { /// use async_std::fs::File; /// use async_std::prelude::*; /// - /// let mut f = File::create("a.txt").await?; + /// let mut file = File::create("a.txt").await?; /// - /// f.write_all(b"hello world").await?; - /// f.flush().await?; + /// file.write_all(b"hello world").await?; + /// file.flush().await?; /// # /// # Ok(()) }) } /// ``` @@ -113,6 +113,8 @@ pub trait Write { /// an error is returned. This method will not return until the entire buffer has been /// successfully written or such an error occurs. /// + /// [`write`]: #tymethod.write + /// /// # Examples /// /// ```no_run @@ -121,9 +123,9 @@ pub trait Write { /// use async_std::fs::File; /// use async_std::prelude::*; /// - /// let mut f = File::create("a.txt").await?; + /// let mut file = File::create("a.txt").await?; /// - /// f.write_all(b"hello world").await?; + /// file.write_all(b"hello world").await?; /// # /// # Ok(()) }) } /// ``` diff --git a/src/lib.rs b/src/lib.rs index e5615b99..01813e4e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,16 @@ //! Async version of the Rust standard library. //! -//! This crate is an async version of [`std`]. +//! Modules in this crate are organized in the same way as in the standard library, except blocking +//! functions have been replaced with async functions and threads have been replaced with +//! lightweight tasks. //! -//! Higher-level documentation in the form of the book -//! ["Async programming in Rust with async-std"][book] -//! is available. +//! More information, reading materials, and other resources: //! -//! [`std`]: https://doc.rust-lang.org/std/index.html -//! [book]: https://book.async.rs +//! * [🌐 The async-std website](https://async.rs/) +//! * [📖 The async-std book](https://book.async.rs) +//! * [🐙 GitHub repository](https://github.com/async-rs/async-std) +//! * [📒 List of code examples](https://github.com/async-rs/async-std/tree/master/examples) +//! * [💬 Discord chat](https://discord.gg/JvZeVNe) //! //! # Examples //! @@ -23,8 +26,15 @@ //! } //! ``` //! -//! See [here](https://github.com/async-rs/async-std/tree/master/examples) -//! for more examples. +//! # Features +//! +//! Unstable APIs in this crate are available when the `unstable` Cargo feature is enabled: +//! +//! ```toml +//! [dependencies.async-std] +//! version = "0.99" +//! features = ["unstable"] +//! ``` #![cfg_attr(feature = "docs", feature(doc_cfg))] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] diff --git a/src/os/unix/fs.rs b/src/os/unix/fs.rs index 16d7cab7..be8932c0 100644 --- a/src/os/unix/fs.rs +++ b/src/os/unix/fs.rs @@ -69,7 +69,6 @@ cfg_if! { fn custom_flags(&mut self, flags: i32) -> &mut Self; } } else { - #[doc(inline)] pub use std::os::unix::fs::{DirBuilderExt, OpenOptionsExt}; } } diff --git a/src/os/unix/io.rs b/src/os/unix/io.rs index 87e83ea2..820d509c 100644 --- a/src/os/unix/io.rs +++ b/src/os/unix/io.rs @@ -51,7 +51,6 @@ cfg_if! { fn into_raw_fd(self) -> RawFd; } } else { - #[doc(inline)] pub use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; } } diff --git a/src/os/unix/net/mod.rs b/src/os/unix/net/mod.rs index 1597948f..465db540 100644 --- a/src/os/unix/net/mod.rs +++ b/src/os/unix/net/mod.rs @@ -94,7 +94,6 @@ cfg_if! { } } } else { - #[doc(inline)] pub use std::os::unix::net::SocketAddr; } } diff --git a/src/os/windows/io.rs b/src/os/windows/io.rs index adf0c0d0..20f87d29 100644 --- a/src/os/windows/io.rs +++ b/src/os/windows/io.rs @@ -43,7 +43,6 @@ cfg_if! { fn into_raw_handle(self) -> RawHandle; } } else { - #[doc(inline)] pub use std::os::windows::io::{ AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, RawSocket, }; diff --git a/src/prelude.rs b/src/prelude.rs index 38956b93..a50e1237 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,6 +1,15 @@ //! The async prelude. //! -//! The prelude re-exports the most commonly used traits in this crate. +//! The prelude re-exports most commonly used traits and macros from this crate. +//! +//! # Examples +//! +//! Import the prelude with: +//! +//! ``` +//! # #[allow(unused_imports)] +//! use async_std::prelude::*; +//! ``` #[doc(no_inline)] pub use crate::future::Future;