use std::io;
use std::pin::Pin;
use std::task::Context;
use std::task::Poll;
use futures::AsyncWrite;
use futures::Stream;
use futures::io::AsyncRead;
use futures::io::AsyncSeek;
use pin_project::pin_project;
use crate::error::Error;
use crate::error::Result;
use crate::error::VfsResult;
use crate::prelude::*;
use crate::traits::vfs::DirEntryInfo;
use crate::traits::vfs::OwnedPathType;
use crate::traits::vfs::PathType;
use crate::traits::vfs::VfsCore;
pub trait VfsAsync: VfsCore + Send + Sync + Unpin {
type RFile: AsyncRead + Send + Unpin;
type OpenReadFuture: Future<Output = VfsResult<Self::RFile, Self>> + Send + Unpin;
fn open_read(
self: Pin<&Self>,
path: <<Self as VfsCore>::Path as PathType>::OwnedPath,
) -> Self::OpenReadFuture;
type ReadFuture<'a>: Future<Output = VfsResult<Vec<u8>, Self>> + Send + Unpin + 'a
where
Self: 'a;
fn read<'a>(
self: Pin<&'a Self>,
path: <<Self as VfsCore>::Path as PathType>::OwnedPath,
) -> Self::ReadFuture<'a>;
type ReadStringFuture<'a>: Future<Output = VfsResult<String, Self>> + Send + Unpin + 'a
where
Self: 'a;
fn read_string<'a>(
self: Pin<&'a Self>,
path: <<Self as VfsCore>::Path as PathType>::OwnedPath,
) -> Self::ReadStringFuture<'a>;
type ExistsFuture<'a>: Future<Output = VfsResult<bool, Self>> + Send + 'a
where
Self: 'a;
fn exists<'a>(
self: Pin<&'a Self>,
path: <<Self as VfsCore>::Path as PathType>::OwnedPath,
) -> Self::ExistsFuture<'a>;
type IsDirFuture<'a>: Future<Output = VfsResult<bool, Self>> + Send + 'a
where
Self: 'a;
fn is_dir<'a>(
self: Pin<&'a Self>,
path: <<Self as VfsCore>::Path as PathType>::OwnedPath,
) -> Self::IsDirFuture<'a>;
type DirWalk<'a>: Stream<Item = VfsResult<DirEntryInfo<<Self as VfsCore>::Path>, Self>>
+ Send
+ 'a
where
Self: 'a;
type DirWalkFuture<'a>: Future<Output = VfsResult<Self::DirWalk<'a>, Self>> + Send + 'a
where
Self: 'a;
fn walk_dir<'a>(
self: Pin<&'a Self>,
path: <<Self as VfsCore>::Path as PathType>::OwnedPath,
) -> Self::DirWalkFuture<'a>;
}
pub trait VfsAsyncWithSeekRead: VfsAsync
where
Self::RFile: AsyncSeek + Send + Unpin,
{
}
impl<T: VfsAsync> VfsAsyncWithSeekRead for T where T::RFile: AsyncSeek + Send + Unpin {}
pub trait VfsAsyncExt: VfsAsync {
fn read_typed_async_pinned<'a, T: ReadFromAsync<'a, Self>>(
self: Pin<&'a Self>,
path: impl Into<<<Self as VfsCore>::Path as PathType>::OwnedPath>,
) -> T::Future {
T::read_from_async(path.into(), self)
}
fn read_typed_async<'a, T: ReadFromAsync<'a, Self>>(
&'a self,
path: impl Into<<<Self as VfsCore>::Path as PathType>::OwnedPath>,
) -> T::Future {
Pin::new(self).read_typed_async_pinned::<T>(path)
}
}
impl<V: VfsAsync + ?Sized> VfsAsyncExt for V {}
pub trait WriteSupportingVfsAsync: VfsAsync {
type WFile: AsyncWrite + Send + Unpin;
type OpenWriteFuture: Future<Output = VfsResult<Self::WFile, Self>> + Send + Unpin;
fn open_write(
self: Pin<&Self>,
path: <<Self as VfsCore>::Path as PathType>::OwnedPath,
) -> Self::OpenWriteFuture;
type WriteFuture<'a>: Future<Output = VfsResult<(), Self>> + Send + Unpin + 'a
where
Self: 'a;
fn write<'d, 'a: 'd>(
self: Pin<&'a Self>,
path: <<Self as VfsCore>::Path as PathType>::OwnedPath,
data: &'d [u8],
) -> Self::WriteFuture<'d>;
type RemoveDirAllFuture<'a>: Future<Output = VfsResult<(), Self>> + Send + 'a
where
Self: 'a;
fn remove_dir_all<'a>(
self: Pin<&'a Self>,
path: <<Self as VfsCore>::Path as PathType>::OwnedPath,
) -> Self::RemoveDirAllFuture<'a>;
type CreateDirFuture<'a>: Future<Output = VfsResult<(), Self>> + Send + 'a
where
Self: 'a;
fn create_dir<'a>(
self: Pin<&'a Self>,
path: <<Self as VfsCore>::Path as PathType>::OwnedPath,
) -> Self::CreateDirFuture<'a>;
type CreateDirAllFuture<'a>: Future<Output = VfsResult<(), Self>> + Send + 'a
where
Self: 'a;
fn create_dir_all<'a>(
self: Pin<&'a Self>,
path: <<Self as VfsCore>::Path as PathType>::OwnedPath,
) -> Self::CreateDirAllFuture<'a>;
type CreateParentDirFuture<'a>: Future<Output = VfsResult<(), Self>> + Send + 'a
where
Self: 'a;
fn create_parent_dir<'a>(
self: Pin<&'a Self>,
path: <<Self as VfsCore>::Path as PathType>::OwnedPath,
) -> Self::CreateParentDirFuture<'a>;
}
pub trait VfsAsyncWithSeekWrite: WriteSupportingVfsAsync
where
Self::WFile: AsyncSeek + Send + Unpin,
{
}
impl<T: WriteSupportingVfsAsync> VfsAsyncWithSeekWrite for T where T::WFile: AsyncSeek + Send + Unpin
{}
pub trait WriteSupportingVfsAsyncExt: WriteSupportingVfsAsync {
fn write_typed_async_ref_pinned<'r, 'a: 'r, T: WriteToAsyncRef<'a, Self>>(
self: Pin<&'r Self>,
path: impl Into<<<Self as VfsCore>::Path as PathType>::OwnedPath>,
value: &'r T,
) -> T::Future<'r> {
T::write_to_async_ref(value, path.into(), self)
}
fn write_typed_async_ref<'r, 'a: 'r, T: WriteToAsyncRef<'a, Self>>(
&'r self,
path: impl Into<<<Self as VfsCore>::Path as PathType>::OwnedPath>,
data: &'r T,
) -> T::Future<'r>
where
Self: Unpin,
{
Pin::new(self).write_typed_async_ref_pinned(path, data)
}
fn write_typed_async_pinned<'a, T: WriteToAsync<'a, Self>>(
self: Pin<&'a Self>,
path: impl Into<<<Self as VfsCore>::Path as PathType>::OwnedPath>,
value: T,
) -> T::Future {
value.write_to_async(path.into(), self)
}
fn write_typed_async<'a, T: WriteToAsync<'a, Self>>(
&'a self,
path: impl Into<<<Self as VfsCore>::Path as PathType>::OwnedPath>,
value: T,
) -> T::Future
where
Self: Unpin,
{
Pin::new(self).write_typed_async_pinned(path, value)
}
}
impl<V: WriteSupportingVfsAsync + ?Sized> WriteSupportingVfsAsyncExt for V {}
#[pin_project(project_replace = CreateParentDirDefaultFutureProjOwn)]
#[doc(hidden)]
pub enum CreateParentDirDefaultFuture<'a, Vfs: WriteSupportingVfsAsync + 'a>
where
for<'f> Vfs::ExistsFuture<'f>: Future<Output = VfsResult<bool, Vfs>> + Unpin,
for<'f> Vfs::CreateDirAllFuture<'f>: Future<Output = VfsResult<(), Vfs>> + Unpin,
{
Poison,
Start {
vfs: Pin<&'a Vfs>,
path: <<Vfs as VfsCore>::Path as PathType>::OwnedPath,
},
ExistsFuture {
vfs: Pin<&'a Vfs>,
path: <<Vfs as VfsCore>::Path as PathType>::OwnedPath,
exists_future: Vfs::ExistsFuture<'a>,
},
CreateDirAllFuture {
vfs: Pin<&'a Vfs>,
create_dir_all_future: Vfs::CreateDirAllFuture<'a>,
},
}
impl<'a, Vfs: WriteSupportingVfsAsync + 'a> Future for CreateParentDirDefaultFuture<'a, Vfs>
where
for<'f> Vfs::ExistsFuture<'f>: Future<Output = VfsResult<bool, Vfs>> + Unpin,
for<'f> Vfs::CreateDirAllFuture<'f>: Future<Output = VfsResult<(), Vfs>> + Unpin,
{
type Output = VfsResult<(), Vfs>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.as_mut().project_replace(Self::Poison);
match this {
CreateParentDirDefaultFutureProjOwn::Start { vfs, path } => {
self.project_replace(Self::ExistsFuture {
exists_future: vfs.exists(path.clone()),
vfs,
path,
});
cx.waker().wake_by_ref();
Poll::Pending
}
CreateParentDirDefaultFutureProjOwn::ExistsFuture {
vfs,
path,
mut exists_future,
} => match Pin::new(&mut exists_future).poll(cx) {
Poll::Ready(Ok(true)) => Poll::Ready(Ok(())),
Poll::Ready(Ok(false)) => {
self.project_replace(Self::CreateDirAllFuture {
create_dir_all_future: vfs.create_dir_all(path),
vfs,
});
cx.waker().wake_by_ref();
Poll::Pending
}
Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
Poll::Pending => {
self.project_replace(Self::ExistsFuture {
vfs,
path,
exists_future,
});
Poll::Pending
}
},
CreateParentDirDefaultFutureProjOwn::CreateDirAllFuture {
vfs,
mut create_dir_all_future,
} => match Pin::new(&mut create_dir_all_future).poll(cx) {
Poll::Ready(Ok(())) => Poll::Ready(Ok(())),
Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
Poll::Pending => {
self.project_replace(Self::CreateDirAllFuture {
vfs,
create_dir_all_future,
});
Poll::Pending
}
},
CreateParentDirDefaultFutureProjOwn::Poison => {
panic!("CreateParentDirDefaultFuture polled after completion")
}
}
}
}
#[pin_project(project = IoErrorWrapperFutureProj)]
#[doc(hidden)]
pub struct IoErrorWrapperFuture<T, F: Future<Output = io::Result<T>>, P: OwnedPathType> {
#[pin]
future: F,
path: P,
}
impl<T, F, P> IoErrorWrapperFuture<T, F, P>
where
F: Future<Output = io::Result<T>>,
P: OwnedPathType,
{
pub fn new(path: P, future: F) -> Self {
Self { future, path }
}
}
impl<T, F, P> Future for IoErrorWrapperFuture<T, F, P>
where
F: Future<Output = io::Result<T>>,
P: OwnedPathType + Clone,
{
type Output = Result<T, P>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.as_mut().project();
match this.future.poll(cx) {
Poll::Ready(Ok(value)) => Poll::Ready(Ok(value)),
Poll::Ready(Err(e)) => {
let path = self.path.clone();
Poll::Ready(Err(Error::Io(path, e)))
}
Poll::Pending => Poll::Pending,
}
}
}