use super::File;
use crate::runtime::spawn_blocking_io;
use std::io;
use std::path::Path;
#[derive(Debug, Clone)]
#[allow(clippy::struct_excessive_bools)]
pub struct OpenOptions {
read: bool,
write: bool,
append: bool,
truncate: bool,
create: bool,
create_new: bool,
#[cfg(unix)]
mode: Option<u32>,
#[cfg(unix)]
custom_flags: Option<i32>,
}
impl OpenOptions {
#[must_use]
pub fn new() -> Self {
Self {
read: false,
write: false,
append: false,
truncate: false,
create: false,
create_new: false,
#[cfg(unix)]
mode: None,
#[cfg(unix)]
custom_flags: None,
}
}
#[must_use]
pub fn read(mut self, read: bool) -> Self {
self.read = read;
self
}
#[must_use]
pub fn write(mut self, write: bool) -> Self {
self.write = write;
self
}
#[must_use]
pub fn append(mut self, append: bool) -> Self {
self.append = append;
self
}
#[must_use]
pub fn truncate(mut self, truncate: bool) -> Self {
self.truncate = truncate;
self
}
#[must_use]
pub fn create(mut self, create: bool) -> Self {
self.create = create;
self
}
#[must_use]
pub fn create_new(mut self, create_new: bool) -> Self {
self.create_new = create_new;
self
}
#[cfg(unix)]
#[must_use]
pub fn mode(mut self, mode: u32) -> Self {
self.mode = Some(mode);
self
}
#[cfg(unix)]
#[must_use]
pub fn custom_flags(mut self, flags: i32) -> Self {
self.custom_flags = Some(flags);
self
}
pub async fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
let path = path.as_ref().to_owned();
let opts = self.clone();
let std_file = spawn_blocking_io(move || opts.to_std_options().open(&path)).await?;
Ok(File::from_std(std_file))
}
fn to_std_options(&self) -> std::fs::OpenOptions {
let mut opts = std::fs::OpenOptions::new();
opts.read(self.read);
opts.write(self.write);
opts.append(self.append);
opts.truncate(self.truncate);
opts.create(self.create);
opts.create_new(self.create_new);
#[cfg(unix)]
{
use std::os::unix::fs::OpenOptionsExt;
if let Some(mode) = self.mode {
opts.mode(mode);
}
if let Some(flags) = self.custom_flags {
opts.custom_flags(flags);
}
}
opts
}
}
impl Default for OpenOptions {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
fn init_test(name: &str) {
crate::test_utils::init_test_logging();
crate::test_phase!(name);
}
#[test]
fn open_options_builder() {
init_test("open_options_builder");
let opts = OpenOptions::new().read(true).write(true).create(true);
crate::assert_with_log!(opts.read, "read", true, opts.read);
crate::assert_with_log!(opts.write, "write", true, opts.write);
crate::assert_with_log!(opts.create, "create", true, opts.create);
crate::assert_with_log!(!opts.append, "append false", false, opts.append);
crate::assert_with_log!(!opts.truncate, "truncate false", false, opts.truncate);
crate::assert_with_log!(!opts.create_new, "create_new false", false, opts.create_new);
crate::test_complete!("open_options_builder");
}
#[test]
fn open_options_default() {
init_test("open_options_default");
let opts = OpenOptions::default();
crate::assert_with_log!(!opts.read, "read false", false, opts.read);
crate::assert_with_log!(!opts.write, "write false", false, opts.write);
crate::assert_with_log!(!opts.append, "append false", false, opts.append);
crate::assert_with_log!(!opts.truncate, "truncate false", false, opts.truncate);
crate::assert_with_log!(!opts.create, "create false", false, opts.create);
crate::assert_with_log!(!opts.create_new, "create_new false", false, opts.create_new);
crate::test_complete!("open_options_default");
}
#[test]
fn to_std_options_roundtrip() {
init_test("to_std_options_roundtrip");
let dir = tempdir().unwrap();
let path = dir.path().join("test.txt");
let opts = OpenOptions::new().write(true).create(true);
let std_opts = opts.to_std_options();
let file = std_opts.open(&path);
crate::assert_with_log!(file.is_ok(), "open ok", true, file.is_ok());
crate::test_complete!("to_std_options_roundtrip");
}
#[cfg(unix)]
#[test]
fn mode_option_unix() {
init_test("mode_option_unix");
let opts = OpenOptions::new().write(true).create(true).mode(0o600);
crate::assert_with_log!(opts.mode == Some(0o600), "mode", Some(0o600), opts.mode);
crate::test_complete!("mode_option_unix");
}
#[cfg(unix)]
#[test]
fn custom_flags_option_unix() {
init_test("custom_flags_option_unix");
let opts = OpenOptions::new()
.write(true)
.create(true)
.custom_flags(libc::O_CLOEXEC);
crate::assert_with_log!(
opts.custom_flags == Some(libc::O_CLOEXEC),
"custom_flags",
Some(libc::O_CLOEXEC),
opts.custom_flags
);
crate::test_complete!("custom_flags_option_unix");
}
#[test]
fn open_options_debug_clone_default() {
let opts = OpenOptions::new();
let cloned = opts.clone();
let dbg = format!("{opts:?}");
assert!(dbg.contains("OpenOptions"));
let dbg2 = format!("{cloned:?}");
assert_eq!(dbg, dbg2);
let default_opts = OpenOptions::default();
let dbg3 = format!("{default_opts:?}");
assert_eq!(dbg, dbg3);
}
}