Skip to main content

aimdb_core/
extensions.rs

1//! Generic extension storage for [`AimDbBuilder`](crate::AimDbBuilder) and [`AimDb`](crate::AimDb).
2//!
3//! External crates store typed state here during builder configuration
4//! and retrieve it during record setup or at query time. This is the
5//! hook mechanism used by `aimdb-persistence` — and any other crate that
6//! needs to attach data to the builder or the live database without
7//! modifying `aimdb-core`.
8//!
9//! # Example
10//! ```no_run
11//! # use aimdb_core::{AimDb, AimDbBuilder, RecordRegistrar};
12//! # struct MyState { flag: bool }
13//! # fn at_configure_time(builder: &mut AimDbBuilder) {
14//! // Storing a value (e.g. from an external "with_persistence" builder ext):
15//! builder.extensions_mut().insert(MyState { flag: true });
16//! # }
17//! # fn in_registrar(reg: &mut RecordRegistrar<'_, u32>) {
18//! // Retrieving it from a RecordRegistrar closure:
19//! let state = reg.extensions().get::<MyState>().expect("MyState not configured");
20//! # }
21//! # fn at_query_time(db: &AimDb) {
22//! // Retrieving it from a live AimDb handle (query time):
23//! let state = db.extensions().get::<MyState>().expect("MyState not configured");
24//! # }
25//! ```
26
27use alloc::boxed::Box;
28use core::any::{Any, TypeId};
29use hashbrown::HashMap;
30
31/// Generic extension storage for [`AimDbBuilder`][crate::AimDbBuilder] and
32/// [`AimDb`][crate::AimDb].
33///
34/// Keyed by [`TypeId`] so each stored type occupies exactly one slot.
35/// Values must be `Send + Sync + 'static` to be safe across thread and task
36/// boundaries used throughout AimDB's async executor model.
37pub struct Extensions {
38    map: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
39}
40
41impl Default for Extensions {
42    fn default() -> Self {
43        Self::new()
44    }
45}
46
47impl Extensions {
48    /// Creates an empty extension map.
49    pub fn new() -> Self {
50        Self {
51            map: HashMap::new(),
52        }
53    }
54
55    /// Inserts a value. Replaces any previously stored value of the same type.
56    pub fn insert<T: Send + Sync + 'static>(&mut self, val: T) {
57        self.map.insert(TypeId::of::<T>(), Box::new(val));
58    }
59
60    /// Returns a reference to the value of type `T`, or `None` if not stored.
61    pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
62        self.map
63            .get(&TypeId::of::<T>())
64            .and_then(|b| b.downcast_ref())
65    }
66}