nami_core/
watcher.rs

1//! # Watcher Management
2//!
3//! This module provides the infrastructure for managing reactive value watchers,
4//! including metadata handling and notification systems.
5
6use alloc::{boxed::Box, collections::BTreeMap, rc::Rc, vec::Vec};
7use core::{
8    any::{Any, TypeId, type_name},
9    cell::RefCell,
10    fmt::Debug,
11    num::NonZeroUsize,
12};
13
14/// A type-erased container for metadata that can be associated with computation results.
15///
16/// `Metadata` allows attaching arbitrary typed information to computation results
17/// and passing it through the computation pipeline.
18#[derive(Debug, Default, Clone)]
19pub struct Metadata(Box<MetadataInner>);
20
21/// Internal implementation of the metadata storage system.
22///
23/// Uses a `BTreeMap` with `TypeId` as keys to store type-erased values.
24#[derive(Debug, Default, Clone)]
25struct MetadataInner(BTreeMap<TypeId, Rc<dyn Any>>);
26
27impl MetadataInner {
28    /// Attempts to retrieve a value of type `T` from the metadata store.
29    ///
30    /// Returns `None` if no value of the requested type is present.
31    #[allow(clippy::unwrap_used)]
32    pub fn try_get<T: 'static + Clone>(&self) -> Option<T> {
33        // Once `downcast_ref_unchecked` stablized, we will use it here.
34        self.0
35            .get(&TypeId::of::<T>())
36            .map(|v| v.downcast_ref::<T>().unwrap())
37            .cloned()
38    }
39
40    /// Inserts a value of type `T` into the metadata store.
41    ///
42    /// If a value of the same type already exists, it will be replaced.
43    pub fn insert<T: 'static + Clone>(&mut self, value: T) {
44        // Value are always cheap to clone, for example, `Animation` may only within one machine word.
45        // However, we must erase the type, so we must make a choice between `Rc` and `Box`.
46        // Here we choose `Rc` to allow cheap cloning when retrieving the value.
47        self.0.insert(TypeId::of::<T>(), Rc::new(value));
48    }
49}
50
51/// Type alias for a reference-counted watcher function.
52pub type Watcher<T> = Rc<dyn Fn(Context<T>) + 'static>;
53
54/// Context passed to watchers containing the value and associated metadata.
55#[derive(Debug, Clone)]
56pub struct Context<T> {
57    /// The current value being watched.
58    value: T,
59    /// Associated metadata for this value change.
60    metadata: Metadata,
61}
62
63impl<T> Context<T> {
64    /// Creates a new context with the given value and metadata.
65    pub const fn new(value: T, metadata: Metadata) -> Self {
66        Self { value, metadata }
67    }
68
69    /// Adds additional metadata to this context.
70    #[must_use]
71    pub fn with<V: Clone + 'static>(mut self, value: V) -> Self {
72        self.metadata = self.metadata.with(value);
73        self
74    }
75
76    /// Consumes the context and returns the inner value.
77    pub fn into_value(self) -> T {
78        self.value
79    }
80
81    /// Returns a reference to the inner value.
82    pub const fn value(&self) -> &T {
83        &self.value
84    }
85
86    /// Returns a mutable reference to the inner value.
87    pub const fn value_mut(&mut self) -> &mut T {
88        &mut self.value
89    }
90
91    /// Returns a reference to the metadata.
92    pub const fn metadata(&self) -> &Metadata {
93        &self.metadata
94    }
95
96    /// Returns a mutable reference to the metadata.
97    pub const fn metadata_mut(&mut self) -> &mut Metadata {
98        &mut self.metadata
99    }
100
101    /// Maps the inner value to a new value.
102    pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Context<U> {
103        Context::new(f(self.value), self.metadata)
104    }
105
106    /// Returns a new context with a reference to the inner value.
107    pub fn as_ref(&self) -> Context<&T> {
108        Context::new(&self.value, self.metadata.clone())
109    }
110
111    /// Returns a new context with a mutable reference to the inner value.
112    pub fn as_mut(&mut self) -> Context<&mut T> {
113        Context::new(&mut self.value, self.metadata.clone())
114    }
115
116    /// Returns a new context with a reference to the dereferenced inner value.
117    pub fn as_deref(&self) -> Context<&T::Target>
118    where
119        T: core::ops::Deref,
120    {
121        Context::new(&*self.value, self.metadata.clone())
122    }
123
124    /// Returns a new context with a mutable reference to the dereferenced inner value.
125    pub fn as_deref_mut(&mut self) -> Context<&mut T::Target>
126    where
127        T: core::ops::DerefMut,
128    {
129        Context::new(&mut *self.value, self.metadata.clone())
130    }
131}
132
133impl<T> From<T> for Context<T> {
134    fn from(value: T) -> Self {
135        Self::new(value, Metadata::new())
136    }
137}
138
139/// A guard that ensures proper cleanup of watchers when dropped.
140#[must_use]
141pub trait WatcherGuard: 'static {}
142
143impl WatcherGuard for () {}
144
145impl<T1: WatcherGuard, T2: WatcherGuard> WatcherGuard for (T1, T2) {}
146
147/// A utility struct that runs a cleanup function when dropped.
148#[derive(Debug)]
149pub struct OnDrop<F>(Option<F>)
150where
151    F: FnOnce();
152
153impl<F> OnDrop<F>
154where
155    F: FnOnce() + 'static,
156{
157    /// Creates a new `OnDrop` that will call the function when dropped.
158    pub const fn new(f: F) -> Self {
159        Self(Some(f))
160    }
161
162    /// Attaches a cleanup function to a guard.
163    #[allow(clippy::needless_pass_by_value)]
164    pub fn attach(guard: impl WatcherGuard, f: F) -> impl WatcherGuard {
165        OnDrop::new(move || {
166            let _ = guard;
167            f();
168        })
169    }
170}
171
172#[allow(clippy::unwrap_used)]
173impl<F> Drop for OnDrop<F>
174where
175    F: FnOnce(),
176{
177    fn drop(&mut self) {
178        (self.0.take().unwrap())();
179    }
180}
181
182/// Type alias for a boxed watcher guard.
183pub type BoxWatcherGuard = Box<dyn WatcherGuard>;
184
185impl<T: WatcherGuard + ?Sized> WatcherGuard for Box<T> {}
186impl<T: WatcherGuard + ?Sized> WatcherGuard for Rc<T> {}
187
188impl<F: FnOnce() + 'static> WatcherGuard for OnDrop<F> {}
189
190impl Metadata {
191    /// Creates a new, empty metadata container.
192    #[must_use]
193    pub fn new() -> Self {
194        Self::default()
195    }
196
197    /// Gets a value of type `T` from the metadata.
198    ///
199    /// # Panics
200    ///
201    /// Panics if no value of type `T` is present in the metadata.
202    #[must_use]
203    #[allow(clippy::expect_used)]
204    pub fn get<T: 'static + Clone>(&self) -> T {
205        self.try_get()
206            .expect("Value of requested type should be present in metadata")
207    }
208
209    /// Attempts to get a value of type `T` from the metadata.
210    ///
211    /// Returns `None` if no value of the requested type is present.
212    #[must_use]
213    pub fn try_get<T: 'static + Clone>(&self) -> Option<T> {
214        self.0.try_get()
215    }
216
217    /// Adds a value to the metadata and returns the updated metadata.
218    ///
219    /// This method is chainable for fluent API usage.
220    #[must_use]
221    pub fn with<T: 'static + Clone>(mut self, value: T) -> Self {
222        self.0.insert(value);
223        self
224    }
225
226    /// Checks if the metadata container is empty.
227    #[must_use]
228    pub fn is_empty(&self) -> bool {
229        self.0.0.is_empty()
230    }
231}
232
233/// A unique identifier for registered watchers.
234pub(crate) type WatcherId = NonZeroUsize;
235
236/// Manages a collection of watchers for a specific computation type.
237///
238/// Provides functionality to register, notify, and cancel watchers.
239#[derive(Debug)]
240pub struct WatcherManager<T> {
241    inner: Rc<RefCell<WatcherManagerInner<T>>>,
242}
243
244impl<T> Clone for WatcherManager<T> {
245    fn clone(&self) -> Self {
246        Self {
247            inner: self.inner.clone(),
248        }
249    }
250}
251
252impl<T> Default for WatcherManager<T> {
253    fn default() -> Self {
254        Self {
255            inner: Rc::default(),
256        }
257    }
258}
259
260impl<T: 'static> WatcherManager<T> {
261    /// Creates a new, empty watcher manager.
262    #[must_use]
263    pub fn new() -> Self {
264        Self::default()
265    }
266
267    /// Checks if the manager has any registered watchers.
268    #[must_use]
269    pub fn is_empty(&self) -> bool {
270        self.inner.borrow().is_empty()
271    }
272
273    /// Registers a new watcher and returns its unique identifier.
274    pub fn register(&self, watcher: impl Fn(Context<T>) + 'static) -> WatcherId {
275        self.inner.borrow_mut().register(watcher)
276    }
277
278    /// Registers a watcher and returns a guard that will unregister it when dropped.
279    pub fn register_as_guard(
280        &self,
281        watcher: impl Fn(Context<T>) + 'static,
282    ) -> WatcherManagerGuard<T> {
283        let id = self.register(watcher);
284        let this = self.clone();
285        WatcherManagerGuard { manager: this, id }
286    }
287
288    /// Notifies all registered watchers with a preconstructed context.
289    pub fn notify(&self, ctx: &Context<T>)
290    where
291        T: Clone,
292    {
293        let watchers = {
294            let inner = self.inner.borrow();
295            inner.watchers_snapshot()
296        };
297
298        if watchers.is_empty() {
299            return;
300        }
301
302        for watcher in watchers {
303            watcher(ctx.clone());
304        }
305    }
306
307    /// Cancels a previously registered watcher by its identifier.
308    pub fn cancel(&self, id: WatcherId) {
309        self.inner.borrow_mut().cancel(id);
310    }
311}
312
313/// A guard that ensures a watcher is unregistered when dropped.
314#[must_use]
315#[derive(Debug)]
316pub struct WatcherManagerGuard<T: 'static> {
317    manager: WatcherManager<T>,
318    id: WatcherId,
319}
320
321impl<T> WatcherGuard for WatcherManagerGuard<T> {}
322
323impl<T: 'static> Drop for WatcherManagerGuard<T> {
324    fn drop(&mut self) {
325        self.manager.cancel(self.id);
326    }
327}
328
329/// Internal implementation of the watcher manager.
330///
331/// Maintains the collection of watchers and handles identifier assignment.
332struct WatcherManagerInner<T> {
333    id: WatcherId,
334    map: BTreeMap<WatcherId, Watcher<T>>,
335}
336
337impl<T> Debug for WatcherManagerInner<T> {
338    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
339        f.write_str(type_name::<Self>())
340    }
341}
342
343impl<T> Default for WatcherManagerInner<T> {
344    fn default() -> Self {
345        Self {
346            id: WatcherId::MIN,
347            map: BTreeMap::new(),
348        }
349    }
350}
351
352impl<T: 'static> WatcherManagerInner<T> {
353    /// Checks if there are any registered watchers.
354    pub fn is_empty(&self) -> bool {
355        self.map.is_empty()
356    }
357
358    /// Assigns a new unique identifier for a watcher.
359    const fn assign(&mut self) -> WatcherId {
360        let id = self.id;
361        self.id = match self.id.checked_add(1) {
362            Some(id) => id,
363            None => panic!("`id` grows beyond `usize::MAX`"),
364        };
365        id
366    }
367
368    /// Registers a watcher and returns its unique identifier.
369    pub fn register(&mut self, watcher: impl Fn(Context<T>) + 'static) -> WatcherId {
370        let id = self.assign();
371        self.map.insert(id, Rc::new(watcher));
372        id
373    }
374
375    /// Creates a snapshot of the current watchers for notification.
376    fn watchers_snapshot(&self) -> Vec<Watcher<T>> {
377        self.map.values().cloned().collect()
378    }
379
380    /// Cancels a watcher registration by its identifier.
381    pub fn cancel(&mut self, id: WatcherId) {
382        self.map.remove(&id);
383    }
384}