use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use std::sync::atomic::{AtomicUsize, Ordering};
#[allow(unused_imports)]
use crate::Effect;
pub struct RuntimeInner {
id_counter: AtomicUsize,
pub(crate) current_observer: RefCell<Option<usize>>,
pub(crate) dependencies: RefCell<HashMap<usize, HashSet<usize>>>,
pub(crate) observer_deps: RefCell<HashMap<usize, HashSet<usize>>>,
pub(crate) observers: RefCell<HashMap<usize, Rc<dyn Fn()>>>,
pub(crate) memo_dirty: RefCell<HashMap<usize, bool>>,
pub(crate) derived_effects: RefCell<HashMap<usize, Vec<crate::Effect>>>,
}
impl RuntimeInner {
pub fn new() -> Self {
Self {
id_counter: AtomicUsize::new(0),
current_observer: RefCell::new(None),
dependencies: RefCell::new(HashMap::new()),
observer_deps: RefCell::new(HashMap::new()),
observers: RefCell::new(HashMap::new()),
memo_dirty: RefCell::new(HashMap::new()),
derived_effects: RefCell::new(HashMap::new()),
}
}
pub fn allocate_id(&self) -> usize {
self.id_counter.fetch_add(1, Ordering::SeqCst)
}
pub fn track_read(&self, signal_id: usize) {
let current_observer = *self.current_observer.borrow();
if let Some(current_observer) = current_observer {
self.dependencies
.borrow_mut()
.entry(signal_id)
.or_insert_with(HashSet::new)
.insert(current_observer);
self.observer_deps
.borrow_mut()
.entry(current_observer)
.or_insert_with(HashSet::new)
.insert(signal_id);
}
}
pub fn notify_observers(&self, signal_id: usize) {
let observers: Vec<usize> = self
.dependencies
.borrow()
.get(&signal_id)
.map(|obs| obs.iter().copied().collect())
.unwrap_or_default();
for observer_id in observers {
self.mark_observer_dirty(observer_id);
}
}
fn mark_observer_dirty(&self, observer_id: usize) {
let is_memo = self.memo_dirty.borrow().contains_key(&observer_id);
if is_memo {
let already_dirty = self
.memo_dirty
.borrow()
.get(&observer_id)
.copied()
.unwrap_or(false);
if !already_dirty {
self.memo_dirty.borrow_mut().insert(observer_id, true);
let dependents: Vec<usize> = self
.dependencies
.borrow()
.get(&observer_id)
.map(|deps| deps.iter().copied().collect())
.unwrap_or_default();
for dependent_id in dependents {
self.mark_observer_dirty(dependent_id);
}
}
} else {
let effect = self.observers.borrow().get(&observer_id).cloned();
if let Some(effect) = effect {
effect();
}
}
}
pub fn create_observer<F>(&self, observer_id: usize, f: F)
where
F: Fn() + 'static,
{
if let Some(old_deps) = self.observer_deps.borrow_mut().remove(&observer_id) {
for signal_id in old_deps {
if let Some(deps) = self.dependencies.borrow_mut().get_mut(&signal_id) {
deps.remove(&observer_id);
}
}
}
self.observers.borrow_mut().insert(observer_id, Rc::new(f));
}
pub fn with_observer_scoped<F, R>(
runtime: &Rc<RefCell<RuntimeInner>>,
observer_id: usize,
f: F,
) -> R
where
F: FnOnce() -> R,
{
struct Guard<'a> {
runtime: &'a Rc<RefCell<RuntimeInner>>,
prev_observer: Option<usize>,
}
impl<'a> Drop for Guard<'a> {
fn drop(&mut self) {
*self.runtime.borrow().current_observer.borrow_mut() = self.prev_observer;
}
}
let prev_observer = runtime
.borrow()
.current_observer
.borrow_mut()
.replace(observer_id);
let _guard = Guard {
runtime,
prev_observer,
};
f()
}
pub fn register_memo(&self, memo_id: usize) {
self.memo_dirty.borrow_mut().insert(memo_id, true);
}
pub fn is_memo_dirty(&self, memo_id: usize) -> bool {
self.memo_dirty
.borrow()
.get(&memo_id)
.copied()
.unwrap_or(true)
}
pub fn mark_memo_clean(&self, memo_id: usize) {
self.memo_dirty.borrow_mut().insert(memo_id, false);
}
pub fn remove_observer(&self, observer_id: usize) {
let to_drop = self.observers.borrow_mut().remove(&observer_id);
let old_deps = self.observer_deps.borrow_mut().remove(&observer_id);
if let Some(old_deps) = old_deps {
for signal_id in old_deps {
if let Some(deps) = self.dependencies.borrow_mut().get_mut(&signal_id) {
deps.remove(&observer_id);
}
}
}
drop(to_drop);
}
pub fn remove_signal(&self, signal_id: usize) {
let derived_to_drop = self.derived_effects.borrow_mut().remove(&signal_id);
let observers = self.dependencies.borrow_mut().remove(&signal_id);
if let Some(observers) = observers {
for observer_id in &observers {
if let Some(deps) = self.observer_deps.borrow_mut().get_mut(observer_id) {
deps.remove(&signal_id);
}
}
}
drop(derived_to_drop);
}
pub fn remove_memo(&self, memo_id: usize) {
self.memo_dirty.borrow_mut().remove(&memo_id);
let observer_to_drop = self.observers.borrow_mut().remove(&memo_id);
let old_deps = self.observer_deps.borrow_mut().remove(&memo_id);
if let Some(old_deps) = old_deps {
for signal_id in old_deps {
if let Some(deps) = self.dependencies.borrow_mut().get_mut(&signal_id) {
deps.remove(&memo_id);
}
}
}
drop(observer_to_drop);
}
pub fn clear(&self) {
*self.current_observer.borrow_mut() = None;
self.dependencies.borrow_mut().clear();
self.observer_deps.borrow_mut().clear();
self.observers.borrow_mut().clear();
self.memo_dirty.borrow_mut().clear();
self.derived_effects.borrow_mut().clear();
}
pub fn register_derived_effect(&self, signal_id: usize, effect: crate::Effect) {
self.derived_effects
.borrow_mut()
.entry(signal_id)
.or_insert_with(Vec::new)
.push(effect);
}
}