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::{
ToSharedFd, impl_raw_fd,
op::{
AsyncifyFd, BufResultExt, CloseFile, ReadAt, ReadManagedAt, ResultTakeBuffer, Sync, WriteAt,
},
};
use compio_io::{AsyncReadAt, AsyncReadManagedAt, AsyncWriteAt, util::Splittable};
use compio_runtime::{Attacher, BorrowedBuffer, BufferPool};
#[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 {
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 op = AsyncifyFd::new(self.to_shared_fd(), |file: &std::fs::File| {
match file.metadata().map(Metadata::from_std) {
Ok(meta) => BufResult(Ok(0), Some(meta)),
Err(e) => BufResult(Err(e), None),
}
});
let BufResult(res, meta) = compio_runtime::submit(op).await;
res.map(|_| meta.into_inner().expect("metadata should be present"))
}
#[cfg(windows)]
pub async fn set_len(&self, size: u64) -> io::Result<()> {
let op = AsyncifyFd::new(self.to_shared_fd(), move |file: &std::fs::File| {
BufResult(file.set_len(size).map(|_| 0), ())
});
compio_runtime::submit(op).await.0.map(|_| ())
}
#[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()))
}
pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
let op = AsyncifyFd::new(self.to_shared_fd(), move |file: &std::fs::File| {
BufResult(file.set_permissions(perm.0).map(|_| 0), ())
});
compio_runtime::submit(op).await.0.map(|_| ())
}
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<'a> = BorrowedBuffer<'a>;
type BufferPool = BufferPool;
async fn read_managed_at<'a>(
&self,
buffer_pool: &'a Self::BufferPool,
len: usize,
pos: u64,
) -> io::Result<Self::Buffer<'a>> {
let fd = self.inner.to_shared_fd();
let buffer_pool = buffer_pool.try_inner()?;
let op = ReadManagedAt::new(fd, pos, buffer_pool, len)?;
compio_runtime::submit(op)
.with_extra()
.await
.take_buffer(buffer_pool)
}
}
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_raw_fd!(File, std::fs::File, inner, file);