use crate::fs::{as_sync, Metadata, Permissions};
#[cfg(unix)]
use async_std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(target_os = "wasi")]
use async_std::os::wasi::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(windows)]
use async_std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
use async_std::{
fs, io,
task::{Context, Poll},
};
use std::{fmt, pin::Pin};
pub struct File {
pub(crate) std: fs::File,
}
impl File {
#[inline]
pub fn from_std(std: fs::File) -> Self {
Self { std }
}
#[inline]
pub async fn sync_all(&self) -> io::Result<()> {
self.std.sync_all().await
}
#[inline]
pub async fn sync_data(&self) -> io::Result<()> {
self.std.sync_data().await
}
#[inline]
pub async fn set_len(&self, size: u64) -> io::Result<()> {
self.std.set_len(size).await
}
#[inline]
pub async fn metadata(&self) -> io::Result<Metadata> {
self.std.metadata().await.map(metadata_from_std)
}
#[inline]
pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
let sync = unsafe { as_sync(&self.std) };
self.std
.set_permissions(permissions_into_std(&sync, perm)?)
.await
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
fn metadata_from_std(metadata: fs::Metadata) -> Metadata {
Metadata::from_std(metadata)
}
#[cfg(target_os = "wasi")]
#[inline]
fn metadata_from_std(metadata: fs::Metadata) -> Metadata {
metadata
}
#[cfg(not(target_os = "wasi"))]
#[inline]
fn permissions_into_std(
file: &std::fs::File,
permissions: Permissions,
) -> io::Result<fs::Permissions> {
permissions.into_std(file)
}
#[cfg(target_os = "wasi")]
#[inline]
fn permissions_into_std(
_file: &std::fs::File,
permissions: Permissions,
) -> io::Result<fs::Permissions> {
permissions
}
#[cfg(any(unix, target_os = "wasi"))]
impl FromRawFd for File {
#[inline]
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Self::from_std(fs::File::from_raw_fd(fd))
}
}
#[cfg(windows)]
impl FromRawHandle for File {
#[inline]
unsafe fn from_raw_handle(handle: RawHandle) -> Self {
Self::from_std(fs::File::from_raw_handle(handle))
}
}
#[cfg(any(unix, target_os = "wasi"))]
impl AsRawFd for File {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.std.as_raw_fd()
}
}
#[cfg(windows)]
impl AsRawHandle for File {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
self.std.as_raw_handle()
}
}
#[cfg(any(unix, target_os = "wasi"))]
impl IntoRawFd for File {
#[inline]
fn into_raw_fd(self) -> RawFd {
self.std.into_raw_fd()
}
}
#[cfg(windows)]
impl IntoRawHandle for File {
#[inline]
fn into_raw_handle(self) -> RawHandle {
self.std.into_raw_handle()
}
}
impl io::Read for File {
#[inline]
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
io::Read::poll_read(Pin::new(&mut self.std), cx, buf)
}
#[inline]
fn poll_read_vectored(
mut self: Pin<&mut Self>,
cx: &mut Context,
bufs: &mut [io::IoSliceMut],
) -> Poll<io::Result<usize>> {
io::Read::poll_read_vectored(Pin::new(&mut self.std), cx, bufs)
}
}
impl io::Write for File {
#[inline]
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context,
buf: &[u8],
) -> Poll<io::Result<usize>> {
io::Write::poll_write(Pin::new(&mut self.std), cx, buf)
}
#[inline]
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
io::Write::poll_flush(Pin::new(&mut self.std), cx)
}
#[inline]
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
io::Write::poll_close(Pin::new(&mut self.std), cx)
}
#[inline]
fn poll_write_vectored(
mut self: Pin<&mut Self>,
cx: &mut Context,
bufs: &[io::IoSlice],
) -> Poll<io::Result<usize>> {
io::Write::poll_write_vectored(Pin::new(&mut self.std), cx, bufs)
}
}
impl io::Seek for File {
#[inline]
fn poll_seek(
mut self: Pin<&mut Self>,
cx: &mut Context,
pos: io::SeekFrom,
) -> Poll<io::Result<u64>> {
io::Seek::poll_seek(Pin::new(&mut self.std), cx, pos)
}
}
impl fmt::Debug for File {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut b = f.debug_struct("File");
fmt_debug_file(&self.std, &mut b);
b.finish()
}
}
#[cfg(any(unix, target_os = "wasi", target_os = "fuchsia"))]
fn fmt_debug_file(fd: &impl AsRawFd, b: &mut fmt::DebugStruct) {
unsafe fn get_mode(fd: RawFd) -> Option<(bool, bool)> {
let mode = yanix::fcntl::get_status_flags(fd);
if mode.is_err() {
return None;
}
match mode.unwrap() & yanix::file::OFlags::ACCMODE {
yanix::file::OFlags::RDONLY => Some((true, false)),
yanix::file::OFlags::RDWR => Some((true, true)),
yanix::file::OFlags::WRONLY => Some((false, true)),
_ => None,
}
}
let fd = fd.as_raw_fd();
b.field("fd", &fd);
if let Some((read, write)) = unsafe { get_mode(fd) } {
b.field("read", &read).field("write", &write);
}
}
#[cfg(windows)]
fn fmt_debug_file(fd: &impl AsRawHandle, b: &mut fmt::DebugStruct) {
b.field("TODO fill in the blanks", &fd.as_raw_handle());
}