use crate::default_uring;
use crate::metadata::Metadata;
use crate::uring::URING_LEN_MAX;
use std::cmp::min;
use std::io;
use std::path::Path;
use tokio::fs::File;
#[derive(Clone, Debug, Default)]
pub struct OpenOptions {
read: bool,
write: bool,
append: bool,
truncate: bool,
create: bool,
create_new: bool,
mode: Option<u32>,
custom_flags: i32,
}
impl OpenOptions {
pub fn new() -> Self {
Self::default()
}
pub fn read(&mut self, read: bool) -> &mut Self {
self.read = read;
self
}
pub fn write(&mut self, write: bool) -> &mut Self {
self.write = write;
self
}
pub fn append(&mut self, append: bool) -> &mut Self {
self.append = append;
self
}
pub fn truncate(&mut self, truncate: bool) -> &mut Self {
self.truncate = truncate;
self
}
pub fn create(&mut self, create: bool) -> &mut Self {
self.create = create;
self
}
pub fn create_new(&mut self, create_new: bool) -> &mut Self {
self.create_new = create_new;
self
}
pub fn mode(&mut self, mode: u32) -> &mut Self {
self.mode = Some(mode);
self
}
pub fn custom_flags(&mut self, flags: i32) -> &mut Self {
self.custom_flags = flags;
self
}
pub async fn open(&self, path: impl AsRef<Path>) -> io::Result<File> {
let flags = self.to_flags()?;
let mode = self.mode.unwrap_or(0o644);
let fd = default_uring().open(path, flags, mode).await?;
Ok(File::from_std(std::fs::File::from(fd)))
}
fn to_flags(&self) -> io::Result<i32> {
if self.append && self.truncate {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"cannot combine append and truncate",
));
}
let mut flags = self.custom_flags;
let has_write = self.write || self.append;
if self.read && has_write {
flags |= libc::O_RDWR;
} else if self.read {
flags |= libc::O_RDONLY;
} else if has_write {
flags |= libc::O_WRONLY;
} else {
flags |= libc::O_RDONLY;
}
if self.append {
flags |= libc::O_APPEND;
}
if self.truncate {
flags |= libc::O_TRUNC;
}
if self.create_new {
flags |= libc::O_CREAT | libc::O_EXCL;
} else if self.create {
flags |= libc::O_CREAT;
}
Ok(flags)
}
}
pub async fn open(path: impl AsRef<Path>) -> io::Result<File> {
let fd = default_uring().open(path, libc::O_RDONLY, 0).await?;
Ok(File::from_std(std::fs::File::from(fd)))
}
pub async fn create(path: impl AsRef<Path>) -> io::Result<File> {
let fd = default_uring()
.open(path, libc::O_WRONLY | libc::O_CREAT | libc::O_TRUNC, 0o644)
.await?;
Ok(File::from_std(std::fs::File::from(fd)))
}
pub async fn read(path: impl AsRef<Path>) -> io::Result<Vec<u8>> {
let uring = default_uring();
let fd = uring.open(path.as_ref(), libc::O_RDONLY, 0).await?;
let meta = uring.statx(&fd).await?;
let size = meta.len();
if size == 0 {
return Ok(Vec::new());
}
if size <= URING_LEN_MAX {
return uring.read_exact_at(&fd, 0, size).await;
}
let mut buf = Vec::with_capacity(size as usize);
let mut offset = 0u64;
while offset < size {
let chunk_size = min(URING_LEN_MAX, size - offset);
let chunk = uring.read_exact_at(&fd, offset, chunk_size).await?;
buf.extend_from_slice(&chunk);
offset += chunk_size;
}
Ok(buf)
}
pub async fn read_to_string(path: impl AsRef<Path>) -> io::Result<String> {
let bytes = read(path).await?;
String::from_utf8(bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
}
pub async fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> io::Result<()> {
let uring = default_uring();
let fd = uring
.open(
path.as_ref(),
libc::O_WRONLY | libc::O_CREAT | libc::O_TRUNC,
0o644,
)
.await?;
uring.write_all_at(&fd, 0, contents.as_ref()).await
}
pub async fn metadata(path: impl AsRef<Path>) -> io::Result<Metadata> {
default_uring().statx_path(path).await
}
pub async fn create_dir(path: impl AsRef<Path>) -> io::Result<()> {
default_uring().mkdir(path, 0o755).await
}
pub async fn create_dir_all(path: impl AsRef<Path>) -> io::Result<()> {
let path = path.as_ref();
if metadata(path).await.is_ok() {
return Ok(());
}
let mut to_create = Vec::new();
let mut current = Some(path);
while let Some(p) = current {
if metadata(p).await.is_ok() {
break;
}
to_create.push(p);
current = p.parent();
}
for p in to_create.into_iter().rev() {
match default_uring().mkdir(p, 0o755).await {
Ok(()) => {}
Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {}
Err(e) => return Err(e),
}
}
Ok(())
}
pub async fn remove_file(path: impl AsRef<Path>) -> io::Result<()> {
default_uring().unlink(path).await
}
pub async fn remove_dir(path: impl AsRef<Path>) -> io::Result<()> {
default_uring().rmdir(path).await
}
pub async fn remove_dir_all(path: impl AsRef<Path>) -> io::Result<()> {
let path = path.as_ref();
let mut read_dir = tokio::fs::read_dir(path).await?;
while let Some(entry) = read_dir.next_entry().await? {
let entry_path = entry.path();
let file_type = entry.file_type().await?;
if file_type.is_dir() {
Box::pin(remove_dir_all(&entry_path)).await?;
} else {
remove_file(&entry_path).await?;
}
}
remove_dir(path).await
}
pub async fn rename(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<()> {
default_uring().rename(from, to).await
}
pub async fn copy(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<u64> {
let contents = read(from).await?;
let len = contents.len() as u64;
write(to, contents).await?;
Ok(len)
}
pub async fn exists(path: impl AsRef<Path>) -> bool {
metadata(path).await.is_ok()
}
pub async fn symlink(target: impl AsRef<Path>, link: impl AsRef<Path>) -> io::Result<()> {
default_uring().symlink(target, link).await
}
pub async fn hard_link(original: impl AsRef<Path>, link: impl AsRef<Path>) -> io::Result<()> {
default_uring().hard_link(original, link).await
}
pub async fn truncate(path: impl AsRef<Path>, len: u64) -> io::Result<()> {
let uring = default_uring();
let fd = uring.open(path.as_ref(), libc::O_WRONLY, 0).await?;
uring.ftruncate(&fd, len).await
}
pub async fn append(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> io::Result<()> {
let uring = default_uring();
let fd = uring
.open(
path.as_ref(),
libc::O_WRONLY | libc::O_CREAT | libc::O_APPEND,
0o644,
)
.await?;
uring.write_all_at(&fd, 0, contents.as_ref()).await
}