use async_trait::async_trait;
use std::io;
use std::io::SeekFrom;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
#[derive(Debug, Clone, Default)]
pub struct OpenOptions {
pub read: bool,
pub write: bool,
pub create: bool,
pub create_new: bool,
pub truncate: bool,
pub append: bool,
}
impl OpenOptions {
pub fn new() -> Self {
Self::default()
}
pub fn read(mut self, read: bool) -> Self {
self.read = read;
self
}
pub fn write(mut self, write: bool) -> Self {
self.write = write;
self
}
pub fn create(mut self, create: bool) -> Self {
self.create = create;
self
}
pub fn create_new(mut self, create_new: bool) -> Self {
self.create_new = create_new;
self
}
pub fn truncate(mut self, truncate: bool) -> Self {
self.truncate = truncate;
self
}
pub fn append(mut self, append: bool) -> Self {
self.append = append;
self
}
pub fn read_only() -> Self {
Self::new().read(true)
}
pub fn create_write() -> Self {
Self::new().write(true).create(true).truncate(true)
}
pub fn read_write() -> Self {
Self::new().read(true).write(true)
}
pub fn create_new_write() -> Self {
Self::new().write(true).create_new(true)
}
}
#[async_trait(?Send)]
pub trait StorageProvider: Clone {
type File: StorageFile + 'static;
async fn open(&self, path: &str, options: OpenOptions) -> io::Result<Self::File>;
async fn exists(&self, path: &str) -> io::Result<bool>;
async fn delete(&self, path: &str) -> io::Result<()>;
async fn rename(&self, from: &str, to: &str) -> io::Result<()>;
}
#[async_trait(?Send)]
pub trait StorageFile: AsyncRead + AsyncWrite + AsyncSeek + Unpin {
async fn sync_all(&self) -> io::Result<()>;
async fn sync_data(&self) -> io::Result<()>;
async fn size(&self) -> io::Result<u64>;
async fn set_len(&self, size: u64) -> io::Result<()>;
}
#[derive(Debug, Clone)]
pub struct TokioStorageProvider;
impl TokioStorageProvider {
pub fn new() -> Self {
Self
}
}
impl Default for TokioStorageProvider {
fn default() -> Self {
Self::new()
}
}
#[async_trait(?Send)]
impl StorageProvider for TokioStorageProvider {
type File = TokioStorageFile;
async fn open(&self, path: &str, options: OpenOptions) -> io::Result<Self::File> {
let file = tokio::fs::OpenOptions::new()
.read(options.read)
.write(options.write)
.create(options.create)
.create_new(options.create_new)
.truncate(options.truncate)
.append(options.append)
.open(path)
.await?;
Ok(TokioStorageFile { inner: file })
}
async fn exists(&self, path: &str) -> io::Result<bool> {
match tokio::fs::metadata(path).await {
Ok(_) => Ok(true),
Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(false),
Err(e) => Err(e),
}
}
async fn delete(&self, path: &str) -> io::Result<()> {
tokio::fs::remove_file(path).await
}
async fn rename(&self, from: &str, to: &str) -> io::Result<()> {
tokio::fs::rename(from, to).await
}
}
#[derive(Debug)]
pub struct TokioStorageFile {
inner: tokio::fs::File,
}
#[async_trait(?Send)]
impl StorageFile for TokioStorageFile {
async fn sync_all(&self) -> io::Result<()> {
self.inner.sync_all().await
}
async fn sync_data(&self) -> io::Result<()> {
self.inner.sync_data().await
}
async fn size(&self) -> io::Result<u64> {
let metadata = self.inner.metadata().await?;
Ok(metadata.len())
}
async fn set_len(&self, size: u64) -> io::Result<()> {
self.inner.set_len(size).await
}
}
impl AsyncRead for TokioStorageFile {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
Pin::new(&mut self.inner).poll_read(cx, buf)
}
}
impl AsyncWrite for TokioStorageFile {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.inner).poll_write(cx, buf)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.inner).poll_flush(cx)
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.inner).poll_shutdown(cx)
}
}
impl AsyncSeek for TokioStorageFile {
fn start_seek(mut self: Pin<&mut Self>, position: SeekFrom) -> io::Result<()> {
Pin::new(&mut self.inner).start_seek(position)
}
fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
Pin::new(&mut self.inner).poll_complete(cx)
}
}