1use std::any::{Any, TypeId};
2use std::cell::{Ref, RefCell};
3use std::collections::{HashMap, HashSet};
4use std::rc::{Rc, Weak};
5use std::sync::atomic::{AtomicU64, Ordering};
6
7use tokio::task::JoinHandle;
8
9use crate::actor::Addr;
10use crate::actor::event_bus::subscribe::BusSubscription;
11use crate::actor::registry::{ActorSnapshot, Owner};
12use crate::actor::shape::Declared;
13use crate::actor::traits::ManagedActor;
14
15static NEXT_SUBSCRIBER_ID: AtomicU64 = AtomicU64::new(0);
16
17static NEXT_SERIAL: AtomicU64 = AtomicU64::new(1);
19
20pub struct Subscription {
21 unsubscribe: Option<Box<dyn FnOnce()>>,
22}
23
24impl Subscription {
25 pub(crate) fn inert() -> Self {
28 Self { unsubscribe: None }
29 }
30}
31
32impl Drop for Subscription {
33 fn drop(&mut self) {
34 if let Some(unsubscribe) = self.unsubscribe.take() {
35 unsubscribe();
36 }
37 }
38}
39
40pub struct StateHandle<T>(Rc<RefCell<T>>);
41
42impl<T> StateHandle<T> {
43 pub fn borrow(&self) -> Ref<'_, T> {
44 self.0.borrow()
45 }
46}
47
48impl<T> Clone for StateHandle<T> {
49 fn clone(&self) -> Self {
50 Self(self.0.clone())
51 }
52}
53
54impl<T> From<Rc<RefCell<T>>> for StateHandle<T> {
55 fn from(inner: Rc<RefCell<T>>) -> Self {
56 Self(inner)
57 }
58}
59
60pub trait Reducer: Clone + Default + std::fmt::Debug + 'static {
93 type Update: Clone + 'static;
97
98 fn reduce(&mut self, update: Self::Update);
99}
100
101pub type Slot<R> = RefCell<Rc<R>>;
104
105struct Cell {
106 state: Rc<dyn Any>,
107 listeners: RefCell<Vec<(u64, Rc<dyn Fn()>)>>,
108 observers: RefCell<Vec<(u64, Rc<dyn Fn(&dyn Any)>)>>,
109}
110
111#[derive(Clone, Copy)]
113struct Kind {
114 name: &'static str,
115 describe: fn(&dyn Any) -> String,
116}
117
118impl Kind {
119 fn of<R: Reducer>() -> Self {
120 Self {
121 name: std::any::type_name::<R>(),
122 describe: |state| match state.downcast_ref::<Slot<R>>() {
123 Some(cell) => match cell.try_borrow() {
124 Ok(state) => format!("{:#?}", **state),
125 Err(_) => "<being changed>".to_string(),
126 },
127 None => "<unknown>".to_string(),
128 },
129 }
130 }
131}
132
133#[derive(Clone, Debug, PartialEq)]
135pub struct Listener {
136 pub event: &'static str,
137 pub actor: Option<&'static str>,
139 pub bus: crate::trace::Bus,
140 pub feature: Option<&'static str>,
141}
142
143#[derive(Clone, Debug, PartialEq)]
145pub struct DescribedState {
146 pub type_name: &'static str,
147 pub state: String,
148 pub feature: Option<&'static str>,
150 pub declared: Option<Declared>,
152}
153
154#[derive(Clone, Copy, Debug, PartialEq)]
156pub struct Installed {
157 pub name: &'static str,
158 pub declared: Option<Declared>,
160}
161
162impl Cell {
163 fn empty<R: Reducer>() -> Self {
164 Self::holding(Rc::new(Slot::new(Rc::new(R::default()))))
165 }
166
167 fn holding(state: Rc<dyn Any>) -> Self {
168 Cell {
169 state,
170 listeners: RefCell::new(Vec::new()),
171 observers: RefCell::new(Vec::new()),
172 }
173 }
174}
175
176#[derive(Clone, Copy, PartialEq, Eq, Hash)]
190pub struct Scope {
191 tree: u32,
192 index: u32,
193 serial: u64,
194}
195
196impl std::fmt::Debug for Scope {
197 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198 write!(f, "Scope({})", self.serial)
199 }
200}
201
202pub type Outlet = &'static str;
205
206pub const MAIN: Outlet = "main";
208
209#[derive(Default)]
210struct ScopeData {
211 cells: RefCell<HashMap<TypeId, Cell>>,
212 kinds: RefCell<HashMap<TypeId, Kind>>,
215 teardowns: RefCell<Vec<Box<dyn FnOnce()>>>,
216 installed_features: RefCell<HashSet<TypeId>>,
224 exports: RefCell<HashSet<TypeId>>,
230 sections: RefCell<Vec<HashMap<TypeId, Rc<dyn Any>>>>,
237 section_names: RefCell<Vec<Option<&'static str>>>,
239 section_declarations: RefCell<Vec<Option<Declared>>>,
241 declarations: RefCell<HashMap<TypeId, Declared>>,
243 listeners: RefCell<Vec<Listener>>,
245 owners: RefCell<HashMap<TypeId, usize>>,
248 installing: RefCell<Vec<usize>>,
251 leave_guards: RefCell<Vec<Rc<dyn Fn() -> crate::guard::Verdict>>>,
253 asleep: Rc<std::cell::Cell<bool>>,
256 wake_hooks: RefCell<Vec<Rc<dyn Fn()>>>,
258 window: std::cell::Cell<Option<u64>>,
260 actors: RefCell<Vec<HeldActor>>,
262}
263
264struct HeldActor {
265 id: usize,
266 type_name: &'static str,
267 shape: crate::actor::shape::Shape,
268 owner: Owner,
269 snapshot: Box<dyn Fn() -> String>,
270}
271
272impl HeldActor {
273 fn read(&self) -> ActorSnapshot {
274 ActorSnapshot {
275 id: self.id,
276 type_name: self.type_name,
277 shape: self.shape,
278 owner: self.owner,
279 state: (self.snapshot)(),
280 }
281 }
282}
283
284fn read_all(scopes: Vec<Scope>) -> Vec<ActorSnapshot> {
285 scopes
286 .into_iter()
287 .filter_map(|scope| scope.data())
288 .flat_map(|data| data.actors.borrow().iter().map(HeldActor::read).collect::<Vec<_>>())
289 .collect()
290}
291
292fn read_one(scopes: Vec<Scope>, id: usize) -> Option<ActorSnapshot> {
293 scopes.into_iter().filter_map(|scope| scope.data()).find_map(|data| {
294 data.actors
295 .borrow()
296 .iter()
297 .find(|actor| actor.id == id)
298 .map(HeldActor::read)
299 })
300}
301
302struct Unlisted {
304 root: Option<u64>,
305 id: usize,
306}
307
308impl Teardown for Unlisted {
309 fn teardown(self) {
310 crate::devtools::changed(|| crate::devtools::Change::ActorRemoved {
311 root: self.root,
312 id: self.id,
313 });
314 }
315}
316
317struct Node {
318 serial: u64,
319 data: Option<Rc<ScopeData>>,
320 parent: Option<u32>,
321 outlet: Outlet,
322 children: Vec<u32>,
323}
324
325#[derive(Default)]
327struct Tree {
328 nodes: Vec<Node>,
329 free: Vec<u32>,
330}
331
332impl Tree {
333 fn insert(&mut self, parent: Option<u32>, outlet: Outlet) -> (u32, u64) {
334 let serial = NEXT_SERIAL.fetch_add(1, Ordering::Relaxed);
335 let data = Some(Rc::new(ScopeData::default()));
336 let index = match self.free.pop() {
337 Some(index) => {
338 let node = &mut self.nodes[index as usize];
339 node.serial = serial;
340 node.data = data;
341 node.parent = parent;
342 node.outlet = outlet;
343 index
344 }
345 None => {
346 self.nodes.push(Node {
347 serial,
348 data,
349 parent,
350 outlet,
351 children: Vec::new(),
352 });
353 (self.nodes.len() - 1) as u32
354 }
355 };
356
357 if let Some(parent) = parent {
358 self.nodes[parent as usize].children.push(index);
359 }
360
361 (index, serial)
362 }
363
364 fn node(&self, index: u32, serial: u64) -> Option<&Node> {
365 self.nodes
366 .get(index as usize)
367 .filter(|node| node.serial == serial && node.data.is_some())
368 }
369
370 fn detach(&mut self, index: u32, serial: u64) -> Vec<Rc<ScopeData>> {
374 let Some(node) = self.node(index, serial) else {
375 return Vec::new();
376 };
377 if let Some(parent) = node.parent {
378 self.nodes[parent as usize].children.retain(|child| *child != index);
379 }
380
381 let mut order = Vec::new();
382 self.collect(index, &mut order);
383
384 let mut detached = Vec::with_capacity(order.len());
385 for index in order {
386 let node = &mut self.nodes[index as usize];
387 node.serial = 0;
388 node.parent = None;
389 node.children.clear();
390 if let Some(data) = node.data.take() {
391 detached.push(data);
392 }
393 self.free.push(index);
394 }
395 detached
396 }
397
398 fn collect(&self, index: u32, order: &mut Vec<u32>) {
399 for child in self.nodes[index as usize].children.iter().rev() {
400 self.collect(*child, order);
401 }
402 order.push(index);
403 }
404}
405
406thread_local! {
407 static TREES: RefCell<Vec<Weak<RefCell<Tree>>>> = const { RefCell::new(Vec::new()) };
410}
411
412fn register(tree: &Rc<RefCell<Tree>>) -> u32 {
414 TREES.with(|trees| {
415 let mut trees = trees.borrow_mut();
416 let named = Rc::downgrade(tree);
417
418 match trees.iter().position(|slot| slot.strong_count() == 0) {
419 Some(slot) => {
420 trees[slot] = named;
421 slot as u32
422 }
423 None => {
424 trees.push(named);
425 (trees.len() - 1) as u32
426 }
427 }
428 })
429}
430
431fn tree(slot: u32) -> Option<Rc<RefCell<Tree>>> {
432 TREES
433 .try_with(|trees| trees.borrow().get(slot as usize).and_then(Weak::upgrade))
434 .ok()
435 .flatten()
436}
437
438#[derive(Clone)]
443pub struct Awake(Rc<std::cell::Cell<bool>>);
444
445impl Awake {
446 pub fn now(&self) -> bool {
448 !self.0.get()
449 }
450}
451
452#[must_use = "the scope is removed as soon as this is dropped"]
454pub struct ScopeGuard(Scope);
455
456impl ScopeGuard {
457 pub fn scope(&self) -> Scope {
458 self.0
459 }
460}
461
462impl std::ops::Deref for ScopeGuard {
463 type Target = Scope;
464
465 fn deref(&self) -> &Scope {
466 &self.0
467 }
468}
469
470impl Drop for ScopeGuard {
471 fn drop(&mut self) {
472 self.0.remove();
473 }
474}
475
476pub struct ScopeTree {
482 _tree: Rc<RefCell<Tree>>,
483 root: Scope,
484}
485
486impl ScopeTree {
487 pub fn new() -> Self {
488 let tree = Rc::new(RefCell::new(Tree::default()));
489 let (index, serial) = tree.borrow_mut().insert(None, MAIN);
490
491 Self {
492 root: Scope {
493 tree: register(&tree),
494 index,
495 serial,
496 },
497 _tree: tree,
498 }
499 }
500
501 pub fn scope(&self) -> Scope {
503 self.root
504 }
505}
506
507impl Drop for ScopeTree {
508 fn drop(&mut self) {
509 self.root.remove();
510 }
511}
512
513impl Default for ScopeTree {
514 fn default() -> Self {
515 Self::new()
516 }
517}
518
519impl std::ops::Deref for ScopeTree {
520 type Target = Scope;
521
522 fn deref(&self) -> &Scope {
523 &self.root
524 }
525}
526
527impl Scope {
528 pub fn child(&self) -> Scope {
530 self.child_in(MAIN)
531 }
532
533 pub fn child_in(&self, outlet: Outlet) -> Scope {
535 let tree =
536 tree(self.tree).unwrap_or_else(|| panic!("a child for {self:?}, whose tree is gone"));
537 let mut nodes = tree.borrow_mut();
538 assert!(
539 nodes.node(self.index, self.serial).is_some(),
540 "a child for {self:?}, which was removed"
541 );
542 let (index, serial) = nodes.insert(Some(self.index), outlet);
543
544 Scope {
545 tree: self.tree,
546 index,
547 serial,
548 }
549 }
550
551 pub fn remove(&self) {
557 let Some(tree) = tree(self.tree) else {
558 return;
559 };
560 let detached = tree.borrow_mut().detach(self.index, self.serial);
561 drop(tree);
562
563 for data in detached {
564 data.tear_down();
565 }
566 }
567
568 pub fn is_alive(&self) -> bool {
570 self.read(|tree| tree.node(self.index, self.serial).is_some())
571 .unwrap_or(false)
572 }
573
574 pub fn parent(&self) -> Option<Scope> {
576 self.read(|tree| {
577 let parent = tree.node(self.index, self.serial)?.parent?;
578 Some(self.named(tree, parent))
579 })
580 .flatten()
581 }
582
583 pub fn outlet(&self) -> Option<Outlet> {
585 self.read(|tree| tree.node(self.index, self.serial).map(|node| node.outlet))
586 .flatten()
587 }
588
589 pub fn ancestors(&self) -> Vec<Scope> {
591 std::iter::successors(self.parent(), Scope::parent).collect()
592 }
593
594 fn read<T>(&self, read: impl FnOnce(&Tree) -> T) -> Option<T> {
595 let tree = tree(self.tree)?;
596 let nodes = tree.borrow();
597 Some(read(&nodes))
598 }
599
600 fn named(&self, tree: &Tree, index: u32) -> Scope {
601 Scope {
602 tree: self.tree,
603 index,
604 serial: tree.nodes[index as usize].serial,
605 }
606 }
607
608 pub fn owner_of<R: 'static>(&self) -> Option<Scope> {
615 if self.claims::<R>() {
616 return Some(*self);
617 }
618
619 std::iter::successors(self.parent(), Scope::parent).find(|scope| scope.exports::<R>())
620 }
621
622 pub fn set_window(&self, id: u64) {
625 if let Some(data) = self.data() {
626 data.window.set(Some(id));
627 }
628 }
629
630 pub fn window(&self) -> Option<u64> {
632 std::iter::successors(Some(*self), Scope::parent)
633 .find_map(|scope| scope.data()?.window.get())
634 }
635
636 pub fn hold_actor<A: ManagedActor + std::fmt::Debug>(
643 &self,
644 addr: &Addr<A>,
645 feature: Option<&'static str>,
646 drives: Option<&'static str>,
647 ) {
648 let Some(data) = self.data() else { return };
649 let id = addr.id();
650 let type_name = crate::actor::short_type_name::<A>();
651 let owner = Owner {
652 scope: Some(self.key()),
653 feature,
654 drives,
655 };
656
657 let held = addr.clone();
658 data.actors.borrow_mut().push(HeldActor {
659 id,
660 type_name,
661 shape: A::SHAPE,
662 owner,
663 snapshot: Box::new(move || held.debug_snapshot()),
664 });
665 drop(data);
666
667 let root = self.window();
668 crate::devtools::changed(|| crate::devtools::Change::ActorAdded {
669 root,
670 id,
671 type_name,
672 owner,
673 });
674 self.own(Unlisted { root, id });
675 }
676
677 pub fn actors(&self) -> Vec<ActorSnapshot> {
679 read_all(self.subtree())
680 }
681
682 pub fn actor(&self, id: usize) -> Option<ActorSnapshot> {
685 read_one(self.subtree(), id)
686 }
687
688 pub fn actors_here(&self) -> Vec<ActorSnapshot> {
691 read_all(vec![*self])
692 }
693
694 pub fn actor_here(&self, id: usize) -> Option<ActorSnapshot> {
696 read_one(vec![*self], id)
697 }
698
699 fn subtree(&self) -> Vec<Scope> {
701 self.read(|tree| {
702 if tree.node(self.index, self.serial).is_none() {
703 return Vec::new();
704 }
705
706 let mut order = Vec::new();
707 tree.collect(self.index, &mut order);
708 order
709 .into_iter()
710 .map(|index| self.named(tree, index))
711 .collect()
712 })
713 .unwrap_or_default()
714 }
715
716 pub fn guard(&self) -> ScopeGuard {
718 ScopeGuard(*self)
719 }
720
721 pub fn key(&self) -> usize {
723 self.serial as usize
724 }
725
726 fn data(&self) -> Option<Rc<ScopeData>> {
727 self.read(|tree| {
728 tree.node(self.index, self.serial)
729 .and_then(|node| node.data.clone())
730 })
731 .flatten()
732 }
733
734 fn installing(&self, doing: &str) -> Rc<ScopeData> {
735 self.data()
736 .unwrap_or_else(|| panic!("{doing} in {self:?}, which was removed"))
737 }
738
739 pub fn mark_feature_installed<F: 'static>(&self) {
745 let data = self.installing("installing a feature");
746 let newly_inserted = data.installed_features.borrow_mut().insert(TypeId::of::<F>());
747 assert!(
748 newly_inserted,
749 "feature already installed in this scope - install() called twice for the same feature"
750 );
751 }
752
753 pub fn claims<R: 'static>(&self) -> bool {
757 self.data()
758 .is_some_and(|data| data.owners.borrow().contains_key(&TypeId::of::<R>()))
759 }
760
761 pub fn note_reducer_owner<R: 'static>(&self) {
762 let data = self.installing("claiming a reducer");
763 data.installed_features.borrow_mut().insert(TypeId::of::<R>());
764 let section = data.current_section();
765 data.owners.borrow_mut().entry(TypeId::of::<R>()).or_insert(section);
766 }
767
768 pub fn note_reducer_declared<R: 'static>(&self, declared: Declared) {
770 let data = self.installing("claiming a reducer");
771 data.declarations.borrow_mut().entry(TypeId::of::<R>()).or_insert(declared);
772 }
773
774 pub fn current_crate_dir(&self) -> Option<&'static str> {
777 let data = self.data()?;
778 let section = data.current_section();
779 let declarations = data.section_declarations.borrow();
780 declarations.get(section).copied().flatten().map(|declared| declared.crate_dir)
781 }
782
783 pub fn open_section(&self, name: Option<&'static str>, declared: Option<Declared>) -> usize {
786 let data = self.installing("installing a feature");
787 let mut sections = data.sections.borrow_mut();
788 if sections.is_empty() {
789 sections.push(HashMap::new());
790 }
791 sections.push(HashMap::new());
792 let index = sections.len() - 1;
793 let mut names = data.section_names.borrow_mut();
794 names.resize(index + 1, None);
795 names[index] = name;
796
797 let mut declarations = data.section_declarations.borrow_mut();
798 declarations.resize(index + 1, None);
799 declarations[index] = declared;
800
801 data.installing.borrow_mut().push(index);
802 index
803 }
804
805 pub fn section_name(&self, section: usize) -> Option<&'static str> {
807 self.data()?.section_name(section)
808 }
809
810 pub fn features(&self) -> Vec<Installed> {
812 let Some(data) = self.data() else {
813 return Vec::new();
814 };
815 let declarations = data.section_declarations.borrow();
816
817 data.section_names
818 .borrow()
819 .iter()
820 .enumerate()
821 .filter_map(|(section, name)| {
822 Some(Installed {
823 name: (*name)?,
824 declared: declarations.get(section).copied().flatten(),
825 })
826 })
827 .collect()
828 }
829
830 pub fn current_feature(&self) -> Option<&'static str> {
832 let data = self.data()?;
833 data.section_name(data.current_section())
834 }
835
836 pub fn note_listener(
839 &self,
840 event: &'static str,
841 actor: Option<&'static str>,
842 bus: crate::trace::Bus,
843 ) {
844 let Some(data) = self.data() else { return };
845 let feature = data.section_name(data.current_section());
846 data.listeners.borrow_mut().push(Listener {
847 event,
848 actor,
849 bus,
850 feature,
851 });
852 }
853
854 pub fn listeners(&self) -> Vec<Listener> {
855 self.data()
856 .map(|data| data.listeners.borrow().clone())
857 .unwrap_or_default()
858 }
859
860 pub fn close_section(&self) {
861 if let Some(data) = self.data() {
862 data.installing.borrow_mut().pop();
863 }
864 }
865
866 pub fn current_section(&self) -> usize {
868 self.data().map_or(0, |data| data.current_section())
869 }
870
871 pub fn section_of<R: 'static>(&self) -> usize {
874 self.data()
875 .and_then(|data| data.owners.borrow().get(&TypeId::of::<R>()).copied())
876 .unwrap_or(0)
877 }
878
879 pub fn has_feature<F: 'static>(&self) -> bool {
881 self.data()
882 .is_some_and(|data| data.installed_features.borrow().contains(&TypeId::of::<F>()))
883 }
884
885 pub fn note_export<R: 'static>(&self) {
892 let data = self.installing("exporting a reducer");
893 data.exports.borrow_mut().insert(TypeId::of::<R>());
894 }
895
896 pub fn exports<R: 'static>(&self) -> bool {
898 self.data()
899 .is_some_and(|data| data.exports.borrow().contains(&TypeId::of::<R>()))
900 }
901
902 pub fn describe_states(&self) -> Vec<DescribedState> {
904 let Some(data) = self.data() else {
905 return Vec::new();
906 };
907 let cells = data.cells.borrow();
908 let kinds = data.kinds.borrow();
909 let owners = data.owners.borrow();
910 let mut described: Vec<DescribedState> = cells
911 .iter()
912 .filter_map(|(type_id, cell)| {
913 let kind = kinds.get(type_id)?;
914 Some(DescribedState {
915 type_name: kind.name,
916 state: (kind.describe)(&*cell.state),
917 feature: owners
918 .get(type_id)
919 .and_then(|section| data.section_name(*section)),
920 declared: data.declarations.borrow().get(type_id).copied(),
921 })
922 })
923 .collect();
924 described.sort_by_key(|state| state.type_name);
925 described
926 }
927
928 pub fn state<R: Reducer>(&self) -> Rc<Slot<R>> {
933 match self.data() {
934 Some(data) => data.state::<R>(),
935 None => Rc::new(Slot::new(Rc::new(R::default()))),
936 }
937 }
938
939 pub fn peek<R: Reducer>(&self) -> Option<Rc<Slot<R>>> {
940 let data = self.data()?;
941 data.note_kind::<R>();
942 let cells = data.cells.borrow();
943 let cell = cells.get(&TypeId::of::<R>())?;
944 Some(
945 cell.state
946 .clone()
947 .downcast::<Slot<R>>()
948 .expect("Scope cell type mismatch for this TypeId - unreachable, keyed by R"),
949 )
950 }
951
952 pub fn seed<R: Reducer>(&self, state: R) {
956 let data = self.installing("seeding a reducer");
957 data.note_kind::<R>();
958 let replaced = data
959 .cells
960 .borrow_mut()
961 .insert(TypeId::of::<R>(), Cell::holding(Rc::new(Slot::new(Rc::new(state)))));
962 drop(replaced);
963 }
964
965 pub fn answers<M: 'static>(&self, answer: impl Fn(M) + 'static) {
972 let data = self.installing("answering an action");
973 let answer: Rc<dyn Fn(M)> = Rc::new(answer);
974 let section = data.current_section();
975
976 let mut sections = data.sections.borrow_mut();
977 while sections.len() <= section {
978 sections.push(HashMap::new());
979 }
980 sections[section].insert(TypeId::of::<M>(), Rc::new(answer) as Rc<dyn Any>);
981 }
982
983 pub fn answerer<M: 'static>(&self, section: usize) -> Option<Rc<dyn Fn(M)>> {
985 self.data()?.answerer::<M>(section)
986 }
987
988 pub fn first_answerer<M: 'static>(&self) -> Option<Rc<dyn Fn(M)>> {
992 let data = self.data()?;
993 let sections = data.sections.borrow().len();
994 (0..sections).find_map(|section| data.answerer::<M>(section))
995 }
996
997 pub fn push<R: Reducer>(&self, update: R::Update) {
1005 let Some(data) = self.data() else { return };
1006 let carried: Option<Box<dyn Any>> = data
1007 .is_observed(TypeId::of::<R>())
1008 .then(|| Box::new(update.clone()) as Box<dyn Any>);
1009 let state = data.state::<R>();
1010 drop(data);
1011
1012 {
1013 let mut state = state.borrow_mut();
1014 Rc::make_mut(&mut state).reduce(update);
1015 }
1016
1017 crate::notify::mark(*self, TypeId::of::<R>(), carried);
1018 }
1019
1020 pub fn observe<R: Reducer>(&self, callback: impl Fn(&R::Update) + 'static) -> Subscription {
1028 let Some(data) = self.data() else {
1029 return Subscription::inert();
1030 };
1031 let id = NEXT_SUBSCRIBER_ID.fetch_add(1, Ordering::Relaxed);
1032 {
1033 let mut cells = data.cells.borrow_mut();
1034 let cell = cells.entry(TypeId::of::<R>()).or_insert_with(Cell::empty::<R>);
1035 cell.observers.borrow_mut().push((
1036 id,
1037 Rc::new(move |update: &dyn Any| {
1038 if let Some(update) = update.downcast_ref::<R::Update>() {
1039 callback(update);
1040 }
1041 }),
1042 ));
1043 }
1044
1045 let scope = *self;
1046 Subscription {
1047 unsubscribe: Some(Box::new(move || {
1048 let Some(data) = scope.data() else { return };
1049 if let Some(cell) = data.cells.borrow_mut().get_mut(&TypeId::of::<R>()) {
1050 cell.observers.borrow_mut().retain(|(oid, _)| *oid != id);
1051 }
1052 })),
1053 }
1054 }
1055
1056 pub(crate) fn listeners_of(&self, cell: TypeId) -> Vec<Rc<dyn Fn()>> {
1057 let Some(data) = self.data() else {
1058 return Vec::new();
1059 };
1060 let cells = data.cells.borrow();
1061 cells
1062 .get(&cell)
1063 .map(|cell| cell.listeners.borrow().iter().map(|(_, f)| f.clone()).collect())
1064 .unwrap_or_default()
1065 }
1066
1067 pub(crate) fn observers_of(&self, cell: TypeId) -> Vec<Rc<dyn Fn(&dyn Any)>> {
1068 let Some(data) = self.data() else {
1069 return Vec::new();
1070 };
1071 let cells = data.cells.borrow();
1072 cells
1073 .get(&cell)
1074 .map(|cell| cell.observers.borrow().iter().map(|(_, f)| f.clone()).collect())
1075 .unwrap_or_default()
1076 }
1077
1078 pub fn subscribe<R: Reducer>(&self, callback: impl Fn() + 'static) -> Subscription {
1079 let Some(data) = self.data() else {
1080 return Subscription::inert();
1081 };
1082 let id = NEXT_SUBSCRIBER_ID.fetch_add(1, Ordering::Relaxed);
1083 {
1084 let mut cells = data.cells.borrow_mut();
1085 let cell = cells.entry(TypeId::of::<R>()).or_insert_with(Cell::empty::<R>);
1086 cell.listeners.borrow_mut().push((id, Rc::new(callback)));
1087 }
1088
1089 let scope = *self;
1090 Subscription {
1091 unsubscribe: Some(Box::new(move || {
1092 let Some(data) = scope.data() else { return };
1093 if let Some(cell) = data.cells.borrow_mut().get_mut(&TypeId::of::<R>()) {
1094 cell.listeners.borrow_mut().retain(|(lid, _)| *lid != id);
1095 }
1096 })),
1097 }
1098 }
1099
1100 pub fn binding<R: Reducer>(&self) -> crate::binding::ReducerBinding<R> {
1103 crate::binding::ReducerBinding::new(*self)
1104 }
1105
1106 pub fn own_subscription(&self, subscription: BusSubscription) {
1107 self.own(DropGuard(subscription));
1108 }
1109
1110 pub fn snapshot_states(&self) -> HashMap<TypeId, Rc<dyn Any>> {
1114 let Some(data) = self.data() else {
1115 return HashMap::new();
1116 };
1117 data.cells
1118 .borrow()
1119 .iter()
1120 .map(|(type_id, cell)| (*type_id, cell.state.clone()))
1121 .collect()
1122 }
1123
1124 pub fn restore_states(&self, states: HashMap<TypeId, Rc<dyn Any>>) {
1129 let data = self.installing("restoring state");
1130 let mut replaced = Vec::new();
1131 {
1132 let mut cells = data.cells.borrow_mut();
1133 for (type_id, state) in states {
1134 replaced.extend(cells.insert(type_id, Cell::holding(state)));
1135 }
1136 }
1137 drop(replaced);
1138 }
1139
1140 pub fn own<R: Teardown>(&self, resource: R) {
1143 match self.data() {
1144 Some(data) => data
1145 .teardowns
1146 .borrow_mut()
1147 .push(Box::new(move || resource.teardown())),
1148 None => resource.teardown(),
1149 }
1150 }
1151
1152 pub fn on_leave(&self, guard: impl Fn() -> crate::guard::Verdict + 'static) {
1159 let data = self.installing("guarding a leave");
1160 data.leave_guards.borrow_mut().push(Rc::new(guard));
1161 }
1162
1163 pub fn leave_guards(&self) -> Vec<Rc<dyn Fn() -> crate::guard::Verdict>> {
1168 self.data()
1169 .map(|data| data.leave_guards.borrow().clone())
1170 .unwrap_or_default()
1171 }
1172
1173 pub fn sleep(&self) {
1180 if let Some(data) = self.data() {
1181 data.asleep.set(true);
1182 }
1183 }
1184
1185 pub fn wake(&self) {
1188 let Some(data) = self.data() else { return };
1189 data.asleep.set(false);
1190
1191 let hooks = data.wake_hooks.borrow().clone();
1192 drop(data);
1193 for hook in hooks {
1194 hook();
1195 }
1196 }
1197
1198 pub fn is_awake(&self) -> bool {
1200 self.data().is_some_and(|data| !data.asleep.get())
1201 }
1202
1203 pub fn awake(&self) -> Awake {
1205 match self.data() {
1206 Some(data) => Awake(data.asleep.clone()),
1207 None => Awake(Rc::new(std::cell::Cell::new(true))),
1208 }
1209 }
1210
1211 pub fn on_wake(&self, hook: impl Fn() + 'static) {
1214 let data = self.installing("waiting for a wake");
1215 data.wake_hooks.borrow_mut().push(Rc::new(hook));
1216 }
1217}
1218
1219impl ScopeData {
1220 fn section_name(&self, section: usize) -> Option<&'static str> {
1221 self.section_names.borrow().get(section).copied().flatten()
1222 }
1223
1224 fn current_section(&self) -> usize {
1225 self.installing.borrow().last().copied().unwrap_or(0)
1226 }
1227
1228 fn note_kind<R: Reducer>(&self) {
1229 self.kinds
1230 .borrow_mut()
1231 .entry(TypeId::of::<R>())
1232 .or_insert_with(Kind::of::<R>);
1233 }
1234
1235 fn state<R: Reducer>(&self) -> Rc<Slot<R>> {
1236 self.note_kind::<R>();
1237 let mut cells = self.cells.borrow_mut();
1238 let cell = cells.entry(TypeId::of::<R>()).or_insert_with(Cell::empty::<R>);
1239 cell.state
1240 .clone()
1241 .downcast::<Slot<R>>()
1242 .expect("Scope cell type mismatch for this TypeId - unreachable, keyed by R")
1243 }
1244
1245 fn answerer<M: 'static>(&self, section: usize) -> Option<Rc<dyn Fn(M)>> {
1246 let sections = self.sections.borrow();
1247 let answer = sections.get(section)?.get(&TypeId::of::<M>())?.clone();
1248 answer.downcast::<Rc<dyn Fn(M)>>().ok().map(|a| (*a).clone())
1249 }
1250
1251 fn is_observed(&self, cell: TypeId) -> bool {
1252 self.cells
1253 .borrow()
1254 .get(&cell)
1255 .is_some_and(|cell| !cell.observers.borrow().is_empty())
1256 }
1257
1258 fn tear_down(&self) {
1261 let teardowns = std::mem::take(&mut *self.teardowns.borrow_mut());
1262 for teardown in teardowns.into_iter().rev() {
1263 teardown();
1264 }
1265 }
1266}
1267
1268pub trait Teardown: 'static {
1272 fn teardown(self);
1273}
1274
1275impl Teardown for JoinHandle<()> {
1276 fn teardown(self) {
1277 self.abort();
1278 }
1279}
1280
1281impl<A: 'static> Teardown for Addr<A> {
1282 fn teardown(self) {
1283 self.dispose();
1284 drop(self);
1285 }
1286}
1287
1288pub struct DropGuard<T: 'static>(pub T);
1291
1292impl<T: 'static> Teardown for DropGuard<T> {
1293 fn teardown(self) {
1294 drop(self.0);
1295 }
1296}
1297
1298#[cfg(test)]
1299mod tests {
1300 use super::*;
1301 use std::sync::Arc;
1302 use std::sync::atomic::{AtomicBool, Ordering};
1303
1304 #[derive(Default, Clone, PartialEq, Debug)]
1305 struct Counter {
1306 value: i32,
1307 }
1308
1309 #[derive(Clone)]
1310 enum CounterMsg {
1311 Set(i32),
1312 }
1313
1314 impl Reducer for Counter {
1315 type Update = CounterMsg;
1316
1317 fn reduce(&mut self, update: CounterMsg) {
1318 match update {
1319 CounterMsg::Set(v) => self.value = v,
1320 }
1321 }
1322 }
1323
1324 struct Said(&'static str, Rc<RefCell<Vec<&'static str>>>);
1325
1326 impl Teardown for Said {
1327 fn teardown(self) {
1328 self.1.borrow_mut().push(self.0);
1329 }
1330 }
1331
1332 #[test]
1333 fn a_tree_takes_every_scope_in_it_along_when_its_owner_lets_go() {
1334 let said = Rc::new(RefCell::new(Vec::new()));
1335 let tree = ScopeTree::new();
1336 let window = tree.child();
1337 let page = window.child();
1338 tree.own(Said("application", said.clone()));
1339 page.own(Said("page", said.clone()));
1340
1341 drop(tree);
1342
1343 assert_eq!(*said.borrow(), ["page", "application"]);
1344 assert!(!window.is_alive());
1345 assert!(!page.is_alive());
1346 }
1347
1348 #[test]
1349 fn removing_a_scope_tears_down_what_is_under_it_first() {
1350 let said = Rc::new(RefCell::new(Vec::new()));
1351 let root = ScopeTree::new();
1352 let older = root.child();
1353 let below = older.child();
1354 let newer = root.child();
1355
1356 for (scope, name) in [
1357 (root.scope(), "root"),
1358 (older, "older"),
1359 (below, "below"),
1360 (newer, "newer"),
1361 ] {
1362 scope.own(Said(name, said.clone()));
1363 }
1364 root.remove();
1365
1366 assert_eq!(*said.borrow(), ["newer", "below", "older", "root"]);
1367 }
1368
1369 #[test]
1370 fn a_scope_lets_go_of_what_it_holds_in_the_reverse_of_the_order_it_took_it() {
1371 let said = Rc::new(RefCell::new(Vec::new()));
1372 let scope = ScopeTree::new();
1373 scope.own(Said("store", said.clone()));
1374 scope.own(Said("actor reading the store", said.clone()));
1375
1376 scope.remove();
1377
1378 assert_eq!(*said.borrow(), ["actor reading the store", "store"]);
1379 }
1380
1381 #[test]
1382 fn a_removed_scope_stays_gone_and_its_slot_names_another_one() {
1383 let root = ScopeTree::new();
1384 let gone = root.child();
1385 gone.remove();
1386 let next = root.child();
1387
1388 assert!(!gone.is_alive());
1389 assert!(next.is_alive());
1390 assert_ne!(gone, next);
1391 assert_ne!(gone.key(), next.key());
1392 assert_eq!(next.parent(), Some(root.scope()));
1393 }
1394
1395 #[test]
1396 fn a_scope_goes_when_it_is_removed_however_many_name_it() {
1397 let said = Rc::new(RefCell::new(Vec::new()));
1398 let tree = ScopeTree::new();
1399 let scope = tree.child();
1400 let named_elsewhere = scope;
1401 scope.own(Said("torn down", said.clone()));
1402
1403 scope.remove();
1404
1405 assert_eq!(*said.borrow(), ["torn down"]);
1406 assert!(!named_elsewhere.is_alive());
1407 }
1408
1409 #[test]
1410 fn what_a_removed_scope_is_handed_is_torn_down_at_once() {
1411 let said = Rc::new(RefCell::new(Vec::new()));
1412 let scope = ScopeTree::new();
1413 scope.remove();
1414
1415 scope.own(Said("at once", said.clone()));
1416
1417 assert_eq!(*said.borrow(), ["at once"]);
1418 }
1419
1420 #[derive(Debug)]
1421 struct Ticker(u32);
1422
1423 struct Tick;
1424
1425 guinea_macros::actor! {
1426 Ticker {
1427 handlers {
1428 Tick
1429 }
1430 }
1431 }
1432
1433 impl crate::actor::Handler<Tick> for Ticker {
1434 fn handle(&mut self, _: Tick, _cx: crate::actor::Cx<Self, Tick>) {
1435 self.0 += 1;
1436 }
1437 }
1438
1439 fn ticker() -> Addr<Ticker> {
1440 Addr::new_managed_scoped(
1441 Ticker(0),
1442 crate::actor::UiThreadToken::dangerously_create_token_unchecked(),
1443 )
1444 }
1445
1446 fn watched(run: impl FnOnce()) -> Vec<crate::devtools::Change> {
1447 let seen = Rc::new(RefCell::new(Vec::new()));
1448 let sink = seen.clone();
1449 crate::devtools::watch(move |change| sink.borrow_mut().push(change.clone()));
1450
1451 run();
1452 crate::devtools::stop_watching();
1453
1454 seen.take()
1455 }
1456
1457 #[test]
1458 fn devtools_hear_an_actor_a_scope_holds_come_and_go_and_whose_it_is() {
1459 use crate::devtools::Change;
1460
1461 let window = ScopeTree::new();
1462 window.set_window(7);
1463 let page = window.child();
1464 let addr = ticker();
1465 let id = addr.id();
1466
1467 let seen = watched(|| {
1468 page.hold_actor(&addr, Some("app::Clock"), Some("Time"));
1469 window.remove();
1470 });
1471
1472 assert_eq!(
1473 seen,
1474 [
1475 Change::ActorAdded {
1476 root: Some(7),
1477 id,
1478 type_name: crate::actor::short_type_name::<Ticker>(),
1479 owner: Owner {
1480 scope: Some(page.key()),
1481 feature: Some("app::Clock"),
1482 drives: Some("Time"),
1483 },
1484 },
1485 Change::ActorRemoved { root: Some(7), id },
1486 ]
1487 );
1488 }
1489
1490 #[test]
1491 fn a_window_reads_the_actors_under_it_and_one_by_id_without_the_rest() {
1492 let window = ScopeTree::new();
1493 let page = window.child();
1494 let (bumped, other) = (ticker(), ticker());
1495 window.hold_actor(&other, None, None);
1496 page.hold_actor(&bumped, None, None);
1497 bumped.send(Tick);
1498
1499 let mut held: Vec<usize> = window.actors().iter().map(|actor| actor.id).collect();
1500 held.sort_unstable();
1501 let mut expected = vec![bumped.id(), other.id()];
1502 expected.sort_unstable();
1503 assert_eq!(held, expected);
1504 assert_eq!(page.actors().len(), 1, "a page reads only what is under it");
1505
1506 let read = window.actor(bumped.id()).map(|actor| actor.state);
1507 assert_eq!(read, Some(format!("{:#?}", Ticker(1))));
1508 assert!(window.actor(usize::MAX).is_none());
1509
1510 window.remove();
1511 assert!(window.actors().is_empty(), "what a removed scope held is not listed");
1512 }
1513
1514 #[test]
1515 fn a_reducer_is_read_where_it_was_claimed_or_from_the_nearest_export_above() {
1516 #[derive(Clone, Default, Debug)]
1517 struct Hidden;
1518
1519 impl Reducer for Hidden {
1520 type Update = ();
1521
1522 fn reduce(&mut self, _: ()) {}
1523 }
1524
1525 let root = ScopeTree::new();
1526 let layout = root.child();
1527 let page = layout.child();
1528 root.note_reducer_owner::<Counter>();
1529 root.note_export::<Counter>();
1530 layout.note_reducer_owner::<Counter>();
1531 layout.note_export::<Counter>();
1532 layout.note_reducer_owner::<Hidden>();
1533 page.note_reducer_owner::<Hidden>();
1534
1535 assert_eq!(page.owner_of::<Counter>(), Some(layout), "the nearest export wins");
1536 assert_eq!(page.owner_of::<Hidden>(), Some(page), "what it claimed is its own");
1537 assert_eq!(layout.child().owner_of::<Hidden>(), None, "claimed above is not exported");
1538 assert_eq!(root.owner_of::<Counter>(), Some(root.scope()));
1539 }
1540
1541 #[test]
1542 fn a_described_state_names_the_feature_that_claimed_it() {
1543 #[derive(Clone, Default, Debug)]
1544 struct Loose;
1545
1546 impl Reducer for Loose {
1547 type Update = ();
1548
1549 fn reduce(&mut self, _: ()) {}
1550 }
1551
1552 let scope = ScopeTree::new();
1553 scope.open_section(Some("app::CounterFeature"), None);
1554 scope.note_reducer_owner::<Counter>();
1555 scope.state::<Counter>();
1556 scope.close_section();
1557 scope.state::<Loose>();
1558
1559 let described = scope.describe_states();
1560 let feature = |name: &str| {
1561 described
1562 .iter()
1563 .find(|state| state.type_name.ends_with(name))
1564 .map(|state| state.feature)
1565 };
1566 assert_eq!(feature("Counter"), Some(Some("app::CounterFeature")));
1567 assert_eq!(feature("Loose"), Some(None));
1568 }
1569
1570 #[test]
1571 fn state_survives_unmount_remount_within_a_live_store() {
1572 let store = ScopeTree::new();
1573
1574 let first_read = store.state::<Counter>();
1575 assert_eq!(first_read.borrow().value, 0);
1576 store.push::<Counter>(CounterMsg::Set(42));
1577
1578 drop(first_read);
1579
1580 let second_read = store.state::<Counter>();
1581 assert_eq!(second_read.borrow().value, 42);
1582 }
1583
1584 #[test]
1585 fn push_notifies_subscribers_and_unsubscribe_stops_it() {
1586 let store = ScopeTree::new();
1587 let seen = Rc::new(RefCell::new(Vec::new()));
1588
1589 let seen_for_sub = seen.clone();
1590 let read = store.scope();
1591 let sub = store.subscribe::<Counter>(move || {
1592 seen_for_sub.borrow_mut().push(read.state::<Counter>().borrow().value);
1593 });
1594
1595 store.push::<Counter>(CounterMsg::Set(1));
1596 store.push::<Counter>(CounterMsg::Set(2));
1597 assert_eq!(*seen.borrow(), vec![1, 2]);
1598
1599 drop(sub);
1600 store.push::<Counter>(CounterMsg::Set(3));
1601 assert_eq!(
1602 *seen.borrow(),
1603 vec![1, 2],
1604 "no further notifications after the Subscription is dropped"
1605 );
1606 }
1607
1608 #[tokio::test]
1609 async fn removing_the_store_aborts_owned_tasks() {
1610 let ran_to_completion = Arc::new(AtomicBool::new(false));
1611 let flag = ran_to_completion.clone();
1612
1613 let store = ScopeTree::new();
1614 let handle = tokio::spawn(async move {
1615 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
1616 flag.store(true, Ordering::SeqCst);
1617 });
1618 store.own(handle);
1619
1620 store.remove();
1621
1622 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
1623 assert!(
1624 !ran_to_completion.load(Ordering::SeqCst),
1625 "task should have been aborted when its owning scope was removed"
1626 );
1627 }
1628
1629 #[test]
1630 fn own_actor_disposes_the_registry_entry_on_removal() {
1631 let token = crate::actor::UiThreadToken::dangerously_create_token_unchecked();
1632 let addr = Addr::new_scoped((), token);
1633 let counter = addr.strong_count_ptr();
1634
1635 let store = ScopeTree::new();
1636 store.own(addr.clone());
1637 drop(addr);
1638
1639 assert!(
1640 Rc::strong_count(&counter) > 1,
1641 "REGISTRY should still hold the actor alive while its Scope is alive"
1642 );
1643
1644 store.remove();
1645
1646 assert_eq!(
1647 Rc::strong_count(&counter),
1648 1,
1649 "removing the Scope should dispose the REGISTRY entry, \
1650 leaving only this test's own counter handle"
1651 );
1652 }
1653}