1use slotmap::{Key as _, SecondaryMap, SlotMap, new_key_type};
2use std::collections::HashMap;
3
4new_key_type! {
5 pub struct RawAssetHandle;
11}
12
13pub struct Assets<T: 'static + Send + Sync> {
20 storage: SlotMap<RawAssetHandle, T>,
21 handles: HashMap<String, RawAssetHandle>,
22 queue: Vec<RawAssetHandle>,
23 removed: Vec<RawAssetHandle>,
24}
25
26impl<T: 'static + Send + Sync> Assets<T> {
27 pub fn new() -> Self {
28 Self {
29 storage: SlotMap::with_key(),
30 handles: HashMap::new(),
31 queue: Vec::new(),
32 removed: Vec::new(),
33 }
34 }
35
36 pub fn insert(&mut self, name: &str, asset: T) -> RawAssetHandle {
44 if let Some(&existing) = self.handles.get(name) {
45 if let Some(slot) = self.storage.get_mut(existing) {
47 *slot = asset;
48 if !self.queue.contains(&existing) {
50 self.queue.push(existing);
51 }
52 tracing::debug!(
53 "Assets<{}>: replaced data for {:?} ({name}) in-place",
54 std::any::type_name::<T>(),
55 existing
56 );
57 return existing;
58 }
59 }
60
61 let handle = self.storage.insert(asset);
62 self.handles.insert(name.to_string(), handle);
63 self.queue.push(handle);
64 handle
65 }
66
67 pub fn get(&self, handle: RawAssetHandle) -> Option<&T> {
69 if handle.is_null() {
70 tracing::warn!(
71 "Assets<{}>: get() called with a null/default handle — \
72 did you forget to insert the asset and store the returned handle?",
73 std::any::type_name::<T>()
74 );
75 return None;
76 }
77 let result = self.storage.get(handle);
78 if result.is_none() {
79 tracing::warn!(
80 "Assets<{}>: get() called with a stale handle {:?} — \
81 the asset was likely removed or replaced since this handle was obtained",
82 std::any::type_name::<T>(),
83 handle
84 );
85 }
86 result
87 }
88
89 pub fn get_mut(&mut self, handle: RawAssetHandle) -> Option<&mut T> {
91 if handle.is_null() {
92 tracing::warn!(
93 "Assets<{}>: get_mut() called with a null/default handle — \
94 did you forget to insert the asset and store the returned handle?",
95 std::any::type_name::<T>()
96 );
97 return None;
98 }
99 let result = self.storage.get_mut(handle);
100 if result.is_none() {
101 tracing::warn!(
102 "Assets<{}>: get_mut() called with a stale handle {:?} — \
103 the asset was likely removed or replaced since this handle was obtained",
104 std::any::type_name::<T>(),
105 handle
106 );
107 }
108 result
109 }
110
111 pub fn get_by_name(&self, name: &str) -> Option<&T> {
113 let result = self.handles
114 .get(name)
115 .and_then(|&handle| self.storage.get(handle));
116 if result.is_none() {
117 tracing::debug!(
118 "Assets<{}>: get_by_name({:?}) found no asset — \
119 the name may not have been inserted yet",
120 std::any::type_name::<T>(),
121 name
122 );
123 }
124 result
125 }
126
127 pub fn get_mut_by_name(&mut self, name: &str) -> Option<&mut T> {
129 let handle = self.handles.get(name).copied();
130 if handle.is_none() {
131 tracing::debug!(
132 "Assets<{}>: get_mut_by_name({:?}) found no asset — \
133 the name may not have been inserted yet",
134 std::any::type_name::<T>(),
135 name
136 );
137 return None;
138 }
139 self.storage.get_mut(handle.unwrap())
140 }
141
142 pub fn get_handle_by_name(&self, name: &str) -> Option<RawAssetHandle> {
144 self.handles.get(name).copied()
145 }
146
147 pub fn take_dirty(&mut self) -> Vec<RawAssetHandle> {
151 std::mem::take(&mut self.queue)
152 }
153
154 pub fn remove(&mut self, handle: RawAssetHandle) -> Option<T> {
156 if handle.is_null() {
157 tracing::warn!(
158 "Assets<{}>: remove() called with a null/default handle — no-op",
159 std::any::type_name::<T>()
160 );
161 return None;
162 }
163 let value = self.storage.remove(handle)?;
164
165 self.handles.retain(|_, h| *h != handle);
166 self.queue.retain(|h| *h != handle);
167 self.removed.push(handle);
168
169 Some(value)
170 }
171
172 pub fn remove_by_name(&mut self, name: &str) -> Option<T> {
174 let handle = self.handles.remove(name)?;
175 self.queue.retain(|h| *h != handle);
176 self.removed.push(handle);
177 self.storage.remove(handle)
178 }
179
180 pub fn take_removed(&mut self) -> Vec<RawAssetHandle> {
184 std::mem::take(&mut self.removed)
185 }
186
187 pub fn dirty_is_empty(&self) -> bool {
189 self.queue.is_empty()
190 }
191
192 pub fn dirty_len(&self) -> usize {
194 self.queue.len()
195 }
196
197 pub fn requeue(&mut self, handles: Vec<RawAssetHandle>) {
199 self.queue.extend(handles);
200 }
201
202 pub fn replace(&mut self, handle: RawAssetHandle, asset: T) -> bool {
209 if handle.is_null() {
210 tracing::warn!(
211 "Assets<{}>: replace() called with a null/default handle — no-op",
212 std::any::type_name::<T>()
213 );
214 return false;
215 }
216 let Some(slot) = self.storage.get_mut(handle) else {
217 tracing::warn!(
218 "Assets<{}>: replace() called with a stale handle {:?} — no-op",
219 std::any::type_name::<T>(),
220 handle
221 );
222 return false;
223 };
224 *slot = asset;
225 if !self.queue.contains(&handle) {
226 self.queue.push(handle);
227 }
228 tracing::debug!(
229 "Assets<{}>: replaced data for {:?}{} via handle",
230 std::any::type_name::<T>(),
231 handle,
232 self.name_for_handle(handle)
233 .map(|n| format!(" ({n})"))
234 .unwrap_or_default()
235 );
236 true
237 }
238
239 pub fn mark_dirty(&mut self, handle: RawAssetHandle) {
248 if handle.is_null() {
249 tracing::warn!(
250 "Assets<{}>: mark_dirty() called with a null/default handle — no-op",
251 std::any::type_name::<T>()
252 );
253 return;
254 }
255 if self.storage.contains_key(handle) && !self.queue.contains(&handle) {
256 self.queue.push(handle);
257 }
258 }
259
260 pub fn iter(&self) -> impl Iterator<Item = (RawAssetHandle, &T)> {
262 self.storage.iter()
263 }
264
265 pub fn iter_mut(&mut self) -> impl Iterator<Item = (RawAssetHandle, &mut T)> {
267 self.storage.iter_mut()
268 }
269
270 pub fn names(&self) -> impl Iterator<Item = (&str, RawAssetHandle)> {
272 self.handles
273 .iter()
274 .map(|(name, &handle)| (name.as_str(), handle))
275 }
276
277 pub fn name_for_handle(&self, handle: RawAssetHandle) -> Option<&str> {
278 self.handles
279 .iter()
280 .find(|(_, h)| **h == handle)
281 .map(|(name, _)| name.as_str())
282 }
283}
284
285impl<'a, T: 'static + Send + Sync> IntoIterator for &'a Assets<T> {
286 type Item = (RawAssetHandle, &'a T);
287 type IntoIter = slotmap::basic::Iter<'a, RawAssetHandle, T>;
288
289 fn into_iter(self) -> Self::IntoIter {
290 self.storage.iter()
291 }
292}
293
294impl<'a, T: 'static + Send + Sync> IntoIterator for &'a mut Assets<T> {
295 type Item = (RawAssetHandle, &'a mut T);
296 type IntoIter = slotmap::basic::IterMut<'a, RawAssetHandle, T>;
297
298 fn into_iter(self) -> Self::IntoIter {
299 self.storage.iter_mut()
300 }
301}
302
303pub struct ProcessedAssets<T: 'static + Send + Sync> {
308 storage: SecondaryMap<RawAssetHandle, T>,
309 pub(crate) names: HashMap<String, RawAssetHandle>,
310}
311
312impl<T: 'static + Send + Sync> ProcessedAssets<T> {
313 pub fn new() -> Self {
314 Self {
315 storage: SecondaryMap::new(),
316 names: HashMap::new(),
317 }
318 }
319
320 pub fn insert(&mut self, handle: RawAssetHandle, asset: T) -> Option<T> {
322 self.storage.insert(handle, asset)
323 }
324
325 pub fn get(&self, handle: RawAssetHandle) -> Option<&T> {
327 if handle.is_null() {
328 tracing::warn!(
329 "ProcessedAssets<{}>: get() called with a null/default handle — \
330 did you forget to insert the source asset and store the returned handle?",
331 std::any::type_name::<T>()
332 );
333 return None;
334 }
335 let result = self.storage.get(handle);
336 if result.is_none() {
337 tracing::debug!(
338 "ProcessedAssets<{}>: get() for handle {:?} returned nothing — \
339 the asset may still be pending upload or was removed",
340 std::any::type_name::<T>(),
341 handle
342 );
343 }
344 result
345 }
346
347 pub fn get_mut(&mut self, handle: RawAssetHandle) -> Option<&mut T> {
349 if handle.is_null() {
350 tracing::warn!(
351 "ProcessedAssets<{}>: get_mut() called with a null/default handle — \
352 did you forget to insert the source asset and store the returned handle?",
353 std::any::type_name::<T>()
354 );
355 return None;
356 }
357 let result = self.storage.get_mut(handle);
358 if result.is_none() {
359 tracing::debug!(
360 "ProcessedAssets<{}>: get_mut() for handle {:?} returned nothing — \
361 the asset may still be pending upload or was removed",
362 std::any::type_name::<T>(),
363 handle
364 );
365 }
366 result
367 }
368
369 pub fn remove(&mut self, handle: RawAssetHandle) -> Option<T> {
371 self.names.retain(|_, h| *h != handle);
372 self.storage.remove(handle)
373 }
374
375 pub fn contains(&self, handle: RawAssetHandle) -> bool {
377 self.storage.contains_key(handle)
378 }
379
380 pub fn iter(&self) -> impl Iterator<Item = (RawAssetHandle, &T)> {
382 self.storage.iter()
383 }
384
385 pub fn iter_mut(&mut self) -> impl Iterator<Item = (RawAssetHandle, &mut T)> {
387 self.storage.iter_mut()
388 }
389
390 pub fn get_by_name(&self, name: &str) -> Option<&T> {
391 let handle = self.names.get(name)?;
392 self.storage.get(*handle)
393 }
394}
395
396impl<'a, T: 'static + Send + Sync> IntoIterator for &'a ProcessedAssets<T> {
397 type Item = (RawAssetHandle, &'a T);
398 type IntoIter = slotmap::secondary::Iter<'a, RawAssetHandle, T>;
399
400 fn into_iter(self) -> Self::IntoIter {
401 self.storage.iter()
402 }
403}
404
405impl<'a, T: 'static + Send + Sync> IntoIterator for &'a mut ProcessedAssets<T> {
406 type Item = (RawAssetHandle, &'a mut T);
407 type IntoIter = slotmap::secondary::IterMut<'a, RawAssetHandle, T>;
408
409 fn into_iter(self) -> Self::IntoIter {
410 self.storage.iter_mut()
411 }
412}