Skip to main content

guinea_core/
scope.rs

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
17/// Numbers every scope once, from 1: a node that holds no scope is numbered 0.
18static NEXT_SERIAL: AtomicU64 = AtomicU64::new(1);
19
20pub struct Subscription {
21    unsubscribe: Option<Box<dyn FnOnce()>>,
22}
23
24impl Subscription {
25    /// A subscription to something that no longer exists - dropping it does
26    /// nothing.
27    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
60/// State, and how it changes.
61///
62/// The type that implements this **is** the state - there is no `type State`,
63/// because the struct is already there and already named. Two items, both
64/// about state; a reducer cannot know who asked, only what changed.
65///
66/// ```ignore
67/// #[derive(Clone, Debug, Default)]
68/// pub struct Processes { pub items: Vec<String> }
69///
70/// pub enum Refreshed { Items(Vec<String>) }
71///
72/// impl Reducer for Processes {
73///     type Update = Refreshed;
74///
75///     fn reduce(&mut self, update: Refreshed) {
76///         match update { Refreshed::Items(items) => self.items = items }
77///     }
78/// }
79/// ```
80///
81/// Usually it is written as a function instead - `#[reducer] fn processes(this:
82/// &mut Processes, update: Refreshed)` - and the attribute writes this impl.
83///
84/// There is no actor here, and there must not be. A reducer knows its own
85/// state and how it changes; naming the actor that happens to drive it would
86/// put the domain's plumbing into the one declaration that is supposed to be
87/// free of it. What relates an action to an actor is
88/// [`Action`](crate::feature::Action), declared where actions already live.
89///
90/// `Clone` because a reader is handed the state as it is now, shared, and a
91/// change made while a reader still holds it goes to a copy.
92pub trait Reducer: Clone + Default + std::fmt::Debug + 'static {
93    /// What changes it. `Clone` because an observer is handed the update
94    /// itself, and `reduce` consumes it - the copy is made only when
95    /// something is actually observing.
96    type Update: Clone + 'static;
97
98    fn reduce(&mut self, update: Self::Update);
99}
100
101/// Where a reducer's state lives: the current value, shared with whoever read
102/// it last.
103pub 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/// What a cell holds, for printing it without knowing the type.
112#[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/// A subscription a feature made, as devtools see it.
134#[derive(Clone, Debug, PartialEq)]
135pub struct Listener {
136    pub event: &'static str,
137    /// The actor that listens, or `None` for a feature's own callback.
138    pub actor: Option<&'static str>,
139    pub bus: crate::trace::Bus,
140    pub feature: Option<&'static str>,
141}
142
143/// One reducer as devtools see it.
144#[derive(Clone, Debug, PartialEq)]
145pub struct DescribedState {
146    pub type_name: &'static str,
147    pub state: String,
148    /// The feature that claimed it; `None` for the segment's own.
149    pub feature: Option<&'static str>,
150    /// Where it was claimed: the `cx.state::<R>()` that did it.
151    pub declared: Option<Declared>,
152}
153
154/// A feature installed in a scope, as devtools see it.
155#[derive(Clone, Copy, Debug, PartialEq)]
156pub struct Installed {
157    pub name: &'static str,
158    /// Where `impl Feature` was written, when `#[installs]` wrote it.
159    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/// Where a segment's state, features and resources live, from the moment it
177/// is installed until it is removed.
178///
179/// A name for one, and `Copy`: the [`ScopeTree`] it is in owns what each scope
180/// holds, and nothing else does. Holding a `Scope` does not keep it alive, so
181/// a scope goes exactly when [`remove`](Self::remove) is called on it or on a
182/// scope above it - children first - or when its tree's owner lets go of the
183/// tree, and not whenever the last of whoever happened to be holding it lets
184/// go.
185///
186/// A removed scope's name never comes back: every scope is numbered once, so
187/// an old `Scope` keeps naming the scope that is gone, and
188/// [`is_alive`](Self::is_alive) says so.
189#[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
202/// Which of its parent's outlets a scope sits in. A layout has one today,
203/// [`MAIN`].
204pub type Outlet = &'static str;
205
206/// The outlet a scope sits in unless it is said otherwise.
207pub const MAIN: Outlet = "main";
208
209#[derive(Default)]
210struct ScopeData {
211    cells: RefCell<HashMap<TypeId, Cell>>,
212    /// Kept apart from `cells`: a cell restored from the state cache arrives
213    /// erased, and learns its type again at the next typed access.
214    kinds: RefCell<HashMap<TypeId, Kind>>,
215    teardowns: RefCell<Vec<Box<dyn FnOnce()>>>,
216    /// Features (identified by their `install` function's own type) that
217    /// were explicitly installed *in this scope*. Separate from `cells`
218    /// (which tracks `Reducer` state/actions) because a feature can spawn
219    /// several actors/reducers as one unit - this tracks the unit itself,
220    /// so a descendant scope can find out "has an ancestor already taken
221    /// ownership of this feature" without knowing which reducer types it
222    /// happens to use internally.
223    installed_features: RefCell<HashSet<TypeId>>,
224    /// The reducers this scope lets segments below it read. See
225    /// [`Scope::note_export`].
226    ///
227    /// Flat, unlike the answerers below: visibility is not per-instance, and
228    /// two instances of one feature export two different reducer types anyway.
229    exports: RefCell<HashSet<TypeId>>,
230    /// What this scope answers, one map per installed feature.
231    ///
232    /// Not one map: an action type is the same for every instance of a
233    /// feature, so `ListFeature<Recent>` and `ListFeature<Archived>` both
234    /// answer `Refresh`, and a flat map would let whichever installed last
235    /// answer for both. Section 0 is the segment's own, outside any feature.
236    sections: RefCell<Vec<HashMap<TypeId, Rc<dyn Any>>>>,
237    /// Which feature each section belongs to.
238    section_names: RefCell<Vec<Option<&'static str>>>,
239    /// Where each section's feature was written.
240    section_declarations: RefCell<Vec<Option<Declared>>>,
241    /// Where each reducer was claimed.
242    declarations: RefCell<HashMap<TypeId, Declared>>,
243    /// What this scope's features subscribed to.
244    listeners: RefCell<Vec<Listener>>,
245    /// Which section claimed each reducer - what turns "the state I was
246    /// reading" into "the instance that owns it".
247    owners: RefCell<HashMap<TypeId, usize>>,
248    /// The sections currently being installed, innermost last. A feature is
249    /// free to install another one.
250    installing: RefCell<Vec<usize>>,
251    /// Asked before this scope is torn down. See [`Scope::on_leave`].
252    leave_guards: RefCell<Vec<Rc<dyn Fn() -> crate::guard::Verdict>>>,
253    /// Set while a router keeps this scope without showing it. See
254    /// [`Scope::sleep`].
255    asleep: Rc<std::cell::Cell<bool>>,
256    /// Run when it is shown again. See [`Scope::on_wake`].
257    wake_hooks: RefCell<Vec<Rc<dyn Fn()>>>,
258    /// The window this scope is the root of. See [`Scope::set_window`].
259    window: std::cell::Cell<Option<u64>>,
260    /// The actors it holds, for devtools to read. See [`Scope::hold_actor`].
261    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
302/// Tells devtools an actor is no longer listed, when its scope goes.
303struct 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/// Every scope in one [`ScopeTree`], and which sits under which.
326#[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    /// Takes the scope at `index` and everything under it out of the tree,
371    /// children before their parent and the newest child first, and hands
372    /// back what they held in that order.
373    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    /// Where a `Scope` finds its tree. Weak: each tree is its [`ScopeTree`]'s,
408    /// and the thread ending lets go of nothing here but names.
409    static TREES: RefCell<Vec<Weak<RefCell<Tree>>>> = const { RefCell::new(Vec::new()) };
410}
411
412/// Lists `tree`, in the first slot whose tree is gone, and says which.
413fn 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/// Whether a scope is awake, for something it owns to ask on its own.
439///
440/// Held rather than the scope itself, so that a timer the scope owns can ask
441/// from off the scope's own code paths.
442#[derive(Clone)]
443pub struct Awake(Rc<std::cell::Cell<bool>>);
444
445impl Awake {
446    /// Whether the scope is awake now.
447    pub fn now(&self) -> bool {
448        !self.0.get()
449    }
450}
451
452/// Removes its scope when dropped: for a child that nothing else will remove.
453#[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
476/// A tree of scopes, and the owner of every scope in it: letting go of it
477/// removes its root and everything under it, the last first.
478///
479/// Whoever hosts something holds one - an application, a window with no
480/// application around it, a test.
481pub 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    /// The scope at the top of the tree.
502    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    /// A new scope under this one, removed when this one is.
529    pub fn child(&self) -> Scope {
530        self.child_in(MAIN)
531    }
532
533    /// A new scope under this one, in `outlet`.
534    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    /// Removes this scope and every scope under it, now: children before
552    /// their parent, and each one's resources the last first.
553    ///
554    /// From the moment this is called, every `Scope` naming them is dead.
555    /// Removing a scope that is already gone does nothing.
556    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    /// Whether this scope is still there.
569    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    /// The scope this one sits under, if it has one and it is still there.
575    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    /// Which of its parent's outlets this scope sits in.
584    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    /// The scopes above this one, the nearest first.
590    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    /// Where `R` is read from here: this scope if it claimed `R`, or the
609    /// nearest one above that exports it.
610    ///
611    /// The two ends are asked different questions on purpose. A segment may
612    /// read anything it claimed itself; what a scope above claimed is its own
613    /// business unless it said otherwise in `Exports`.
614    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    /// Says this scope is the root of window `id`, as `RootId::get` numbers
623    /// it: what devtools are told the actors under it belong to.
624    pub fn set_window(&self, id: u64) {
625        if let Some(data) = self.data() {
626            data.window.set(Some(id));
627        }
628    }
629
630    /// The window this scope is under, if it is under one.
631    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    /// Lists `addr` among the actors this scope holds, for devtools, until
637    /// the scope is removed - `feature` created it, and it drives `drives`
638    /// when it was made to.
639    ///
640    /// Only the listing: what ends the actor is still whoever owns it, which
641    /// for an actor of a segment is [`own`](Self::own) on this same scope.
642    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    /// The actors this scope and every scope under it hold, read now.
678    pub fn actors(&self) -> Vec<ActorSnapshot> {
679        read_all(self.subtree())
680    }
681
682    /// The actor `id`, if this scope or one under it holds it, read now; the
683    /// others are not read.
684    pub fn actor(&self, id: usize) -> Option<ActorSnapshot> {
685        read_one(self.subtree(), id)
686    }
687
688    /// The actors this scope holds itself, read now - not those of the
689    /// scopes under it.
690    pub fn actors_here(&self) -> Vec<ActorSnapshot> {
691        read_all(vec![*self])
692    }
693
694    /// The actor `id`, if this scope holds it itself, read now.
695    pub fn actor_here(&self, id: usize) -> Option<ActorSnapshot> {
696        read_one(vec![*self], id)
697    }
698
699    /// This scope and every scope under it, children first.
700    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    /// Removes this scope when the guard is dropped.
717    pub fn guard(&self) -> ScopeGuard {
718        ScopeGuard(*self)
719    }
720
721    /// Identifies this scope, and never another one.
722    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    /// Marks feature `F` (its `install` function, used purely as a type
740    /// identity) as owned by this scope. `F` must not already be marked
741    /// here - two different call sites both claiming ownership of the same
742    /// feature in the same scope is a setup bug, not something to merge
743    /// silently.
744    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    /// Whether anything in this scope has claimed `R`.
754    ///
755    /// What tells an export that was earned from one that was only declared.
756    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    /// Notes where reducer `R` was claimed, the first claim winning.
769    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    /// The manifest directory of the feature installing now, for a claim that
775    /// only knows the file the compiler gave it.
776    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    /// Opens a section for what is about to install - the feature `name`, or
784    /// something that is not a feature, such as a plugin. Returns its index.
785    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    /// The feature a section belongs to; `None` for the segment's own.
806    pub fn section_name(&self, section: usize) -> Option<&'static str> {
807        self.data()?.section_name(section)
808    }
809
810    /// Every feature installed here, in the order they were.
811    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    /// The feature being installed right now, if any.
831    pub fn current_feature(&self) -> Option<&'static str> {
832        let data = self.data()?;
833        data.section_name(data.current_section())
834    }
835
836    /// Notes that whatever is installing listens to `event` on `bus` - through
837    /// `actor` when an actor does the listening.
838    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    /// The section being installed, or the segment's own when none is.
867    pub fn current_section(&self) -> usize {
868        self.data().map_or(0, |data| data.current_section())
869    }
870
871    /// Which section owns `R` - the instance whose dispatcher a reader of `R`
872    /// should be handed.
873    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    /// Whether feature `F` was marked installed in *this exact* scope.
880    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    /// Marks `R` as readable from segments below this one.
886    ///
887    /// Called by `cx.install::<F>()` for everything in `F::Exports`, and by
888    /// an application's `export::<R>()`. A reducer a feature claimed but did not export stays
889    /// visible to the feature itself and invisible from below - which is the
890    /// whole difference between a feature and a folder.
891    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    /// Whether `R` is readable from below this scope.
897    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    /// Every reducer this scope holds whose type it has seen, printed.
903    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    /// `R`'s state here, created from `R::default()` on first read.
929    ///
930    /// A removed scope has no state to hand out: what comes back is a fresh
931    /// default nobody else sees.
932    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    /// Sets `R`'s starting value instead of `R::default()`. Call before
953    /// anything else touches `R` in this scope - overwrites any existing cell,
954    /// dropping its listeners.
955    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    /// Says that this scope answers `M`, and how.
966    ///
967    /// Keyed by the action, not by whoever answers it - which is what keeps
968    /// the answerer out of every signature the UI touches. `actor!` calls this
969    /// for each handler it lists; a domain that runs on tasks, or a channel,
970    /// or a plain closure over a `RefCell`, calls it itself.
971    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    /// What answers `M` in one section of this scope, if anything does.
984    pub fn answerer<M: 'static>(&self, section: usize) -> Option<Rc<dyn Fn(M)>> {
985        self.data()?.answerer::<M>(section)
986    }
987
988    /// What answers `M` anywhere in this scope - the first feature that does,
989    /// in the order they installed. For a sender that knows the action and
990    /// not which state it was reading.
991    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    /// Applies `msg` to `F`'s state now, and marks the cell so its listeners
998    /// run at the next [`notify::drain`](crate::notify::drain).
999    ///
1000    /// The state is current the moment this returns; what waits is the
1001    /// redraw. Nothing a listener does - navigating, dropping scopes, sending
1002    /// to another actor - happens on the stack of whoever pushed. A no-op once
1003    /// the scope is removed.
1004    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    /// Watches *what happened* to `F`, not merely that something did.
1021    ///
1022    /// A listener from [`subscribe`](Self::subscribe) learns the cell changed
1023    /// and reads the new state; an observer is handed the update itself, which
1024    /// is what tells a rename apart from a whole new list. Runs during the
1025    /// drain, before any listener, so state that depends on this one has
1026    /// settled by the time anything draws.
1027    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    /// A handle to `R`'s state and actions in this scope, for code that reads
1101    /// or watches them without being a UI hook. See [`crate::binding`].
1102    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    /// Returns a shallow clone of every reducer state currently held by this
1111    /// scope, keyed by the reducer's `TypeId`. Used by the router to cache
1112    /// page state in memory while the page is not mounted.
1113    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    /// Pre-populates reducer cells with previously cached states. This is the
1125    /// inverse of [`snapshot_states`](Self::snapshot_states): when a page is
1126    /// remounted, restoring its state before `install` runs lets the page find
1127    /// ready data instead of defaults.
1128    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    /// Binds any [`Teardown`] resource to this scope's lifetime: it is torn
1141    /// down when the scope is - at once, if the scope is already gone.
1142    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    /// Asked before this scope is torn down by a navigation.
1153    ///
1154    /// Registered during `install`, which is the whole reason leaving and
1155    /// entering are declared in different places: on the way out the scope
1156    /// exists, so the guard can read its own state - which is what "unsaved
1157    /// changes" is. On the way in there is nothing to read yet.
1158    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    /// The guards to ask, in the order they were registered.
1164    ///
1165    /// Cloned out rather than borrowed: a guard is free to touch this scope,
1166    /// and the caller runs them while deciding.
1167    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    /// Puts this scope to sleep: kept, with its state and actors, but deaf.
1174    ///
1175    /// What it owns stops hearing anything while it sleeps - its timers skip
1176    /// their ticks, its actors and callbacks are not told what the buses
1177    /// carry - and nothing is queued for later: what happened meanwhile is
1178    /// missed. A router does this to a `keep` segment it leaves.
1179    pub fn sleep(&self) {
1180        if let Some(data) = self.data() {
1181            data.asleep.set(true);
1182        }
1183    }
1184
1185    /// Wakes this scope, then runs what asked to hear of it, in the order it
1186    /// asked.
1187    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    /// Whether this scope is there and awake. A removed one is neither.
1199    pub fn is_awake(&self) -> bool {
1200        self.data().is_some_and(|data| !data.asleep.get())
1201    }
1202
1203    /// Whether this scope is awake, for what it owns to ask later.
1204    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    /// Runs `hook` every time this scope wakes: for a feature to catch up on
1212    /// what it missed while it slept.
1213    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    /// Lets go of what this scope took on, the last first: what came later
1259    /// may rest on what came before it.
1260    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
1268/// A resource whose lifetime can be bound to a [`Scope`] via [`Scope::own`].
1269/// Implement per resource kind; the "blanket" over arbitrary `T` is the
1270/// [`DropGuard`] newtype, so specialized teardowns never overlap.
1271pub 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
1288/// Blanket teardown for resources that just need dropping
1289/// (`scope.own(DropGuard(resource))` when no specialized impl exists).
1290pub 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}