use crate::fs::{asyncify, File};
use std::io;
use std::path::Path;
cfg_io_uring! {
mod uring_open_options;
pub(crate) use uring_open_options::UringOpenOptions;
use crate::runtime::driver::op::Op;
}
#[cfg(test)]
mod mock_open_options;
#[cfg(test)]
use mock_open_options::MockOpenOptions as StdOpenOptions;
#[cfg(not(test))]
use std::fs::OpenOptions as StdOpenOptions;
#[cfg(unix)]
use std::os::unix::fs::OpenOptionsExt;
#[cfg(windows)]
use std::os::windows::fs::OpenOptionsExt;
#[derive(Clone, Debug)]
pub struct OpenOptions {
inner: Kind,
}
#[derive(Debug, Clone)]
enum Kind {
Std(StdOpenOptions),
#[cfg(all(
tokio_unstable,
feature = "io-uring",
feature = "rt",
feature = "fs",
target_os = "linux"
))]
Uring(UringOpenOptions),
}
impl OpenOptions {
pub fn new() -> OpenOptions {
#[cfg(all(
tokio_unstable,
feature = "io-uring",
feature = "rt",
feature = "fs",
target_os = "linux"
))]
let inner = Kind::Uring(UringOpenOptions::new());
#[cfg(not(all(
tokio_unstable,
feature = "io-uring",
feature = "rt",
feature = "fs",
target_os = "linux"
)))]
let inner = Kind::Std(StdOpenOptions::new());
OpenOptions { inner }
}
pub fn read(&mut self, read: bool) -> &mut OpenOptions {
match &mut self.inner {
Kind::Std(opts) => {
opts.read(read);
}
#[cfg(all(
tokio_unstable,
feature = "io-uring",
feature = "rt",
feature = "fs",
target_os = "linux"
))]
Kind::Uring(opts) => {
opts.read(read);
}
}
self
}
pub fn write(&mut self, write: bool) -> &mut OpenOptions {
match &mut self.inner {
Kind::Std(opts) => {
opts.write(write);
}
#[cfg(all(
tokio_unstable,
feature = "io-uring",
feature = "rt",
feature = "fs",
target_os = "linux"
))]
Kind::Uring(opts) => {
opts.write(write);
}
}
self
}
pub fn append(&mut self, append: bool) -> &mut OpenOptions {
match &mut self.inner {
Kind::Std(opts) => {
opts.append(append);
}
#[cfg(all(
tokio_unstable,
feature = "io-uring",
feature = "rt",
feature = "fs",
target_os = "linux"
))]
Kind::Uring(opts) => {
opts.append(append);
}
}
self
}
pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
match &mut self.inner {
Kind::Std(opts) => {
opts.truncate(truncate);
}
#[cfg(all(
tokio_unstable,
feature = "io-uring",
feature = "rt",
feature = "fs",
target_os = "linux"
))]
Kind::Uring(opts) => {
opts.truncate(truncate);
}
}
self
}
pub fn create(&mut self, create: bool) -> &mut OpenOptions {
match &mut self.inner {
Kind::Std(opts) => {
opts.create(create);
}
#[cfg(all(
tokio_unstable,
feature = "io-uring",
feature = "rt",
feature = "fs",
target_os = "linux"
))]
Kind::Uring(opts) => {
opts.create(create);
}
}
self
}
pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions {
match &mut self.inner {
Kind::Std(opts) => {
opts.create_new(create_new);
}
#[cfg(all(
tokio_unstable,
feature = "io-uring",
feature = "rt",
feature = "fs",
target_os = "linux"
))]
Kind::Uring(opts) => {
opts.create_new(create_new);
}
}
self
}
pub async fn open(&self, path: impl AsRef<Path>) -> io::Result<File> {
match &self.inner {
Kind::Std(opts) => Self::std_open(opts, path).await,
#[cfg(all(
tokio_unstable,
feature = "io-uring",
feature = "rt",
feature = "fs",
target_os = "linux"
))]
Kind::Uring(opts) => {
let handle = crate::runtime::Handle::current();
let driver_handle = handle.inner.driver().io();
if driver_handle.check_and_init()? {
Op::open(path.as_ref(), opts)?.await
} else {
let opts = opts.clone().into();
Self::std_open(&opts, path).await
}
}
}
}
async fn std_open(opts: &StdOpenOptions, path: impl AsRef<Path>) -> io::Result<File> {
let path = path.as_ref().to_owned();
let opts = opts.clone();
let std = asyncify(move || opts.open(path)).await?;
Ok(File::from_std(std))
}
#[cfg(windows)]
pub(super) fn as_inner_mut(&mut self) -> &mut StdOpenOptions {
match &mut self.inner {
Kind::Std(ref mut opts) => opts,
}
}
}
feature! {
#![unix]
impl OpenOptions {
pub fn mode(&mut self, mode: u32) -> &mut OpenOptions {
match &mut self.inner {
Kind::Std(opts) => {
opts.mode(mode);
}
#[cfg(all(
tokio_unstable,
feature = "io-uring",
feature = "rt",
feature = "fs",
target_os = "linux"
))]
Kind::Uring(opts) => {
opts.mode(mode);
}
}
self
}
pub fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions {
match &mut self.inner {
Kind::Std(opts) => {
opts.custom_flags(flags);
}
#[cfg(all(
tokio_unstable,
feature = "io-uring",
feature = "rt",
feature = "fs",
target_os = "linux"
))]
Kind::Uring(opts) => {
opts.custom_flags(flags);
}
}
self
}
}
}
cfg_windows! {
impl OpenOptions {
pub fn access_mode(&mut self, access: u32) -> &mut OpenOptions {
self.as_inner_mut().access_mode(access);
self
}
pub fn share_mode(&mut self, share: u32) -> &mut OpenOptions {
self.as_inner_mut().share_mode(share);
self
}
pub fn custom_flags(&mut self, flags: u32) -> &mut OpenOptions {
self.as_inner_mut().custom_flags(flags);
self
}
pub fn attributes(&mut self, attributes: u32) -> &mut OpenOptions {
self.as_inner_mut().attributes(attributes);
self
}
pub fn security_qos_flags(&mut self, flags: u32) -> &mut OpenOptions {
self.as_inner_mut().security_qos_flags(flags);
self
}
}
}
impl From<StdOpenOptions> for OpenOptions {
fn from(options: StdOpenOptions) -> OpenOptions {
OpenOptions {
inner: Kind::Std(options),
}
}
}
impl Default for OpenOptions {
fn default() -> Self {
Self::new()
}
}