Skip to main content

guinea_core/scope/
mod.rs

1use std::any::{Any, TypeId};
2use std::cell::RefCell;
3use std::collections::{HashMap, HashSet};
4use std::rc::Rc;
5
6use crate::actor::shape::Declared;
7
8mod actors;
9mod features;
10mod lifetime;
11mod state;
12mod tree;
13
14#[cfg(test)]
15mod tests;
16
17pub use features::{Installed, Listener};
18pub use lifetime::{Awake, DropGuard, Teardown};
19pub use state::{DescribedState, Reducer, Slot, StateHandle, Subscription};
20pub use tree::{ScopeGuard, ScopeTree};
21
22use actors::HeldActor;
23use state::{Cell, Kind};
24use tree::{Tree, tree};
25
26/// Where a segment's state, features and resources live, from the moment it
27/// is installed until it is removed.
28///
29/// A name for one, and `Copy`: the [`ScopeTree`] it is in owns what each scope
30/// holds, and nothing else does. Holding a `Scope` does not keep it alive, so
31/// a scope goes exactly when [`remove`](Self::remove) is called on it or on a
32/// scope above it - children first - or when its tree's owner lets go of the
33/// tree, and not whenever the last of whoever happened to be holding it lets
34/// go.
35///
36/// A removed scope's name never comes back: every scope is numbered once, so
37/// an old `Scope` keeps naming the scope that is gone, and
38/// [`is_alive`](Self::is_alive) says so.
39#[derive(Clone, Copy, PartialEq, Eq, Hash)]
40pub struct Scope {
41    tree: u32,
42    index: u32,
43    serial: u64,
44}
45
46impl std::fmt::Debug for Scope {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        write!(f, "Scope({})", self.serial)
49    }
50}
51
52/// Which of its parent's outlets a scope sits in. A layout has one today,
53/// [`MAIN`].
54pub type Outlet = &'static str;
55
56/// The outlet a scope sits in unless it is said otherwise.
57pub const MAIN: Outlet = "main";
58
59#[derive(Default)]
60struct ScopeData {
61    cells: RefCell<HashMap<TypeId, Cell>>,
62    /// Kept apart from `cells`: a cell restored from the state cache arrives
63    /// erased, and learns its type again at the next typed access.
64    kinds: RefCell<HashMap<TypeId, Kind>>,
65    teardowns: RefCell<Vec<Box<dyn FnOnce()>>>,
66    /// Features (identified by their `install` function's own type) that
67    /// were explicitly installed *in this scope*. Separate from `cells`
68    /// (which tracks `Reducer` state/actions) because a feature can spawn
69    /// several actors/reducers as one unit - this tracks the unit itself,
70    /// so a descendant scope can find out "has an ancestor already taken
71    /// ownership of this feature" without knowing which reducer types it
72    /// happens to use internally.
73    installed_features: RefCell<HashSet<TypeId>>,
74    /// The reducers this scope lets segments below it read. See
75    /// [`Scope::note_export`].
76    ///
77    /// Flat, unlike the answerers below: visibility is not per-instance, and
78    /// two instances of one feature export two different reducer types anyway.
79    exports: RefCell<HashSet<TypeId>>,
80    /// What this scope answers, one map per installed feature.
81    ///
82    /// Not one map: an action type is the same for every instance of a
83    /// feature, so `ListFeature<Recent>` and `ListFeature<Archived>` both
84    /// answer `Refresh`, and a flat map would let whichever installed last
85    /// answer for both. Section 0 is the segment's own, outside any feature.
86    sections: RefCell<Vec<HashMap<TypeId, Rc<dyn Any>>>>,
87    /// Which feature each section belongs to.
88    section_names: RefCell<Vec<Option<&'static str>>>,
89    /// Where each section's feature was written.
90    section_declarations: RefCell<Vec<Option<Declared>>>,
91    /// Where each reducer was claimed.
92    declarations: RefCell<HashMap<TypeId, Declared>>,
93    /// What this scope's features subscribed to.
94    listeners: RefCell<Vec<Listener>>,
95    /// Which section claimed each reducer - what turns "the state I was
96    /// reading" into "the instance that owns it".
97    owners: RefCell<HashMap<TypeId, usize>>,
98    /// The sections currently being installed, innermost last. A feature is
99    /// free to install another one.
100    installing: RefCell<Vec<usize>>,
101    /// Asked before this scope is torn down. See [`Scope::on_leave`].
102    leave_guards: RefCell<Vec<Rc<dyn Fn() -> crate::guard::Verdict>>>,
103    /// Set while a router keeps this scope without showing it. See
104    /// [`Scope::sleep`].
105    asleep: Rc<std::cell::Cell<bool>>,
106    /// Run when it is shown again. See [`Scope::on_wake`].
107    wake_hooks: RefCell<Vec<Rc<dyn Fn()>>>,
108    /// The window this scope is the root of. See [`Scope::set_window`].
109    window: std::cell::Cell<Option<u64>>,
110    /// The actors it holds, for devtools to read. See [`Scope::hold_actor`].
111    actors: RefCell<Vec<HeldActor>>,
112}
113
114impl Scope {
115    /// A new scope under this one, removed when this one is.
116    pub fn child(&self) -> Scope {
117        self.child_in(MAIN)
118    }
119
120    /// A new scope under this one, in `outlet`.
121    pub fn child_in(&self, outlet: Outlet) -> Scope {
122        let tree =
123            tree(self.tree).unwrap_or_else(|| panic!("a child for {self:?}, whose tree is gone"));
124        let mut nodes = tree.borrow_mut();
125        assert!(
126            nodes.node(self.index, self.serial).is_some(),
127            "a child for {self:?}, which was removed"
128        );
129        let (index, serial) = nodes.insert(Some(self.index), outlet);
130
131        Scope {
132            tree: self.tree,
133            index,
134            serial,
135        }
136    }
137
138    /// Removes this scope and every scope under it, now: children before
139    /// their parent, and each one's resources the last first.
140    ///
141    /// From the moment this is called, every `Scope` naming them is dead.
142    /// Removing a scope that is already gone does nothing.
143    pub fn remove(&self) {
144        let Some(tree) = tree(self.tree) else {
145            return;
146        };
147        let detached = tree.borrow_mut().detach(self.index, self.serial);
148        drop(tree);
149
150        for data in detached {
151            data.tear_down();
152        }
153    }
154
155    /// Whether this scope is still there.
156    pub fn is_alive(&self) -> bool {
157        self.read(|tree| tree.node(self.index, self.serial).is_some())
158            .unwrap_or(false)
159    }
160
161    /// The scope this one sits under, if it has one and it is still there.
162    pub fn parent(&self) -> Option<Scope> {
163        self.read(|tree| {
164            let parent = tree.node(self.index, self.serial)?.parent?;
165            Some(self.named(tree, parent))
166        })
167        .flatten()
168    }
169
170    /// Which of its parent's outlets this scope sits in.
171    pub fn outlet(&self) -> Option<Outlet> {
172        self.read(|tree| tree.node(self.index, self.serial).map(|node| node.outlet))
173            .flatten()
174    }
175
176    /// The scopes above this one, the nearest first.
177    pub fn ancestors(&self) -> Vec<Scope> {
178        std::iter::successors(self.parent(), Scope::parent).collect()
179    }
180
181    /// The scopes right under this one, in `outlet`, oldest first.
182    pub fn children_in(&self, outlet: Outlet) -> Vec<Scope> {
183        self.read(|tree| {
184            let Some(node) = tree.node(self.index, self.serial) else {
185                return Vec::new();
186            };
187            node.children
188                .iter()
189                .filter(|&&child| tree.nodes[child as usize].outlet == outlet)
190                .map(|&child| self.named(tree, child))
191                .collect()
192        })
193        .unwrap_or_default()
194    }
195
196    /// The scopes right under this one in any outlet but [`MAIN`], oldest
197    /// first: what a layout mounts beside its chain.
198    pub fn beside(&self) -> Vec<Scope> {
199        self.read(|tree| {
200            let Some(node) = tree.node(self.index, self.serial) else {
201                return Vec::new();
202            };
203            node.children
204                .iter()
205                .filter(|&&child| tree.nodes[child as usize].outlet != MAIN)
206                .map(|&child| self.named(tree, child))
207                .collect()
208        })
209        .unwrap_or_default()
210    }
211
212    fn read<T>(&self, read: impl FnOnce(&Tree) -> T) -> Option<T> {
213        let tree = tree(self.tree)?;
214        let nodes = tree.borrow();
215        Some(read(&nodes))
216    }
217
218    fn named(&self, tree: &Tree, index: u32) -> Scope {
219        Scope {
220            tree: self.tree,
221            index,
222            serial: tree.nodes[index as usize].serial,
223        }
224    }
225
226    /// Where `R` is read from here: this scope if it claimed `R`, or the
227    /// nearest one above that exports it.
228    ///
229    /// The two ends are asked different questions on purpose. A segment may
230    /// read anything it claimed itself; what a scope above claimed is its own
231    /// business unless it said otherwise in `Exports`.
232    pub fn owner_of<R: 'static>(&self) -> Option<Scope> {
233        if self.claims::<R>() {
234            return Some(*self);
235        }
236
237        std::iter::successors(self.parent(), Scope::parent).find(|scope| scope.exports::<R>())
238    }
239
240    /// This scope and every scope under it, children first.
241    pub fn subtree(&self) -> Vec<Scope> {
242        self.read(|tree| {
243            if tree.node(self.index, self.serial).is_none() {
244                return Vec::new();
245            }
246
247            let mut order = Vec::new();
248            tree.collect(self.index, &mut order);
249            order
250                .into_iter()
251                .map(|index| self.named(tree, index))
252                .collect()
253        })
254        .unwrap_or_default()
255    }
256
257    /// Removes this scope when the guard is dropped.
258    pub fn guard(&self) -> ScopeGuard {
259        ScopeGuard(*self)
260    }
261
262    /// Identifies this scope, and never another one.
263    pub fn key(&self) -> usize {
264        self.serial as usize
265    }
266
267    fn data(&self) -> Option<Rc<ScopeData>> {
268        self.read(|tree| {
269            tree.node(self.index, self.serial)
270                .and_then(|node| node.data.clone())
271        })
272        .flatten()
273    }
274
275    fn installing(&self, doing: &str) -> Rc<ScopeData> {
276        self.data()
277            .unwrap_or_else(|| panic!("{doing} in {self:?}, which was removed"))
278    }
279}
280
281impl ScopeData {
282    fn section_name(&self, section: usize) -> Option<&'static str> {
283        self.section_names.borrow().get(section).copied().flatten()
284    }
285
286    fn current_section(&self) -> usize {
287        self.installing.borrow().last().copied().unwrap_or(0)
288    }
289}