use std::{future::Future, io, mem::ManuallyDrop, path::Path};
use compio_buf::{BufResult, IntoInner, IoBuf, IoBufMut};
#[cfg(unix)]
use compio_driver::op::FileStat;
use compio_driver::{
BufferRef, ResultTakeBuffer, ToSharedFd, impl_raw_fd,
op::{BufResultExt, CloseFile, ReadAt, ReadManagedAt, Sync, WriteAt},
};
use compio_io::{AsyncReadAt, AsyncReadManagedAt, AsyncWriteAt, util::Splittable};
use compio_runtime::{Runtime, fd::AsyncFd};
#[cfg(all(unix, not(solarish)))]
use {
compio_buf::{IoVectoredBuf, IoVectoredBufMut},
compio_driver::op::{ReadVectoredAt, WriteVectoredAt},
};
use crate::{Metadata, OpenOptions, Permissions};
#[derive(Debug, Clone)]
pub struct File {
pub(crate) inner: AsyncFd<std::fs::File>,
}
impl File {
pub(crate) fn from_std(file: std::fs::File) -> io::Result<Self> {
Ok(Self {
inner: AsyncFd::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> {
crate::spawn_blocking_with(self.to_shared_fd(), |file| {
file.metadata().map(Metadata::from_std)
})
.await
}
#[cfg(windows)]
pub async fn set_len(&self, size: u64) -> io::Result<()> {
crate::spawn_blocking_with(self.to_shared_fd(), move |file| file.set_len(size)).await
}
#[cfg(unix)]
pub async fn set_len(&self, size: u64) -> io::Result<()> {
use compio_driver::op::TruncateFile;
let op = TruncateFile::new(self.to_shared_fd(), size);
compio_runtime::submit(op).await.0.map(|_| ())
}
#[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<()> {
crate::spawn_blocking_with(self.to_shared_fd(), move |file| {
if let Some(p) = perm.0.original {
file.set_permissions(p)
} else {
let mut p = file.metadata()?.permissions();
p.set_readonly(perm.readonly());
file.set_permissions(p)
}
})
.await
}
#[cfg(unix)]
pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
crate::spawn_blocking_with(self.to_shared_fd(), |file| file.set_permissions(perm.0)).await
}
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);
let res = compio_runtime::submit(op).await.into_inner();
unsafe { res.map_advanced() }
}
#[cfg(all(unix, not(solarish)))]
async fn read_vectored_at<T: IoVectoredBufMut>(
&self,
buffer: T,
pos: u64,
) -> BufResult<usize, T> {
use compio_driver::op::VecBufResultExt;
let fd = self.inner.to_shared_fd();
let op = ReadVectoredAt::new(fd, pos, buffer);
let res = compio_runtime::submit(op).await.into_inner();
unsafe { res.map_vec_advanced() }
}
}
impl AsyncReadManagedAt for File {
type Buffer = BufferRef;
async fn read_managed_at(&self, len: usize, pos: u64) -> io::Result<Option<Self::Buffer>> {
let fd = self.inner.to_shared_fd();
let res = Runtime::with_current(|rt| {
let buffer_pool = rt.buffer_pool()?;
let op = ReadManagedAt::new(fd, pos, &buffer_pool, len)?;
io::Result::Ok(rt.submit(op))
})?
.await;
unsafe { res.take_buffer() }
}
}
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(all(unix, not(solarish)))]
#[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(all(unix, not(solarish)))]
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 Splittable for File {
type ReadHalf = File;
type WriteHalf = File;
fn split(self) -> (Self::ReadHalf, Self::WriteHalf) {
(self.clone(), self)
}
}
impl Splittable for &File {
type ReadHalf = File;
type WriteHalf = File;
fn split(self) -> (Self::ReadHalf, Self::WriteHalf) {
(self.clone(), self.clone())
}
}
impl Splittable for &mut File {
type ReadHalf = File;
type WriteHalf = File;
fn split(self) -> (Self::ReadHalf, Self::WriteHalf) {
(self.clone(), self.clone())
}
}
impl_raw_fd!(File, std::fs::File, inner, file);