use std::{
fs::{FileType, Metadata, Permissions},
io,
task::Poll,
};
use futures::{AsyncRead, AsyncSeek, AsyncWrite, Stream};
use rasi_syscall::{global_filesystem, FileOpenMode, Handle};
use crate::utils::cancelable_would_block;
use rasi_syscall::path::{Path, PathBuf};
pub struct FileSystem {
syscall: &'static dyn rasi_syscall::FileSystem,
}
impl FileSystem {
pub fn new() -> Self {
FileSystem {
syscall: global_filesystem(),
}
}
pub fn new_with(syscall: &'static dyn rasi_syscall::FileSystem) -> Self {
FileSystem { syscall }
}
#[inline]
pub async fn canonicalize<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf> {
cancelable_would_block(|cx| self.syscall.canonicalize(cx.waker().clone(), path.as_ref()))
.await
}
pub async fn copy<P: AsRef<Path>, Q: AsRef<Path>>(&self, from: P, to: Q) -> io::Result<u64> {
cancelable_would_block(|cx| {
self.syscall
.copy(cx.waker().clone(), from.as_ref(), to.as_ref())
})
.await
}
#[inline]
pub async fn create_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
cancelable_would_block(|cx| self.syscall.create_dir(cx.waker().clone(), path.as_ref()))
.await
}
pub async fn create_dir_all<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
cancelable_would_block(|cx| {
self.syscall
.create_dir_all(cx.waker().clone(), path.as_ref())
})
.await
}
pub async fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(
&self,
from: P,
to: Q,
) -> io::Result<()> {
cancelable_would_block(|cx| {
self.syscall
.hard_link(cx.waker().clone(), from.as_ref(), to.as_ref())
})
.await
}
#[inline]
pub async fn metadata<P: AsRef<Path>>(&self, path: P) -> io::Result<Metadata> {
cancelable_would_block(|cx| self.syscall.metadata(cx.waker().clone(), path.as_ref())).await
}
#[inline]
pub async fn read_link<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf> {
cancelable_would_block(|cx| self.syscall.read_link(cx.waker().clone(), path.as_ref())).await
}
#[inline]
pub async fn remove_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
cancelable_would_block(|cx| self.syscall.remove_dir(cx.waker().clone(), path.as_ref()))
.await
}
#[inline]
pub async fn remove_dir_all<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
cancelable_would_block(|cx| {
self.syscall
.remove_dir_all(cx.waker().clone(), path.as_ref())
})
.await
}
#[inline]
pub async fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
cancelable_would_block(|cx| self.syscall.remove_file(cx.waker().clone(), path.as_ref()))
.await
}
#[inline]
pub async fn rename<P: AsRef<Path>, Q: AsRef<Path>>(&self, from: P, to: Q) -> io::Result<()> {
cancelable_would_block(|cx| {
self.syscall
.rename(cx.waker().clone(), from.as_ref(), to.as_ref())
})
.await
}
#[inline]
pub async fn set_permissions<P: AsRef<Path>>(
&self,
path: P,
perm: Permissions,
) -> io::Result<()> {
cancelable_would_block(|cx| {
self.syscall
.set_permissions(cx.waker().clone(), path.as_ref(), &perm)
})
.await
}
#[inline]
pub async fn symlink_metadata<P: AsRef<Path>>(&self, path: P) -> io::Result<Metadata> {
cancelable_would_block(|cx| {
self.syscall
.symlink_metadata(cx.waker().clone(), path.as_ref())
})
.await
}
#[inline]
pub async fn read_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<ReadDir> {
let handle =
cancelable_would_block(|cx| self.syscall.read_dir(cx.waker().clone(), path.as_ref()))
.await?;
Ok(ReadDir {
handle,
syscall: self.syscall,
cancel_handle: None,
})
}
pub async fn open_file<P: AsRef<Path>>(
&self,
path: P,
open_mode: FileOpenMode,
) -> io::Result<File> {
let handle = cancelable_would_block(|cx| {
self.syscall
.open_file(cx.waker().clone(), path.as_ref(), &open_mode)
})
.await?;
Ok(File::new(handle, self.syscall))
}
pub async fn exists<P: AsRef<Path>>(&self, path: P) -> bool {
self.metadata(path).await.is_ok()
}
pub async fn is_dir<P: AsRef<Path>>(&self, path: P) -> bool {
self.metadata(path)
.await
.map(|m| m.is_dir())
.unwrap_or(false)
}
pub async fn is_file<P: AsRef<Path>>(&self, path: P) -> bool {
self.metadata(path)
.await
.map(|m| m.is_file())
.unwrap_or(false)
}
}
pub struct File {
handle: Handle,
syscall: &'static dyn rasi_syscall::FileSystem,
write_cancel_handle: Option<Handle>,
read_cancel_handle: Option<Handle>,
}
impl File {
fn new(handle: Handle, syscall: &'static dyn rasi_syscall::FileSystem) -> Self {
Self {
handle,
syscall,
write_cancel_handle: None,
read_cancel_handle: None,
}
}
pub async fn metadata(&self) -> io::Result<Metadata> {
cancelable_would_block(|cx| self.syscall.file_meta(cx.waker().clone(), &self.handle)).await
}
pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
cancelable_would_block(|cx| {
self.syscall
.file_set_permissions(cx.waker().clone(), &self.handle, &perm)
})
.await
}
pub async fn set_len(&self, size: u64) -> io::Result<()> {
cancelable_would_block(|cx| {
self.syscall
.file_set_len(cx.waker().clone(), &self.handle, size)
})
.await
}
}
impl AsyncWrite for File {
fn poll_write(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
match self
.syscall
.file_write(cx.waker().clone(), &self.handle, buf)
{
rasi_syscall::CancelablePoll::Ready(r) => Poll::Ready(r),
rasi_syscall::CancelablePoll::Pending(write_cancel_handle) => {
self.write_cancel_handle = Some(write_cancel_handle);
Poll::Pending
}
}
}
fn poll_flush(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> Poll<io::Result<()>> {
match self.syscall.file_flush(cx.waker().clone(), &self.handle) {
rasi_syscall::CancelablePoll::Ready(r) => Poll::Ready(r),
rasi_syscall::CancelablePoll::Pending(write_cancel_handle) => {
self.write_cancel_handle = Some(write_cancel_handle);
Poll::Pending
}
}
}
fn poll_close(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> Poll<io::Result<()>> {
self.poll_flush(cx)
}
}
impl AsyncRead for File {
fn poll_read(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
match self
.syscall
.file_read(cx.waker().clone(), &self.handle, buf)
{
rasi_syscall::CancelablePoll::Ready(r) => Poll::Ready(r),
rasi_syscall::CancelablePoll::Pending(write_cancel_handle) => {
self.write_cancel_handle = Some(write_cancel_handle);
Poll::Pending
}
}
}
}
impl AsyncSeek for File {
fn poll_seek(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
pos: io::SeekFrom,
) -> Poll<io::Result<u64>> {
match self
.syscall
.file_seek(cx.waker().clone(), &self.handle, pos)
{
rasi_syscall::CancelablePoll::Ready(r) => Poll::Ready(r),
rasi_syscall::CancelablePoll::Pending(read_cancel_handle) => {
self.read_cancel_handle = Some(read_cancel_handle);
Poll::Pending
}
}
}
}
pub struct ReadDir {
handle: Handle,
syscall: &'static dyn rasi_syscall::FileSystem,
cancel_handle: Option<Handle>,
}
impl Stream for ReadDir {
type Item = io::Result<DirEntry>;
fn poll_next(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Option<Self::Item>> {
match self
.syscall
.dir_entry_next(cx.waker().clone(), &self.handle)
{
rasi_syscall::CancelablePoll::Ready(Ok(r)) => Poll::Ready(r.map(|h| {
Ok(DirEntry {
handle: h,
syscall: self.syscall,
})
})),
rasi_syscall::CancelablePoll::Ready(Err(err)) => Poll::Ready(Some(Err(err))),
rasi_syscall::CancelablePoll::Pending(cancel_handle) => {
self.cancel_handle = Some(cancel_handle);
Poll::Pending
}
}
}
}
pub struct DirEntry {
handle: Handle,
syscall: &'static dyn rasi_syscall::FileSystem,
}
impl DirEntry {
pub fn path(&self) -> PathBuf {
self.syscall.dir_entry_path(&self.handle)
}
pub async fn metadata(&self) -> io::Result<Metadata> {
cancelable_would_block(|cx| {
self.syscall
.dir_entry_metadata(cx.waker().clone(), &self.handle)
})
.await
}
pub async fn file_type(&self) -> io::Result<FileType> {
cancelable_would_block(|cx| {
self.syscall
.dir_entry_file_type(cx.waker().clone(), &self.handle)
})
.await
}
pub fn file_name(&self) -> String {
self.syscall.dir_entry_file_name(&self.handle)
}
}
#[inline]
pub async fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
FileSystem::new().canonicalize(path).await
}
#[inline]
pub async fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
FileSystem::new().copy(from, to).await
}
#[inline]
pub async fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
FileSystem::new().create_dir(path).await
}
#[inline]
pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
FileSystem::new().create_dir_all(path).await
}
#[inline]
pub async fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
FileSystem::new().hard_link(from, to).await
}
#[inline]
pub async fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
FileSystem::new().metadata(path).await
}
#[inline]
pub async fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
FileSystem::new().read_link(path).await
}
#[inline]
pub async fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
FileSystem::new().remove_dir(path).await
}
#[inline]
pub async fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
FileSystem::new().remove_dir_all(path).await
}
#[inline]
pub async fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
FileSystem::new().remove_file(path).await
}
#[inline]
pub async fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
FileSystem::new().rename(from, to).await
}
#[inline]
pub async fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> {
FileSystem::new().set_permissions(path, perm).await
}
#[inline]
pub async fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
FileSystem::new().symlink_metadata(path).await
}
#[inline]
pub async fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
FileSystem::new().read_dir(path).await
}
pub async fn open_file<P: AsRef<Path>>(path: P, open_mode: FileOpenMode) -> io::Result<File> {
FileSystem::new().open_file(path, open_mode).await
}
pub async fn exists<P: AsRef<Path>>(path: P) -> bool {
FileSystem::new().exists(path).await
}
pub async fn is_dir<P: AsRef<Path>>(path: P) -> bool {
FileSystem::new().is_dir(path).await
}
pub async fn is_file<P: AsRef<Path>>(path: P) -> bool {
FileSystem::new().is_file(path).await
}
#[derive(Clone, Debug)]
pub struct OpenOptions(FileOpenMode);
impl OpenOptions {
pub fn new() -> OpenOptions {
OpenOptions(FileOpenMode::none())
}
pub fn read(&mut self, read: bool) -> &mut OpenOptions {
if read {
self.0 = self.0.xor(FileOpenMode::Readable);
} else {
self.0 = self.0.and(FileOpenMode::Readable);
}
self
}
pub fn write(&mut self, write: bool) -> &mut OpenOptions {
if write {
self.0 = self.0.xor(FileOpenMode::Writable);
} else {
self.0 = self.0.and(FileOpenMode::Writable);
}
self
}
pub fn append(&mut self, append: bool) -> &mut OpenOptions {
if append {
self.0 = self.0.xor(FileOpenMode::Append);
} else {
self.0 = self.0.and(FileOpenMode::Append);
}
self
}
pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
if truncate {
self.0 = self.0.xor(FileOpenMode::Truncate);
} else {
self.0 = self.0.and(FileOpenMode::Truncate);
}
self
}
pub fn create(&mut self, create: bool) -> &mut OpenOptions {
if create {
self.0 = self.0.xor(FileOpenMode::Create);
} else {
self.0 = self.0.and(FileOpenMode::Create);
}
self
}
pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions {
if create_new {
self.0 = self.0.xor(FileOpenMode::CreateNew);
} else {
self.0 = self.0.and(FileOpenMode::CreateNew);
}
self
}
pub async fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
let path = path.as_ref().to_owned();
open_file(path, self.0).await
}
pub async fn open_with<P: AsRef<Path>>(
&self,
path: P,
syscall: &'static dyn rasi_syscall::FileSystem,
) -> io::Result<File> {
let path = path.as_ref().to_owned();
FileSystem::new_with(syscall).open_file(path, self.0).await
}
}
impl Default for OpenOptions {
fn default() -> Self {
Self::new()
}
}
#[cfg(all(windows, feature = "windows_named_pipe"))]
mod windows {
use std::{
ffi::{OsStr, OsString},
io,
task::Poll,
};
use futures::{AsyncRead, AsyncWrite};
use rasi_syscall::{global_named_pipe, Handle, NamedPipe};
use crate::utils::cancelable_would_block;
pub struct NamedPipeListener {
addr: OsString,
syscall: &'static dyn NamedPipe,
next_handle: Option<Handle>,
}
impl NamedPipeListener {
pub async fn bind_with<A: AsRef<OsStr>>(
addr: A,
syscall: &'static dyn NamedPipe,
) -> io::Result<Self> {
let socket = cancelable_would_block(|cx| {
syscall.server_create(cx.waker().clone(), addr.as_ref())
})
.await?;
Ok(Self {
addr: addr.as_ref().to_os_string(),
syscall,
next_handle: Some(socket),
})
}
pub async fn bind<A: AsRef<OsStr>>(addr: A) -> io::Result<Self> {
Self::bind_with(addr, global_named_pipe()).await
}
pub async fn accept(&mut self) -> io::Result<NamedPipeStream> {
let socket = self.next_handle.take().expect("");
let stream = NamedPipeStream::new(socket, self.syscall);
cancelable_would_block(|cx| {
self.syscall
.server_accept(cx.waker().clone(), &stream.socket)
})
.await?;
let next_socket = cancelable_would_block(|cx| {
self.syscall.server_create(cx.waker().clone(), &self.addr)
})
.await?;
self.next_handle = Some(next_socket);
Ok(stream)
}
}
pub struct NamedPipeStream {
socket: Handle,
syscall: &'static dyn NamedPipe,
write_cancel_handle: Option<Handle>,
read_cancel_handle: Option<Handle>,
}
impl NamedPipeStream {
fn new(socket: Handle, syscall: &'static dyn NamedPipe) -> Self {
Self {
socket,
syscall,
write_cancel_handle: None,
read_cancel_handle: None,
}
}
pub async fn connect_with<A: AsRef<OsStr>>(
addr: A,
syscall: &'static dyn NamedPipe,
) -> io::Result<Self> {
let socket =
cancelable_would_block(|cx| syscall.client_open(cx.waker().clone(), addr.as_ref()))
.await?;
Ok(Self::new(socket, syscall))
}
pub async fn connect<A: AsRef<OsStr>>(addr: A) -> io::Result<Self> {
Self::connect_with(addr, global_named_pipe()).await
}
}
impl AsyncRead for NamedPipeStream {
fn poll_read(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &mut [u8],
) -> std::task::Poll<io::Result<usize>> {
match self.syscall.read(cx.waker().clone(), &self.socket, buf) {
rasi_syscall::CancelablePoll::Ready(r) => Poll::Ready(r),
rasi_syscall::CancelablePoll::Pending(read_cancel_handle) => {
self.read_cancel_handle = Some(read_cancel_handle);
Poll::Pending
}
}
}
}
impl AsyncWrite for NamedPipeStream {
fn poll_write(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
match self.syscall.write(cx.waker().clone(), &self.socket, buf) {
rasi_syscall::CancelablePoll::Ready(r) => Poll::Ready(r),
rasi_syscall::CancelablePoll::Pending(write_cancel_handle) => {
self.write_cancel_handle = Some(write_cancel_handle);
Poll::Pending
}
}
}
fn poll_flush(
self: std::pin::Pin<&mut Self>,
_cx: &mut std::task::Context<'_>,
) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_close(
self: std::pin::Pin<&mut Self>,
_cx: &mut std::task::Context<'_>,
) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
}
}
#[cfg(all(windows, feature = "windows_named_pipe"))]
pub use windows::*;