use parking_lot::RwLock;
use std::sync::Arc;
use crate::hook_context::with_hook_context;
#[cfg(test)]
mod tests;
#[derive(Debug)]
pub struct RefContainer<T> {
value: RwLock<T>,
}
impl<T> RefContainer<T> {
pub fn new<F>(initializer: F) -> Self
where
F: FnOnce() -> T,
{
Self {
value: RwLock::new(initializer()),
}
}
pub fn get(&self) -> T
where
T: Clone,
{
self.value.read().clone()
}
pub fn set(&self, new_value: T) {
let mut value = self.value.write();
*value = new_value;
}
pub fn update<F>(&self, updater: F)
where
F: FnOnce(&mut T),
{
let mut value = self.value.write();
updater(&mut *value);
}
pub fn with<F, R>(&self, accessor: F) -> R
where
F: FnOnce(&T) -> R,
{
let value = self.value.read();
accessor(&*value)
}
pub fn with_mut<F, R>(&self, mutator: F) -> R
where
F: FnOnce(&mut T) -> R,
{
let mut value = self.value.write();
mutator(&mut *value)
}
pub fn replace(&self, new_value: T) -> T {
let mut value = self.value.write();
std::mem::replace(&mut *value, new_value)
}
pub fn take(&self) -> T
where
T: Default,
{
let mut value = self.value.write();
std::mem::take(&mut *value)
}
}
#[derive(Debug)]
pub struct RefHandle<T> {
container: Arc<RefContainer<T>>,
}
impl<T> RefHandle<T> {
pub fn new<F>(initializer: F) -> Self
where
F: FnOnce() -> T,
{
Self {
container: Arc::new(RefContainer::new(initializer)),
}
}
pub fn from_container(container: Arc<RefContainer<T>>) -> Self {
Self { container }
}
pub fn get(&self) -> T
where
T: Clone,
{
self.container.get()
}
pub fn set(&self, new_value: T) {
self.container.set(new_value);
}
pub fn update<F>(&self, updater: F)
where
F: FnOnce(&mut T),
{
self.container.update(updater);
}
pub fn with<F, R>(&self, accessor: F) -> R
where
F: FnOnce(&T) -> R,
{
self.container.with(accessor)
}
pub fn with_mut<F, R>(&self, mutator: F) -> R
where
F: FnOnce(&mut T) -> R,
{
self.container.with_mut(mutator)
}
pub fn replace(&self, new_value: T) -> T {
self.container.replace(new_value)
}
pub fn take(&self) -> T
where
T: Default,
{
self.container.take()
}
pub fn container(&self) -> &Arc<RefContainer<T>> {
&self.container
}
}
impl<T> Clone for RefHandle<T> {
fn clone(&self) -> Self {
Self {
container: self.container.clone(),
}
}
}
pub fn use_ref<T, F>(initializer: F) -> RefHandle<T>
where
T: 'static,
F: FnOnce() -> T,
{
with_hook_context(|ctx| {
let index = ctx.next_hook_index();
let container_ref =
ctx.get_or_init_state(index, || Arc::new(RefContainer::new(initializer)));
let container = container_ref.borrow().clone();
RefHandle::from_container(container)
})
}