Skip to main content

guinea_core/observability/
changes.rs

1//! What a tool lists came or went: actors, timers, subscriptions, routers.
2
3use std::cell::RefCell;
4use std::rc::Rc;
5
6use crate::actor::registry::Owner;
7use crate::trace;
8
9/// Something a tool lists came or went, so what it last read of it is
10/// stale. Says what to read again, not what it now reads.
11///
12/// `root` is the window a registry or a bus belongs to, as `RootId::get`
13/// numbers it; `None` for the application's own, or for one no window owns.
14#[derive(Clone, Debug, PartialEq)]
15#[non_exhaustive]
16pub enum Change {
17    ActorAdded {
18        root: Option<u64>,
19        id: usize,
20        type_name: &'static str,
21        owner: Owner,
22    },
23    ActorRemoved {
24        root: Option<u64>,
25        id: usize,
26    },
27    /// An actor handled a message, so its state may read differently now.
28    /// Where it lives is what [`Change::ActorAdded`] said.
29    ActorHandled { id: usize },
30    /// A timer a tool may see started; its id is the one the running timers
31    /// list it under.
32    TimerStarted { id: u64 },
33    /// A timer stopped, or stopped being one a tool may see.
34    TimerStopped { id: u64 },
35    /// Something subscribed to a bus, or stopped being.
36    Subscriptions { bus: trace::Bus, root: Option<u64> },
37    /// A window's router navigated for the first time.
38    RouterOpened { root: u64 },
39    RouterClosed { root: u64 },
40}
41
42type Watcher = Rc<dyn Fn(&Change)>;
43
44thread_local! {
45    static WATCHER: RefCell<Option<Watcher>> = const { RefCell::new(None) };
46}
47
48/// Hands every [`Change`] on this thread to `watcher`, replacing any watcher
49/// already set.
50///
51/// It is told synchronously, from wherever the change happened - a
52/// registration, a teardown - so it should note what to read again and read
53/// it later, not read it there.
54pub fn watch(watcher: impl Fn(&Change) + 'static) {
55    WATCHER.with(|slot| slot.borrow_mut().replace(Rc::new(watcher)));
56}
57
58pub fn stop_watching() {
59    WATCHER.with(|slot| slot.borrow_mut().take());
60}
61
62/// Tells the watcher, if there is one; `change` is not built otherwise.
63pub fn changed(change: impl FnOnce() -> Change) {
64    let Some(watcher) = WATCHER.try_with(|slot| slot.borrow().clone()).ok().flatten() else {
65        return;
66    };
67
68    watcher(&change());
69}