You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
async-std/src/fs/open_options.rs

349 lines
12 KiB
Rust

use std::fs;
use std::future::Future;
use std::io;
use std::path::Path;
use cfg_if::cfg_if;
use super::File;
use crate::task::blocking;
/// Options and flags which for configuring how a file is opened.
///
/// 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.
///
/// 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.
///
/// 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
/// [`std::fs::OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html
///
/// # Examples
///
/// Opening a file for reading:
///
/// ```no_run
/// # #![feature(async_await)]
/// use async_std::fs::OpenOptions;
///
/// # futures::executor::block_on(async {
/// let file = OpenOptions::new()
/// .read(true)
/// .open("foo.txt")
/// .await?;
/// # std::io::Result::Ok(())
/// # }).unwrap();
/// ```
///
/// Opening a file for both reading and writing, creating it if it doesn't exist:
///
/// ```no_run
/// # #![feature(async_await)]
/// use async_std::fs::OpenOptions;
///
/// # futures::executor::block_on(async {
/// let file = OpenOptions::new()
/// .read(true)
/// .write(true)
/// .create(true)
/// .open("foo.txt")
/// .await?;
/// # std::io::Result::Ok(())
/// # }).unwrap();
/// ```
#[derive(Clone, Debug)]
pub struct OpenOptions(fs::OpenOptions);
impl OpenOptions {
/// Creates a blank new set of options.
///
/// All options are initially set to `false`.
///
/// # Examples
///
/// ```no_run
/// # #![feature(async_await)]
/// use async_std::fs::OpenOptions;
///
/// # futures::executor::block_on(async {
/// let file = OpenOptions::new()
/// .read(true)
/// .open("foo.txt")
/// .await?;
/// # std::io::Result::Ok(())
/// # }).unwrap();
/// ```
pub fn new() -> OpenOptions {
OpenOptions(fs::OpenOptions::new())
}
/// Sets the option for read access.
///
/// This option, when `true`, will indicate that the file should be readable if opened.
///
/// # Examples
///
/// ```no_run
/// # #![feature(async_await)]
/// use async_std::fs::OpenOptions;
///
/// # futures::executor::block_on(async {
/// let file = OpenOptions::new()
/// .read(true)
/// .open("foo.txt")
/// .await?;
/// # std::io::Result::Ok(())
/// # }).unwrap();
/// ```
pub fn read(&mut self, read: bool) -> &mut OpenOptions {
self.0.read(read);
self
}
/// Sets the option for write access.
///
/// This option, when `true`, will indicate that the file should be writable if opened.
///
/// If the file already exists, any write calls on it will overwrite its contents, without
/// truncating it.
///
/// # Examples
///
/// ```no_run
/// # #![feature(async_await)]
/// use async_std::fs::OpenOptions;
///
/// # futures::executor::block_on(async {
/// let file = OpenOptions::new()
/// .write(true)
/// .open("foo.txt")
/// .await?;
/// # std::io::Result::Ok(())
/// # }).unwrap();
/// ```
pub fn write(&mut self, write: bool) -> &mut OpenOptions {
self.0.write(write);
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
///
/// This function doesn't create the file if it doesn't exist. Use the [`create`] method to do
/// so.
///
/// [`create`]: #method.create
///
/// # Examples
///
/// ```no_run
/// # #![feature(async_await)]
/// use async_std::fs::OpenOptions;
///
/// # futures::executor::block_on(async {
/// let file = OpenOptions::new()
/// .append(true)
/// .open("foo.txt")
/// .await?;
/// # std::io::Result::Ok(())
/// # }).unwrap();
/// ```
pub fn append(&mut self, append: bool) -> &mut OpenOptions {
self.0.append(append);
self
}
/// Sets the option for truncating a previous file.
///
/// 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 with write access for truncation to work.
///
/// # Examples
///
/// ```no_run
/// # #![feature(async_await)]
/// use async_std::fs::OpenOptions;
///
/// # futures::executor::block_on(async {
/// let file = OpenOptions::new()
/// .write(true)
/// .truncate(true)
/// .open("foo.txt")
/// .await?;
/// # std::io::Result::Ok(())
/// # }).unwrap();
/// ```
pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
self.0.truncate(truncate);
self
}
/// Sets the option for creating a new file.
///
/// This option indicates whether a new file will be created if the file does not yet exist.
///
/// In order for the file to be created, [`write`] or [`append`] access must be used.
///
/// [`write`]: #method.write
/// [`append`]: #method.append
///
/// # Examples
///
/// ```no_run
/// # #![feature(async_await)]
/// use async_std::fs::OpenOptions;
///
/// # futures::executor::block_on(async {
/// let file = OpenOptions::new()
/// .write(true)
/// .create(true)
/// .open("foo.txt")
/// .await?;
/// # std::io::Result::Ok(())
/// # }).unwrap();
/// ```
pub fn create(&mut self, create: bool) -> &mut OpenOptions {
self.0.create(create);
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.
///
/// 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).
///
/// If `.create_new(true)` is set, [`.create()`] and [`.truncate()`] are ignored.
///
/// The file must be opened with write or append access in order to create a new file.
///
/// [`.create()`]: #method.create
/// [`.truncate()`]: #method.truncate
///
/// # Examples
///
/// ```no_run
/// # #![feature(async_await)]
/// use async_std::fs::OpenOptions;
///
/// # futures::executor::block_on(async {
/// let file = OpenOptions::new()
/// .write(true)
/// .create_new(true)
/// .open("foo.txt")
/// .await?;
/// # std::io::Result::Ok(())
/// # }).unwrap();
/// ```
pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions {
self.0.create_new(create_new);
self
}
/// Opens a file at specified path 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
///
/// # Examples
///
/// ```no_run
/// # #![feature(async_await)]
/// use async_std::fs::OpenOptions;
///
/// # futures::executor::block_on(async {
/// let file = OpenOptions::new().open("foo.txt").await?;
/// # std::io::Result::Ok(())
/// # }).unwrap();
/// ```
pub fn open<P: AsRef<Path>>(&self, path: P) -> impl Future<Output = io::Result<File>> {
let path = path.as_ref().to_owned();
let options = self.0.clone();
async move { blocking::spawn(async move { options.open(path).map(|f| f.into()) }).await }
}
}
cfg_if! {
if #[cfg(feature = "docs.rs")] {
use crate::os::unix::fs::OpenOptionsExt;
} else if #[cfg(unix)] {
use std::os::unix::fs::OpenOptionsExt;
}
}
#[cfg_attr(feature = "docs.rs", doc(cfg(unix)))]
cfg_if! {
if #[cfg(any(unix, feature = "docs.rs"))] {
impl OpenOptionsExt for OpenOptions {
fn mode(&mut self, mode: u32) -> &mut Self {
self.0.mode(mode);
self
}
fn custom_flags(&mut self, flags: i32) -> &mut Self {
self.0.custom_flags(flags);
self
}
}
}
}