use std::{future::Future, io, path::PathBuf, pin::Pin, time::SystemTime};
use tokio::io::{AsyncRead, AsyncSeek};
pub trait Metadata: Send + 'static {
fn is_dir(&self) -> bool;
fn modified(&self) -> io::Result<SystemTime>;
fn len(&self) -> u64;
fn is_empty(&self) -> bool {
self.len() == 0
}
}
pub trait File: AsyncRead + AsyncSeek + Unpin + Send + Sync {
type Metadata: Metadata;
type MetadataFuture<'a>: Future<Output = io::Result<Self::Metadata>> + Send
where
Self: 'a;
fn metadata(&self) -> Self::MetadataFuture<'_>;
}
pub trait Backend: Clone + Send + Sync + 'static {
type File: File<Metadata = Self::Metadata>;
type Metadata: Metadata;
type OpenFuture: Future<Output = io::Result<Self::File>> + Send;
type MetadataFuture: Future<Output = io::Result<Self::Metadata>> + Send;
fn open(&self, path: PathBuf) -> Self::OpenFuture;
fn metadata(&self, path: PathBuf) -> Self::MetadataFuture;
}
#[derive(Clone, Debug, Default)]
pub struct TokioBackend;
impl Backend for TokioBackend {
type File = TokioFile;
type Metadata = std::fs::Metadata;
type OpenFuture = Pin<Box<dyn Future<Output = io::Result<TokioFile>> + Send>>;
type MetadataFuture = Pin<Box<dyn Future<Output = io::Result<std::fs::Metadata>> + Send>>;
fn open(&self, path: PathBuf) -> Self::OpenFuture {
Box::pin(async move {
let file = tokio::fs::File::open(&path).await?;
Ok(TokioFile(file))
})
}
fn metadata(&self, path: PathBuf) -> Self::MetadataFuture {
Box::pin(async move { tokio::fs::metadata(&path).await })
}
}
#[derive(Debug)]
pub struct TokioFile(tokio::fs::File);
impl AsyncRead for TokioFile {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> std::task::Poll<io::Result<()>> {
Pin::new(&mut self.0).poll_read(cx, buf)
}
}
impl AsyncSeek for TokioFile {
fn start_seek(mut self: Pin<&mut Self>, position: io::SeekFrom) -> io::Result<()> {
Pin::new(&mut self.0).start_seek(position)
}
fn poll_complete(
mut self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<io::Result<u64>> {
Pin::new(&mut self.0).poll_complete(cx)
}
}
impl File for TokioFile {
type Metadata = std::fs::Metadata;
type MetadataFuture<'a> =
Pin<Box<dyn Future<Output = io::Result<std::fs::Metadata>> + Send + 'a>>;
fn metadata(&self) -> Self::MetadataFuture<'_> {
Box::pin(async move { self.0.metadata().await })
}
}
impl Metadata for std::fs::Metadata {
fn is_dir(&self) -> bool {
self.is_dir()
}
fn modified(&self) -> io::Result<SystemTime> {
self.modified()
}
fn len(&self) -> u64 {
self.len()
}
}