hyperion/
resources.rs

1use std::any::{Any, TypeId};
2use std::collections::HashMap;
3
4/// A generational handle used to identify a resource stored in a `ResourceRegistry`.
5///
6/// # Fields
7/// - `id`: A unique identifier assigned when the resource is registered.
8/// - `version`: A generation number used to validate the handle's freshness.
9///
10/// # Safety
11/// - Handles become invalid if the associated resource is removed (version is incremented).
12#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
13pub struct ResourceHandle {
14    id: u64,
15    version: u32,
16}
17
18/// A type-erased registry for storing arbitrary resources using generational handles.
19///
20/// # Fields
21/// - `next_id`: Next available numeric id for newly registered resources.
22/// - `versions`: Per-id version counter (generation) for handle validation.
23/// - `stores`: Map of `TypeId` -> boxed `Vec<T>` type-erased.
24/// - `ids`: Map of id -> (TypeId, index in its store).
25/// - `reverse_ids`: Reverse map of (TypeId, index) -> id.
26/// - `unique_resources`: Map of `TypeId` -> unique resource instance.
27///
28/// # Safety
29/// - All type-erased accesses are guarded by `TypeId` checks and handle version
30///   validation to prevent type confusion and use-after-free via stale handles.
31pub struct ResourceRegistry {
32    next_id: u64,
33    versions: HashMap<u64, u32>,
34    stores: HashMap<TypeId, Box<dyn Any>>,
35    ids: HashMap<u64, (TypeId, usize)>,
36    reverse_ids: HashMap<(TypeId, usize), u64>,
37    unique_resources: HashMap<TypeId, Box<dyn Any>>,
38}
39
40impl ResourceRegistry {
41    /// Creates a new, empty `ResourceRegistry`.
42    pub fn new() -> Self {
43        Self {
44            next_id: 0,
45            versions: HashMap::new(),
46            stores: HashMap::new(),
47            ids: HashMap::new(),
48            reverse_ids: HashMap::new(),
49            unique_resources: HashMap::new(),
50        }
51    }
52
53    /// Registers or overwrites the unique resource of type `T`.
54    ///
55    /// If a resource of this type already exists, it will be replaced.
56    ///
57    /// # Type Parameters
58    /// - `T`: The type of the resource. Must be `'static`.
59    ///
60    /// # Parameters
61    /// - `resource`: The resource instance to store as a unique singleton.
62    pub fn register_unique<T: 'static>(&mut self, resource: T) {
63        let tid = TypeId::of::<T>();
64        self.unique_resources.insert(tid, Box::new(resource));
65    }
66
67    /// Gets an immutable reference to the unique resource of type `T`, if it exists.
68    ///
69    /// # Type Parameters
70    /// - `T`: The type of the resource requested.
71    ///
72    /// # Returns
73    /// - `Option<&T>`: Some reference to the resource if registered; None if not found.
74    pub fn get_unique<T: 'static>(&self) -> Option<&T> {
75        let tid = TypeId::of::<T>();
76        self.unique_resources.get(&tid)?.downcast_ref::<T>()
77    }
78
79    /// Gets a mutable reference to the unique resource of type `T`, if it exists.
80    ///
81    /// # Type Parameters
82    /// - `T`: The type of the resource requested.
83    ///
84    /// # Returns
85    /// - `Option<&mut T>`: Some mutable reference to the resource if registered; None if not found.
86    pub fn get_unique_mut<T: 'static>(&mut self) -> Option<&mut T> {
87        let tid = TypeId::of::<T>();
88        self.unique_resources.get_mut(&tid)?.downcast_mut::<T>()
89    }
90
91    /// Removes the unique resource of type `T` from storage and returns it.
92    ///
93    /// # Type Parameters
94    /// - `T`: The type of the resource to remove.
95    ///
96    /// # Returns
97    /// - `Option<T>`: The removed resource if it existed; None if no resource of type `T` was found.
98    pub fn remove_unique<T: 'static>(&mut self) -> Option<T> {
99        let tid = TypeId::of::<T>();
100        self.unique_resources
101            .remove(&tid)?
102            .downcast::<T>()
103            .ok()
104            .map(|boxed| *boxed)
105    }
106
107    /// Registers a resource of type `T` and returns a unique `ResourceHandle`.
108    ///
109    /// # Type Parameters
110    /// - `T`: The concrete type of the resource to store. Must be `'static`.
111    ///
112    /// # Parameters
113    /// - `resource`: The resource instance to store in the registry.
114    ///
115    /// # Returns
116    /// - `ResourceHandle`: A generational handle uniquely identifying the stored resource.
117    ///
118    /// # Notes
119    /// - The handle can later be used to retrieve or remove the resource.
120    /// - Handles are versioned to prevent use-after-free access.
121    pub fn register_resource<T: 'static>(&mut self, resource: T) -> ResourceHandle {
122        let tid = TypeId::of::<T>();
123
124        let store = self
125            .stores
126            .entry(tid)
127            .or_insert_with(|| Box::new(Vec::<T>::new()));
128        let vec = store.downcast_mut::<Vec<T>>().unwrap();
129
130        let index = vec.len();
131        vec.push(resource);
132
133        let id = self.next_id;
134        self.next_id += 1;
135
136        let version = 1;
137        self.versions.insert(id, version);
138        self.ids.insert(id, (tid, index));
139        self.reverse_ids.insert((tid, index), id);
140
141        ResourceHandle { id, version }
142    }
143
144    /// Retrieves an immutable reference to a resource using its handle.
145    ///
146    /// # Type Parameters
147    /// - `T`: The expected type of the resource to retrieve.
148    ///
149    /// # Parameters
150    /// - `handle`: A `ResourceHandle` returned from `register_resource`.
151    ///
152    /// # Returns
153    /// - `Some(&T)`: A reference to the resource if the handle is valid and the type matches.
154    /// - `None`: If the handle is invalid, outdated (version mismatch), or of the wrong type.
155    ///
156    /// # Safety
157    /// - Ensures the handle's version matches the current version of the stored resource.
158    pub fn get_resource<T: 'static>(&self, handle: ResourceHandle) -> Option<&T> {
159        let tid = TypeId::of::<T>();
160
161        let (stored_tid, index) = self.ids.get(&handle.id)?;
162        if *stored_tid != tid {
163            return None;
164        }
165
166        let current_version = self.versions.get(&handle.id)?;
167        if *current_version != handle.version {
168            return None;
169        }
170
171        let vec = self.stores.get(&tid)?.downcast_ref::<Vec<T>>()?;
172        vec.get(*index)
173    }
174
175    /// Retrieves a mutable reference to a resource using its handle.
176    ///
177    /// # Type Parameters
178    /// - `T`: The expected type of the resource to retrieve.
179    ///
180    /// # Parameters
181    /// - `handle`: A `ResourceHandle` returned from `register_resource`.
182    ///
183    /// # Returns
184    /// - `Some(&mut T)`: A mutable reference to the resource if the handle is valid and type matches.
185    /// - `None`: If the handle is invalid, outdated, or of the wrong type.
186    ///
187    /// # Safety
188    /// - Only succeeds if the handle is still valid and uniquely identifies a resource of type `T`.
189    pub fn get_resource_mut<T: 'static>(&mut self, handle: ResourceHandle) -> Option<&mut T> {
190        let tid = TypeId::of::<T>();
191
192        let (stored_tid, index) = self.ids.get(&handle.id)?;
193        if *stored_tid != tid {
194            return None;
195        }
196
197        let current_version = self.versions.get(&handle.id)?;
198        if *current_version != handle.version {
199            return None;
200        }
201
202        let vec = self.stores.get_mut(&tid)?.downcast_mut::<Vec<T>>()?;
203        vec.get_mut(*index)
204    }
205
206    /// Removes a resource from the registry, invalidating its handle.
207    ///
208    /// # Type Parameters
209    /// - `T`: The expected type of the resource to remove.
210    ///
211    /// # Parameters
212    /// - `handle`: The `ResourceHandle` identifying the resource to remove.
213    ///
214    /// # Returns
215    /// - `Some(T)`: The removed resource if the handle was valid and matched the expected type.
216    /// - `None`: If the handle was invalid, outdated (version mismatch), or mismatched in type.
217    ///
218    /// # Notes
219    /// - This function also increments the resource's version, invalidating all existing handles.
220    /// - If the removed element is not at the end of its internal storage, the moved resource’s
221    ///   index is updated, and its handle remains valid.
222    pub fn remove_resource<T: 'static>(&mut self, handle: ResourceHandle) -> Option<T> {
223        let tid = TypeId::of::<T>();
224
225        let (stored_tid, index) = self.ids.remove(&handle.id)?;
226        if stored_tid != tid {
227            return None;
228        }
229
230        let version = self.versions.get_mut(&handle.id)?;
231        if *version != handle.version {
232            return None;
233        }
234
235        // Invalidate the handle by bumping its version
236        *version += 1;
237
238        let vec = self.stores.get_mut(&tid)?.downcast_mut::<Vec<T>>()?;
239
240        if index >= vec.len() {
241            return None;
242        }
243
244        // Remove resource from vec by swapping with last, then popping
245        let removed = vec.swap_remove(index);
246
247        // Clean up reverse ID map
248        self.reverse_ids.remove(&(tid, index));
249
250        // If we swapped with the last element, update the index of the moved one
251        if index < vec.len() {
252            let moved_id = self.reverse_ids.remove(&(tid, vec.len()))?;
253            self.ids.insert(moved_id, (tid, index));
254            self.reverse_ids.insert((tid, index), moved_id);
255        }
256
257        Some(removed)
258    }
259}