use std::{
cell::{Ref, RefCell},
rc::{Rc, Weak},
};
use crate::{Effect, IMemo, IObservable, effect_stack::EffectStackEntry};
pub struct Signal<T> {
value: RefCell<T>,
dependents: RefCell<Vec<Weak<dyn IMemo>>>,
effects: RefCell<Vec<Weak<Effect>>>,
}
impl<T: Default> Default for Signal<T> {
fn default() -> Self {
Self {
value: Default::default(),
dependents: Default::default(),
effects: Default::default(),
}
}
}
impl<T> Signal<T> {
fn flush_effects(&self) {
self.effects.borrow_mut().retain(|w| {
if let Some(e) = w.upgrade() {
crate::effect::run_untracked(&e);
true
} else {
false
}
});
}
#[allow(non_snake_case)]
fn OnPropertyChanged(&self) {
self.flush_effects()
}
#[allow(non_snake_case)]
fn OnPropertyChanging(&self) {
self.invalidate()
}
pub fn new(value: T) -> Rc<Self> {
Signal {
value: value.into(),
dependents: vec![].into(),
effects: vec![].into(),
}
.into()
}
pub fn get(&self) -> Ref<'_, T> {
self.dependency_collection();
if let Some(EffectStackEntry {
effect: e,
collecting,
}) = crate::effect_stack::effect_peak()
&& *collecting
&& !self.effects.borrow().iter().any(|w| Weak::ptr_eq(w, e))
{
self.effects.borrow_mut().push(e.clone());
}
self.value.borrow()
}
}
pub trait SignalSetter<T> {
fn set(&self, value: T) -> bool;
}
impl<T> SignalSetter<T> for Signal<T> {
default fn set(&self, value: T) -> bool {
self.OnPropertyChanging();
*self.value.borrow_mut() = value;
self.OnPropertyChanged();
true
}
}
impl<T: Eq> SignalSetter<T> for Signal<T> {
fn set(&self, value: T) -> bool {
if *self.value.borrow() == value {
return false;
}
self.OnPropertyChanging();
*self.value.borrow_mut() = value;
self.OnPropertyChanged();
true
}
}
impl<T> IObservable for Signal<T> {
fn dependents(&self) -> &RefCell<Vec<Weak<dyn IMemo>>> {
&self.dependents
}
}