use std::{
future::{ready, Future},
io::{Error, ErrorKind, Result, Seek as _},
mem::ManuallyDrop,
os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd},
path::Path,
};
use super::{Metadata, OpenOptions};
use crate::{
io::{Read, ReadAt, Seek, SeekFrom, Write, WriteAt},
runtime::syscall,
};
#[derive(Debug)]
pub struct File(OwnedFd);
impl File {
pub async fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
let path = path.as_ref();
OpenOptions::new().read(true).open(path).await
}
pub async fn create<P: AsRef<Path>>(path: P) -> Result<Self> {
OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(path)
.await
}
pub async fn metadata(&self) -> Result<Metadata> {
syscall::fstat(self.as_fd()).await.map(Metadata::from)
}
pub async fn set_len(&self, size: u64) -> Result<()> {
self.as_std(|file| file.set_len(size))
}
pub async fn sync_all(&self) -> Result<()> {
syscall::fsync(self.as_fd()).await
}
pub async fn sync_data(&self) -> Result<()> {
syscall::fdatasync(self.as_fd()).await
}
}
impl File {
fn as_std<F, R>(&self, f: F) -> R
where
F: Fn(&mut std::fs::File) -> R,
{
let fd = self.0.as_raw_fd();
let mut file = unsafe { ManuallyDrop::new(std::fs::File::from_raw_fd(fd)) };
f(&mut file)
}
}
#[doc(hidden)]
impl From<OwnedFd> for File {
fn from(fd: OwnedFd) -> Self {
Self(fd)
}
}
#[doc(hidden)]
impl AsFd for File {
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
}
impl AsRawFd for File {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl FromRawFd for File {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Self(OwnedFd::from_raw_fd(fd))
}
}
impl Seek for File {
type Seek = impl Future<Output = Result<u64>>;
fn seek(&mut self, pos: SeekFrom) -> Self::Seek {
ready(self.as_std(|file| file.seek(pos)))
}
}
impl Read for File {
type Read<'a> = impl Future<Output = Result<usize>> + 'a;
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::Read<'a> {
syscall::read(self.0.as_fd(), buf)
}
}
impl ReadAt for File {
type ReadAt<'a> = impl Future<Output = Result<usize>> + 'a;
fn read_at<'a>(&'a self, buf: &'a mut [u8], pos: u64) -> Self::ReadAt<'a> {
async move {
let pos = pos
.try_into()
.map_err(|e| Error::new(ErrorKind::InvalidInput, e))?;
syscall::pread(self.0.as_fd(), buf, pos).await
}
}
}
impl Write for File {
type Write<'a> = impl Future<Output = Result<usize>> + 'a;
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::Write<'a> {
syscall::write(self.0.as_fd(), buf)
}
}
impl WriteAt for File {
type WriteAt<'a> = impl Future<Output = Result<usize>> + 'a;
fn write_at<'a>(&'a self, buf: &'a [u8], pos: u64) -> Self::WriteAt<'a> {
async move {
let pos = pos
.try_into()
.map_err(|e| Error::new(ErrorKind::InvalidInput, e))?;
syscall::pwrite(self.0.as_fd(), buf, pos).await
}
}
}