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.
349 lines
12 KiB
Rust
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
|
|
}
|
|
}
|
|
}
|
|
}
|