Skip to main content

guinea_core/scope/
actors.rs

1use crate::actor::Addr;
2use crate::actor::registry::{ActorSnapshot, Owner};
3use crate::actor::traits::ManagedActor;
4use crate::observability::changes::{self, Change};
5
6use super::{Scope, Teardown};
7
8pub(super) struct HeldActor {
9    id: usize,
10    type_name: &'static str,
11    shape: crate::actor::shape::Shape,
12    owner: Owner,
13    snapshot: Box<dyn Fn() -> String>,
14}
15
16impl HeldActor {
17    fn read(&self) -> ActorSnapshot {
18        ActorSnapshot {
19            id: self.id,
20            type_name: self.type_name,
21            shape: self.shape,
22            owner: self.owner,
23            state: (self.snapshot)(),
24        }
25    }
26}
27
28fn read_all(scopes: Vec<Scope>) -> Vec<ActorSnapshot> {
29    scopes
30        .into_iter()
31        .filter_map(|scope| scope.data())
32        .flat_map(|data| data.actors.borrow().iter().map(HeldActor::read).collect::<Vec<_>>())
33        .collect()
34}
35
36fn read_one(scopes: Vec<Scope>, id: usize) -> Option<ActorSnapshot> {
37    scopes.into_iter().filter_map(|scope| scope.data()).find_map(|data| {
38        data.actors
39            .borrow()
40            .iter()
41            .find(|actor| actor.id == id)
42            .map(HeldActor::read)
43    })
44}
45
46/// Tells devtools an actor is no longer listed, when its scope goes.
47struct Unlisted {
48    root: Option<u64>,
49    id: usize,
50}
51
52impl Teardown for Unlisted {
53    fn teardown(self) {
54        changes::changed(|| Change::ActorRemoved {
55            root: self.root,
56            id: self.id,
57        });
58    }
59}
60
61impl Scope {
62    /// Says this scope is the root of window `id`, as `RootId::get` numbers
63    /// it: what devtools are told the actors under it belong to.
64    pub fn set_window(&self, id: u64) {
65        if let Some(data) = self.data() {
66            data.window.set(Some(id));
67        }
68    }
69
70    /// The window this scope is under, if it is under one.
71    pub fn window(&self) -> Option<u64> {
72        std::iter::successors(Some(*self), Scope::parent)
73            .find_map(|scope| scope.data()?.window.get())
74    }
75
76    /// Lists `addr` among the actors this scope holds, for devtools, until
77    /// the scope is removed - `feature` created it, and it drives `drives`
78    /// when it was made to.
79    ///
80    /// Only the listing: what ends the actor is still whoever owns it, which
81    /// for an actor of a segment is [`own`](Self::own) on this same scope.
82    pub fn hold_actor<A: ManagedActor + std::fmt::Debug>(
83        &self,
84        addr: &Addr<A>,
85        feature: Option<&'static str>,
86        drives: Option<&'static str>,
87    ) {
88        let Some(data) = self.data() else { return };
89        let id = addr.id();
90        let type_name = crate::actor::short_type_name::<A>();
91        let owner = Owner {
92            scope: Some(self.key()),
93            feature,
94            drives,
95        };
96
97        let held = addr.clone();
98        data.actors.borrow_mut().push(HeldActor {
99            id,
100            type_name,
101            shape: A::SHAPE,
102            owner,
103            snapshot: Box::new(move || held.debug_snapshot()),
104        });
105        drop(data);
106
107        let root = self.window();
108        changes::changed(|| Change::ActorAdded {
109            root,
110            id,
111            type_name,
112            owner,
113        });
114        self.own(Unlisted { root, id });
115    }
116
117    /// The actors this scope and every scope under it hold, read now.
118    pub fn actors(&self) -> Vec<ActorSnapshot> {
119        read_all(self.subtree())
120    }
121
122    /// The actor `id`, if this scope or one under it holds it, read now; the
123    /// others are not read.
124    pub fn actor(&self, id: usize) -> Option<ActorSnapshot> {
125        read_one(self.subtree(), id)
126    }
127
128    /// The actors this scope holds itself, read now - not those of the
129    /// scopes under it.
130    pub fn actors_here(&self) -> Vec<ActorSnapshot> {
131        read_all(vec![*self])
132    }
133
134    /// The actor `id`, if this scope holds it itself, read now.
135    pub fn actor_here(&self, id: usize) -> Option<ActorSnapshot> {
136        read_one(vec![*self], id)
137    }
138}