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) -> std::io::Result { File::create(path) } pub async fn file_open(path: impl AsRef) -> std::io::Result { File::open(path) } } pub async fn file_create(path: impl AsRef) -> std::io::Result { File::create(path).await } pub async fn file_open(path: impl AsRef) -> std::io::Result { File::open(path).await } #[async_trait] pub trait LockReadExt: AsOpenFile { async fn try_lock_read_async(self) -> LockReadResult where Self: Sized + Send + Sync + 'static; } #[async_trait] pub trait LockWriteExt: AsOpenFile { async fn try_lock_write_async(self) -> LockWriteResult where Self: Sized + Send + Sync + 'static; } #[async_trait] impl LockReadExt for std::fs::File { async fn try_lock_read_async(self) -> LockReadResult 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 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 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 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 where Self: Sized; } #[async_trait] impl AsyncTryClone for std::fs::File { async fn async_try_clone(&self) -> std::io::Result where Self: Sized, { self.try_clone() } } #[async_trait] impl AsyncTryClone for tokio::fs::File { async fn async_try_clone(&self) -> std::io::Result where Self: Sized, { self.try_clone().await } } #[async_trait] pub trait AsOpenFileExt: AsOpenFile { async fn timeout_lock_read_async(self) -> LockReadResult where Self: Sized + Send + Sync + 'static; async fn timeout_lock_write_async(self) -> LockWriteResult where Self: Sized + Send + Sync + 'static; } const TIMEOUT: Duration = Duration::from_millis(1000); #[async_trait] impl AsOpenFileExt for T where T: AsOpenFile + LockRead + LockWrite + AsyncTryClone, { async fn timeout_lock_read_async(self) -> LockReadResult 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 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) } }