use std::{future::Future, io, mem::ManuallyDrop, panic::resume_unwind, path::Path};
use compio_buf::{BufResult, IntoInner, IoBuf, IoBufMut};
use compio_driver::{
impl_raw_fd,
op::{BufResultExt, CloseFile, ReadAt, Sync, WriteAt},
ToSharedFd,
};
use compio_io::{AsyncReadAt, AsyncWriteAt};
use compio_runtime::Attacher;
#[cfg(unix)]
use {
compio_buf::{IoVectoredBuf, IoVectoredBufMut},
compio_driver::op::{FileStat, ReadVectoredAt, WriteVectoredAt},
};
use crate::{Metadata, OpenOptions, Permissions};
#[derive(Debug, Clone)]
pub struct File {
inner: Attacher<std::fs::File>,
}
impl File {
pub(crate) fn from_std(file: std::fs::File) -> io::Result<Self> {
Ok(Self {
inner: Attacher::new(file)?,
})
}
pub async fn open(path: impl AsRef<Path>) -> io::Result<Self> {
OpenOptions::new().read(true).open(path).await
}
pub async fn create(path: impl AsRef<Path>) -> io::Result<Self> {
OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(path)
.await
}
pub fn close(self) -> impl Future<Output = io::Result<()>> {
let this = ManuallyDrop::new(self);
async move {
let fd = ManuallyDrop::into_inner(this)
.inner
.into_inner()
.take()
.await;
if let Some(fd) = fd {
let op = CloseFile::new(fd.into());
compio_runtime::submit(op).await.0?;
}
Ok(())
}
}
#[cfg(windows)]
pub async fn metadata(&self) -> io::Result<Metadata> {
let file = self.inner.clone();
compio_runtime::spawn_blocking(move || file.metadata().map(Metadata::from_std))
.await
.unwrap_or_else(|e| resume_unwind(e))
}
#[cfg(unix)]
pub async fn metadata(&self) -> io::Result<Metadata> {
let op = FileStat::new(self.to_shared_fd());
let BufResult(res, op) = compio_runtime::submit(op).await;
res.map(|_| Metadata::from_stat(op.into_inner()))
}
#[cfg(windows)]
pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
let file = self.inner.clone();
compio_runtime::spawn_blocking(move || file.set_permissions(perm.0))
.await
.unwrap_or_else(|e| resume_unwind(e))
}
#[cfg(unix)]
pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
use std::os::unix::fs::PermissionsExt;
use compio_driver::{syscall, AsRawFd};
let file = self.inner.clone();
compio_runtime::spawn_blocking(move || {
syscall!(libc::fchmod(file.as_raw_fd(), perm.mode() as libc::mode_t))?;
Ok(())
})
.await
.unwrap_or_else(|e| resume_unwind(e))
}
async fn sync_impl(&self, datasync: bool) -> io::Result<()> {
let op = Sync::new(self.to_shared_fd(), datasync);
compio_runtime::submit(op).await.0?;
Ok(())
}
pub async fn sync_all(&self) -> io::Result<()> {
self.sync_impl(false).await
}
pub async fn sync_data(&self) -> io::Result<()> {
self.sync_impl(true).await
}
}
impl AsyncReadAt for File {
async fn read_at<T: IoBufMut>(&self, buffer: T, pos: u64) -> BufResult<usize, T> {
let fd = self.inner.to_shared_fd();
let op = ReadAt::new(fd, pos, buffer);
compio_runtime::submit(op).await.into_inner().map_advanced()
}
#[cfg(unix)]
async fn read_vectored_at<T: IoVectoredBufMut>(
&self,
buffer: T,
pos: u64,
) -> BufResult<usize, T> {
let fd = self.inner.to_shared_fd();
let op = ReadVectoredAt::new(fd, pos, buffer);
compio_runtime::submit(op).await.into_inner().map_advanced()
}
}
impl AsyncWriteAt for File {
#[inline]
async fn write_at<T: IoBuf>(&mut self, buf: T, pos: u64) -> BufResult<usize, T> {
(&*self).write_at(buf, pos).await
}
#[cfg(unix)]
#[inline]
async fn write_vectored_at<T: IoVectoredBuf>(
&mut self,
buf: T,
pos: u64,
) -> BufResult<usize, T> {
(&*self).write_vectored_at(buf, pos).await
}
}
impl AsyncWriteAt for &File {
async fn write_at<T: IoBuf>(&mut self, buffer: T, pos: u64) -> BufResult<usize, T> {
let fd = self.inner.to_shared_fd();
let op = WriteAt::new(fd, pos, buffer);
compio_runtime::submit(op).await.into_inner()
}
#[cfg(unix)]
async fn write_vectored_at<T: IoVectoredBuf>(
&mut self,
buffer: T,
pos: u64,
) -> BufResult<usize, T> {
let fd = self.inner.to_shared_fd();
let op = WriteVectoredAt::new(fd, pos, buffer);
compio_runtime::submit(op).await.into_inner()
}
}
impl_raw_fd!(File, std::fs::File, inner, file);