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