use std::{
cell::{Ref, RefCell, RefMut},
collections::HashMap,
sync::{Arc, Weak},
};
pub(crate) struct Link<T>(RefCell<(T, HashMap<usize, (usize, Arc<dyn Fn()>)>)>);
impl<T> Link<T> {
pub(crate) fn new(t: T) -> Self {
Self(RefCell::new((t, HashMap::new())))
}
pub(crate) fn add_listener<F: FnOnce() -> Arc<dyn Fn()>>(&self, id: usize, f: F) {
#[cfg(feature = "debugging")]
unsafe {
super::LOG(id, "INCREASING LISTENER COUNT")
};
self.0
.borrow_mut()
.1
.entry(id)
.or_insert_with(|| {
#[cfg(feature = "debugging")]
unsafe {
super::LOG(id, "ADDING NEW LISTENER")
};
(0, f())
})
.0 += 1;
}
pub(crate) fn drop_listener(&self, id: usize) {
#[cfg(feature = "debugging")]
unsafe {
super::LOG(id, "DECREASING LISTENER COUNT")
};
let mut p = self.0.borrow_mut();
let c = if let Some((c, _)) = p.1.get_mut(&id) {
*c -= 1;
*c
} else {
1
};
if c == 0 {
#[cfg(feature = "debugging")]
unsafe {
super::LOG(id, "DROPPING LISTENER")
};
p.1.remove(&id);
}
}
pub(crate) fn needs_update(&self) {
for (_id, (_, u)) in self.0.borrow().1.iter().filter(|&(_, &(ct, _))| ct > 0) {
#[cfg(feature = "debugging")]
unsafe {
super::LOG(*_id, "MARKING FOR UPDATE")
};
u()
}
}
pub(crate) fn borrow(&self) -> Ref<T> {
Ref::map(self.0.borrow(), |(r, _)| r)
}
pub(crate) fn borrow_mut(&self) -> RefMut<T> {
RefMut::map(self.0.borrow_mut(), |(r, _)| r)
}
}
pub struct Shareable<T>(pub(crate) Option<Weak<Link<T>>>);
impl<T> Shareable<T> {
pub const fn new() -> Self {
Self(None)
}
}
#[macro_export]
macro_rules! shareable {
($(#[$meta:meta])*$vis:vis $IDENT:ident: $Ty:ty = $($init:tt)*) => {
$(#[$meta])*
$vis struct $IDENT;
impl $IDENT {
pub fn use_rw<P>(self,cx: &$crate::reexported::Scope<P>) -> $crate::Shared<$Ty, $crate::RW> {
$crate::shared::Static::_use_rw(self, cx)
}
pub fn use_w<P>(self,cx: &$crate::reexported::Scope<P>) -> $crate::Shared<$Ty, $crate::W> {
$crate::shared::Static::_use_w(self, cx)
}
}
const _: () = {
#[allow(non_upper_case_globals)]
static mut $IDENT: $crate::shared::Shareable<$Ty> = $crate::shared::Shareable::new();
#[doc(hidden)]
impl $crate::shared::Static for $IDENT {
type Type = $Ty;
fn share_without_hook(self) -> $crate::Shared<$Ty, $crate::W> {
$crate::Shared::from_shareable(unsafe { &mut $IDENT }, || {$($init)*})
}
fn _use_rw<P>(self,cx: &$crate::reexported::Scope<P>) -> $crate::Shared<$Ty, $crate::RW> {
$crate::Shared::init(cx, unsafe { &mut $IDENT }, || {$($init)*}, $crate::RW)
}
fn _use_w<P>(self,cx: &$crate::reexported::Scope<P>) -> $crate::Shared<$Ty, $crate::W> {
$crate::Shared::init(cx, unsafe { &mut $IDENT }, || {$($init)*}, $crate::W)
}
}
};
};
}
#[doc(hidden)]
pub trait Static {
type Type;
fn share_without_hook(self) -> Shared<Self::Type, super::W>;
fn _use_rw<P>(self, cx: &dioxus_core::Scope<P>) -> Shared<Self::Type, super::RW>;
fn _use_w<P>(self, cx: &dioxus_core::Scope<P>) -> Shared<Self::Type, super::W>;
}
pub struct Shared<T: 'static, B: 'static> {
link: Arc<Link<T>>,
pub id: Option<usize>,
__: std::marker::PhantomData<B>,
}
impl<T: 'static, B: 'static> Clone for Shared<T, B> {
fn clone(&self) -> Self {
if let Some(id) = self.id {
self.link.add_listener(id, || Arc::new(|| {}))
}
Self {
link: self.link.clone(),
id: self.id,
__: std::marker::PhantomData,
}
}
}
impl<T: 'static, B: 'static + super::Flag> Shared<T, B> {
pub fn init<P, F: FnOnce() -> T>(
cx: &dioxus_core::Scope<P>,
opt: &mut Shareable<T>,
f: F,
_: B,
) -> Self {
let id = cx.scope_id().0;
cx.use_hook(|_| {
let mut r: Shared<T, super::W> = Shared::from_shareable(opt, f);
if B::READ {
r.id = Some(id);
r.link.add_listener(id, || cx.schedule_update());
}
unsafe { std::mem::transmute::<_, Self>(r) }
})
.clone()
}
pub fn write(&self) -> RefMut<T> {
self.link.needs_update();
self.link.borrow_mut()
}
pub fn write_silent(&self) -> RefMut<T> {
self.link.borrow_mut()
}
pub fn needs_update(&self) {
self.link.needs_update();
}
pub fn set(&self, t: T)
where
T: PartialEq,
{
if *self.link.borrow() != t {
*self.write() = t;
}
}
pub fn set_with<F: Fn(&T) -> T>(&self, f: F)
where
T: PartialEq,
{
let prev = self.link.borrow();
let updated = f(&prev);
if *prev != updated {
drop(prev);
*self.write() = updated;
}
}
pub fn read(&self) -> Ref<T> {
self.link.borrow()
}
pub fn listeners<'a>(&'a self) -> String {
format!(
"{:?}",
self.link
.0
.borrow()
.1
.iter()
.map(|(&i, &(j, _))| (i, j))
.collect::<Vec<_>>()
)
}
}
impl<T: 'static> Shared<T, super::W> {
pub(crate) fn from_link(link: Arc<Link<T>>) -> Self {
Self {
link,
id: None,
__: std::marker::PhantomData,
}
}
#[doc(hidden)]
pub fn from_shareable<F: FnOnce() -> T>(opt: &mut Shareable<T>, f: F) -> Self {
if let Some(Some(p)) = opt.0.as_ref().map(Weak::upgrade) {
Shared {
link: p,
id: None,
__: std::marker::PhantomData,
}
} else {
let r = Shared {
link: Arc::new(Link::new(f())),
id: None,
__: std::marker::PhantomData,
};
opt.0 = Some(Arc::downgrade(&r.link));
r
}
}
}
impl<T: 'static, B: 'static> Drop for Shared<T, B> {
fn drop(&mut self) {
if let Some(id) = self.id {
self.link.drop_listener(id);
}
}
}