#![cfg_attr(docsrs, feature(doc_cfg))]
#![allow(unsafe_code)]
mod reader;
mod errors;
#[cfg_attr(docsrs, doc(cfg(feature = "async-tempfile")))]
#[cfg(feature = "async-tempfile")]
mod temp_file;
mod traits;
mod writer;
use crossbeam::atomic::AtomicCell;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::task::Waker;
use uuid::Uuid;
pub use reader::{FileSize, SharedFileReader};
pub use traits::*;
pub use writer::SharedFileWriter;
pub mod prelude {
pub use crate::errors::*;
pub use crate::traits::*;
pub use crate::SharedFile;
#[cfg_attr(docsrs, doc(cfg(feature = "async-tempfile")))]
#[cfg(feature = "async-tempfile")]
pub use crate::SharedTemporaryFile;
}
#[cfg_attr(docsrs, doc(cfg(feature = "async-tempfile")))]
#[cfg(feature = "async-tempfile")]
pub use temp_file::*;
#[derive(Debug)]
pub struct SharedFile<T> {
sentinel: Arc<Sentinel<T>>,
}
#[derive(Debug)]
struct Sentinel<T> {
original: T,
state: AtomicCell<WriteState>,
wakers: Mutex<HashMap<Uuid, Waker>>,
}
#[derive(Debug, Clone, Copy)]
enum WriteState {
Pending(usize, usize),
Completed(usize),
Failed,
}
impl<T> SharedFile<T>
where
T: SharedFileType<Type = T>,
{
pub fn new() -> Result<SharedFile<T>, T::Error>
where
T: NewFile<Target = T>,
{
let file = T::new()?;
Ok(Self::from(file))
}
pub async fn new_async() -> Result<SharedFile<T>, T::Error>
where
T: AsyncNewFile<Target = T>,
{
let file = T::new_async().await?;
Ok(Self::from(file))
}
pub async fn writer(&self) -> Result<SharedFileWriter<T::Type>, T::OpenError> {
let file = self.sentinel.original.open_rw().await?;
Ok(SharedFileWriter::new(file, self.sentinel.clone()))
}
pub async fn reader(&self) -> Result<SharedFileReader<T::Type>, T::OpenError> {
let file = self.sentinel.original.open_ro().await?;
Ok(SharedFileReader::new(file, self.sentinel.clone()))
}
}
impl<T> From<T> for SharedFile<T> {
fn from(value: T) -> Self {
Self {
sentinel: Arc::new(Sentinel {
original: value,
state: AtomicCell::new(WriteState::Pending(0, 0)),
wakers: Mutex::new(HashMap::default()),
}),
}
}
}
impl<T> Default for SharedFile<T>
where
T: Default,
{
fn default() -> Self {
Self {
sentinel: Arc::new(Sentinel {
original: T::default(),
state: AtomicCell::new(WriteState::Pending(0, 0)),
wakers: Mutex::new(HashMap::default()),
}),
}
}
}
impl<T> FilePath for SharedFile<T>
where
T: FilePath,
{
fn file_path(&self) -> &PathBuf {
self.sentinel.original.file_path()
}
}
impl<T> Sentinel<T> {
fn wake_readers(&self) {
let mut lock = self
.wakers
.lock()
.expect("failed to lock waker vector for writing");
lock.drain().for_each(|(_id, w)| w.wake());
}
fn register_reader_waker(&self, id: Uuid, waker: &Waker) {
let mut lock = self
.wakers
.lock()
.expect("failed to lock waker vector for reading");
lock.entry(id)
.and_modify(|e| e.clone_from(waker))
.or_insert(waker.clone());
}
fn remove_reader_waker(&self, id: &Uuid) {
let mut lock = self.wakers.lock().expect("failed to get lock for readers");
lock.remove(id);
}
}