#![deny(missing_docs)]
use std::{cmp::Eq, collections::HashMap, hash::Hash, sync::Arc};
use tokio::{
sync::RwLock,
time::{error::Elapsed, timeout},
};
struct SlockData<T> {
pub version: u64,
pub value: T,
pub hook: Option<Box<dyn FnMut(&T)>>,
}
pub struct Slock<T> {
lock: Arc<RwLock<SlockData<T>>>,
}
impl<T> Slock<T> {
pub fn new(value: T) -> Self {
let data = SlockData {
version: 0,
value,
hook: None,
};
Self {
lock: Arc::new(RwLock::new(data)),
}
}
pub async fn map<F, U>(&self, mapper: F) -> Result<U, Elapsed>
where
F: FnOnce(&T) -> U,
{
let v = self.lock.read().await;
timeout(std::time::Duration::from_secs(1), async {
mapper(&v.value)
})
.await
}
pub async fn set<F>(&self, setter: F)
where
F: FnOnce(T) -> T,
{
let mut data = self.lock.write().await;
let ptr = &mut data.value as *mut T;
let new = timeout(std::time::Duration::from_secs(1), async {
setter(unsafe { ptr.read() })
})
.await;
if let Ok(new) = new {
timeout(std::time::Duration::from_secs(1), async {
data.hook.as_mut().map(|hook| hook(&new));
})
.await
.ok();
unsafe { ptr.write(new) };
}
data.version += 1;
}
#[deprecated = "Use `clone()` instead"]
pub fn split(&self) -> Self {
Self {
lock: self.lock.clone(),
}
}
pub async fn hook<F: 'static>(&self, hook: F)
where
F: FnMut(&T),
{
let mut data = self.lock.write().await;
data.hook = Some(Box::new(hook));
}
}
impl<T> Clone for Slock<T> {
fn clone(&self) -> Self {
Self {
lock: self.lock.clone(),
}
}
}
impl<T: Clone> Slock<T> {
pub async fn get_clone(&self) -> T {
let data = self.lock.read().await;
data.value.clone()
}
pub async fn clone_deep(&self) -> Self {
return Slock::new(self.get_clone().await);
}
}
impl<T> Slock<Vec<T>> {
pub async fn push(&self, value: T) {
self.set(|mut v| {
v.push(value);
v
})
.await;
}
}
impl<T> Slock<Slock<T>> {
pub async fn flatten(&self) -> Slock<T> {
self.map(|inner| inner.clone()).await.unwrap()
}
}
pub type SlockMap<K, V> = Slock<HashMap<K, Slock<V>>>;
impl<K: Eq + Hash + Copy, V> SlockMap<K, V> {
pub fn new_map() -> Slock<HashMap<K, Slock<V>>> {
let map: HashMap<K, Slock<V>> = HashMap::new();
Slock::new(map)
}
pub async fn insert<F>(&self, key: K, setter: F)
where
F: FnOnce(Option<V>) -> V,
{
if let Some(data) = self.from_key(key).await {
data.set(|v| setter(Some(v))).await;
} else {
self.set(|mut hash_map| {
hash_map.insert(key, Slock::new(setter(None)));
hash_map
})
.await;
}
}
pub async fn from_key(&self, key: K) -> Option<Slock<V>> {
self.map(|hash_map| {
let key = key;
hash_map.get(&key).map(|inner| inner.clone())
})
.await
.unwrap()
}
}
impl<T: Copy> Slock<T> {
pub async fn get(&self) -> T {
let data = self.lock.read().await;
data.value
}
}
unsafe impl<T: Send> Send for Slock<T> {}
unsafe impl<T: Send> Sync for Slock<T> {}