use std::cell::RefCell;
use std::marker::PhantomData;
use std::rc::Rc;
use super::runtime::{ArcSubscription, NodeId};
use super::with_runtime;
pub(crate) struct ArcSignalInner<T> {
value: RefCell<T>,
subscribers: RefCell<Vec<NodeId>>,
}
impl<T: 'static> ArcSubscription for ArcSignalInner<T> {
fn unsubscribe(&self, subscriber: NodeId) {
self.subscribers.borrow_mut().retain(|n| *n != subscriber);
}
}
fn track<T: 'static>(inner: &Rc<ArcSignalInner<T>>) {
let added = {
let mut subs = inner.subscribers.borrow_mut();
with_runtime(|rt| {
if let Some(tracker) = rt.current_tracker {
if !subs.contains(&tracker) {
subs.push(tracker);
return Some(tracker);
}
}
None
})
};
if let Some(tracker) = added {
let cleanup: Rc<dyn ArcSubscription> = inner.clone();
with_runtime(|rt| {
if let Some(node) = rt.nodes.get_mut(tracker) {
node.arc_sources.push(cleanup);
}
});
}
}
fn notify_subscribers<T: 'static>(inner: &Rc<ArcSignalInner<T>>) {
let subscribers: Vec<NodeId> = inner.subscribers.borrow().clone();
let mut stale: Vec<NodeId> = Vec::new();
with_runtime(|rt| {
for sub in &subscribers {
if !rt.nodes.contains_key(*sub) {
stale.push(*sub);
}
}
});
for sub in &subscribers {
if !stale.contains(sub) {
super::scheduler::schedule(*sub);
}
}
if !stale.is_empty() {
inner
.subscribers
.borrow_mut()
.retain(|n| !stale.contains(n));
}
}
pub struct ArcRwSignal<T: 'static> {
pub(crate) inner: Rc<ArcSignalInner<T>>,
}
impl<T: 'static> Clone for ArcRwSignal<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<T: 'static> ArcRwSignal<T> {
pub fn new(value: T) -> Self {
Self {
inner: Rc::new(ArcSignalInner {
value: RefCell::new(value),
subscribers: RefCell::new(Vec::new()),
}),
}
}
pub fn read_only(&self) -> ArcReadSignal<T> {
ArcReadSignal {
inner: self.inner.clone(),
_ty: PhantomData,
}
}
pub fn write_only(&self) -> ArcWriteSignal<T> {
ArcWriteSignal {
inner: self.inner.clone(),
_ty: PhantomData,
}
}
pub fn split(self) -> (ArcReadSignal<T>, ArcWriteSignal<T>) {
(
ArcReadSignal {
inner: self.inner.clone(),
_ty: PhantomData,
},
ArcWriteSignal {
inner: self.inner,
_ty: PhantomData,
},
)
}
pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
track(&self.inner);
f(&self.inner.value.borrow())
}
pub fn with_untracked<R>(&self, f: impl FnOnce(&T) -> R) -> R {
f(&self.inner.value.borrow())
}
pub fn set(&self, value: T) {
self.update(move |slot| *slot = value);
}
pub fn update(&self, f: impl FnOnce(&mut T)) {
f(&mut self.inner.value.borrow_mut());
notify_subscribers(&self.inner);
}
pub fn update_untracked(&self, f: impl FnOnce(&mut T)) {
f(&mut self.inner.value.borrow_mut());
}
}
impl<T: 'static + Clone> ArcRwSignal<T> {
pub fn get(&self) -> T {
self.with(|v| v.clone())
}
pub fn get_untracked(&self) -> T {
self.with_untracked(|v| v.clone())
}
}
pub struct ArcReadSignal<T: 'static> {
pub(crate) inner: Rc<ArcSignalInner<T>>,
pub(crate) _ty: PhantomData<fn() -> T>,
}
impl<T: 'static> Clone for ArcReadSignal<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
_ty: PhantomData,
}
}
}
impl<T: 'static> ArcReadSignal<T> {
pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
track(&self.inner);
f(&self.inner.value.borrow())
}
pub fn with_untracked<R>(&self, f: impl FnOnce(&T) -> R) -> R {
f(&self.inner.value.borrow())
}
}
impl<T: 'static + Clone> ArcReadSignal<T> {
pub fn get(&self) -> T {
self.with(|v| v.clone())
}
pub fn get_untracked(&self) -> T {
self.with_untracked(|v| v.clone())
}
}
pub struct ArcWriteSignal<T: 'static> {
pub(crate) inner: Rc<ArcSignalInner<T>>,
pub(crate) _ty: PhantomData<fn(T)>,
}
impl<T: 'static> Clone for ArcWriteSignal<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
_ty: PhantomData,
}
}
}
impl<T: 'static> ArcWriteSignal<T> {
pub fn set(&self, value: T) {
self.update(move |slot| *slot = value);
}
pub fn update(&self, f: impl FnOnce(&mut T)) {
f(&mut self.inner.value.borrow_mut());
notify_subscribers(&self.inner);
}
pub fn update_untracked(&self, f: impl FnOnce(&mut T)) {
f(&mut self.inner.value.borrow_mut());
}
}
pub fn arc_signal<T: 'static>(initial: T) -> (ArcReadSignal<T>, ArcWriteSignal<T>) {
ArcRwSignal::new(initial).split()
}