use async_fd_lock::{
AsOpenFile, LockError, LockRead, LockReadResult, LockWrite, LockWriteResult,
};
use async_trait::async_trait;
use std::io::ErrorKind;
use std::path::Path;
use std::time::Duration;
use tokio::fs::File;
use tokio::time;
use tokio::time::error::Elapsed;
pub mod blocking {
pub use async_fd_lock::blocking::*;
pub use std::fs::File;
use std::path::Path;
pub async fn file_create(path: impl AsRef<Path>) -> std::io::Result<File> {
File::create(path)
}
pub async fn file_open(path: impl AsRef<Path>) -> std::io::Result<File> {
File::open(path)
}
}
pub async fn file_create(path: impl AsRef<Path>) -> std::io::Result<File> {
File::create(path).await
}
pub async fn file_open(path: impl AsRef<Path>) -> std::io::Result<File> {
File::open(path).await
}
#[async_trait]
pub trait LockReadExt: AsOpenFile {
async fn try_lock_read_async(self) -> LockReadResult<Self>
where
Self: Sized + Send + Sync + 'static;
}
#[async_trait]
pub trait LockWriteExt: AsOpenFile {
async fn try_lock_write_async(self) -> LockWriteResult<Self>
where
Self: Sized + Send + Sync + 'static;
}
#[async_trait]
impl LockReadExt for std::fs::File {
async fn try_lock_read_async(self) -> LockReadResult<Self>
where
Self: Sized + Send + Sync + 'static,
{
blocking::LockRead::try_lock_read(self)
}
}
#[async_trait]
impl LockWriteExt for std::fs::File {
async fn try_lock_write_async(self) -> LockWriteResult<Self>
where
Self: Sized + Send + Sync + 'static,
{
blocking::LockWrite::try_lock_write(self)
}
}
#[async_trait]
impl LockReadExt for tokio::fs::File {
async fn try_lock_read_async(self) -> LockReadResult<Self>
where
Self: Sized + Send + Sync + 'static,
{
LockRead::try_lock_read(self).await
}
}
#[async_trait]
impl LockWriteExt for tokio::fs::File {
async fn try_lock_write_async(self) -> LockWriteResult<Self>
where
Self: Sized + Send + Sync + 'static,
{
LockWrite::try_lock_write(self).await
}
}
#[async_trait]
pub trait AsyncTryClone {
async fn async_try_clone(&self) -> std::io::Result<Self>
where
Self: Sized;
}
#[async_trait]
impl AsyncTryClone for std::fs::File {
async fn async_try_clone(&self) -> std::io::Result<Self>
where
Self: Sized,
{
self.try_clone()
}
}
#[async_trait]
impl AsyncTryClone for tokio::fs::File {
async fn async_try_clone(&self) -> std::io::Result<Self>
where
Self: Sized,
{
self.try_clone().await
}
}
#[async_trait]
pub trait AsOpenFileExt: AsOpenFile {
async fn timeout_lock_read_async(self) -> LockReadResult<Self>
where
Self: Sized + Send + Sync + 'static;
async fn timeout_lock_write_async(self) -> LockWriteResult<Self>
where
Self: Sized + Send + Sync + 'static;
}
const TIMEOUT: Duration = Duration::from_millis(1000);
#[async_trait]
impl<T> AsOpenFileExt for T
where
T: AsOpenFile + LockRead + LockWrite + AsyncTryClone,
{
async fn timeout_lock_read_async(self) -> LockReadResult<Self>
where
T: Sized + Send + Sync + 'static,
{
let clone = match self.async_try_clone().await {
Ok(clone) => clone,
Err(error) => return Err(LockError::new(self, error)),
};
time::timeout(TIMEOUT, self.lock_read())
.await
.map_err(move |_: Elapsed| LockError::new(clone, ErrorKind::WouldBlock.into()))
.and_then(std::convert::identity)
}
async fn timeout_lock_write_async(self) -> LockWriteResult<Self>
where
T: Sized + Send + Sync + 'static,
{
let clone = match self.async_try_clone().await {
Ok(clone) => clone,
Err(error) => return Err(LockError::new(self, error)),
};
time::timeout(TIMEOUT, self.lock_write())
.await
.map_err(move |_: Elapsed| LockError::new(clone, ErrorKind::WouldBlock.into()))
.and_then(std::convert::identity)
}
}