1use slotmap::{Key as _, SecondaryMap, SlotMap, new_key_type};
2use std::collections::HashMap;
3
4use crate::assets::handle::Handle;
5
6new_key_type! {
7 pub struct RawAssetHandle;
13}
14
15fn warn_if_null<T>(handle: RawAssetHandle, container: &str, method: &str, on_null: &str) -> bool {
23 if handle.is_null() {
24 tracing::warn!(
25 "{container}<{}>: {method}() called with a null/default handle — {on_null}",
26 std::any::type_name::<T>()
27 );
28 true
29 } else {
30 false
31 }
32}
33
34pub struct Assets<T: 'static + Send + Sync> {
41 storage: SlotMap<RawAssetHandle, T>,
42 handles: HashMap<String, RawAssetHandle>,
43 queue: Vec<RawAssetHandle>,
44 removed: Vec<RawAssetHandle>,
45}
46
47impl<T: 'static + Send + Sync> Assets<T> {
48 pub fn new() -> Self {
49 Self {
50 storage: SlotMap::with_key(),
51 handles: HashMap::new(),
52 queue: Vec::new(),
53 removed: Vec::new(),
54 }
55 }
56
57 pub fn insert(&mut self, name: &str, asset: T) -> Handle<T> {
65 if let Some(&existing) = self.handles.get(name) {
66 if let Some(slot) = self.storage.get_mut(existing) {
68 *slot = asset;
69 if !self.queue.contains(&existing) {
71 self.queue.push(existing);
72 }
73 tracing::debug!(
74 "Assets<{}>: replaced data for {:?} ({name}) in-place",
75 std::any::type_name::<T>(),
76 existing
77 );
78 return Handle::new(existing);
79 }
80 }
81
82 let handle = self.storage.insert(asset);
83 self.handles.insert(name.to_string(), handle);
84 self.queue.push(handle);
85 Handle::new(handle)
86 }
87
88 pub(crate) fn get_quiet(&self, handle: RawAssetHandle) -> Option<&T> {
95 self.storage.get(handle)
96 }
97
98 pub fn get(&self, handle: Handle<T>) -> Option<&T> {
100 let handle = handle.id;
101 if warn_if_null::<T>(handle, "Assets", "get", "did you forget to insert the asset and store the returned handle?") {
102 return None;
103 }
104 let result = self.storage.get(handle);
105 if result.is_none() {
106 tracing::warn!(
107 "Assets<{}>: get() called with a stale handle {:?} — \
108 the asset was likely removed or replaced since this handle was obtained",
109 std::any::type_name::<T>(),
110 handle
111 );
112 }
113 result
114 }
115
116 pub fn get_mut(&mut self, handle: Handle<T>) -> Option<&mut T> {
118 let handle = handle.id;
119 if warn_if_null::<T>(handle, "Assets", "get_mut", "did you forget to insert the asset and store the returned handle?") {
120 return None;
121 }
122 let result = self.storage.get_mut(handle);
123 if result.is_none() {
124 tracing::warn!(
125 "Assets<{}>: get_mut() called with a stale handle {:?} — \
126 the asset was likely removed or replaced since this handle was obtained",
127 std::any::type_name::<T>(),
128 handle
129 );
130 }
131 result
132 }
133
134 pub fn contains(&self, handle: Handle<T>) -> bool {
137 self.storage.contains_key(handle.id)
138 }
139
140 pub fn get_by_name(&self, name: &str) -> Option<&T> {
142 let result = self.handles
143 .get(name)
144 .and_then(|&handle| self.storage.get(handle));
145 if result.is_none() {
146 tracing::debug!(
147 "Assets<{}>: get_by_name({:?}) found no asset — \
148 the name may not have been inserted yet",
149 std::any::type_name::<T>(),
150 name
151 );
152 }
153 result
154 }
155
156 pub fn get_mut_by_name(&mut self, name: &str) -> Option<&mut T> {
158 let handle = self.handles.get(name).copied();
159 if handle.is_none() {
160 tracing::debug!(
161 "Assets<{}>: get_mut_by_name({:?}) found no asset — \
162 the name may not have been inserted yet",
163 std::any::type_name::<T>(),
164 name
165 );
166 return None;
167 }
168 self.storage.get_mut(handle.unwrap())
169 }
170
171 pub fn get_handle_by_name(&self, name: &str) -> Option<Handle<T>> {
173 self.handles.get(name).copied().map(Handle::new)
174 }
175
176 pub fn take_dirty(&mut self) -> Vec<RawAssetHandle> {
180 std::mem::take(&mut self.queue)
181 }
182
183 pub fn remove(&mut self, handle: Handle<T>) -> Option<T> {
185 let handle = handle.id;
186 if warn_if_null::<T>(handle, "Assets", "remove", "no-op") {
187 return None;
188 }
189 let value = self.storage.remove(handle)?;
190
191 self.handles.retain(|_, h| *h != handle);
192 self.queue.retain(|h| *h != handle);
193 self.removed.push(handle);
194
195 Some(value)
196 }
197
198 pub fn remove_by_name(&mut self, name: &str) -> Option<T> {
200 let handle = self.handles.remove(name)?;
201 self.queue.retain(|h| *h != handle);
202 self.removed.push(handle);
203 self.storage.remove(handle)
204 }
205
206 pub fn take_removed(&mut self) -> Vec<RawAssetHandle> {
210 std::mem::take(&mut self.removed)
211 }
212
213 pub fn dirty_is_empty(&self) -> bool {
215 self.queue.is_empty()
216 }
217
218 pub fn dirty_len(&self) -> usize {
220 self.queue.len()
221 }
222
223 pub fn requeue(&mut self, handles: Vec<RawAssetHandle>) {
225 self.queue.extend(handles);
226 }
227
228 pub fn replace(&mut self, handle: Handle<T>, asset: T) -> bool {
235 let handle = handle.id;
236 if warn_if_null::<T>(handle, "Assets", "replace", "no-op") {
237 return false;
238 }
239 let Some(slot) = self.storage.get_mut(handle) else {
240 tracing::warn!(
241 "Assets<{}>: replace() called with a stale handle {:?} — no-op",
242 std::any::type_name::<T>(),
243 handle
244 );
245 return false;
246 };
247 *slot = asset;
248 if !self.queue.contains(&handle) {
249 self.queue.push(handle);
250 }
251 tracing::debug!(
252 "Assets<{}>: replaced data for {:?}{} via handle",
253 std::any::type_name::<T>(),
254 handle,
255 self.name_for_handle(handle)
256 .map(|n| format!(" ({n})"))
257 .unwrap_or_default()
258 );
259 true
260 }
261
262 pub fn mark_dirty(&mut self, handle: Handle<T>) {
271 let handle = handle.id;
272 if warn_if_null::<T>(handle, "Assets", "mark_dirty", "no-op") {
273 return;
274 }
275 if self.storage.contains_key(handle) && !self.queue.contains(&handle) {
276 self.queue.push(handle);
277 }
278 }
279
280 pub fn iter(&self) -> impl Iterator<Item = (RawAssetHandle, &T)> {
282 self.storage.iter()
283 }
284
285 pub fn iter_mut(&mut self) -> impl Iterator<Item = (RawAssetHandle, &mut T)> {
287 self.storage.iter_mut()
288 }
289
290 pub fn names(&self) -> impl Iterator<Item = (&str, RawAssetHandle)> {
292 self.handles
293 .iter()
294 .map(|(name, &handle)| (name.as_str(), handle))
295 }
296
297 pub fn name_for_handle(&self, handle: RawAssetHandle) -> Option<&str> {
301 self.handles
302 .iter()
303 .find(|(_, h)| **h == handle)
304 .map(|(name, _)| name.as_str())
305 }
306}
307
308impl<'a, T: 'static + Send + Sync> IntoIterator for &'a Assets<T> {
309 type Item = (RawAssetHandle, &'a T);
310 type IntoIter = slotmap::basic::Iter<'a, RawAssetHandle, T>;
311
312 fn into_iter(self) -> Self::IntoIter {
313 self.storage.iter()
314 }
315}
316
317impl<'a, T: 'static + Send + Sync> IntoIterator for &'a mut Assets<T> {
318 type Item = (RawAssetHandle, &'a mut T);
319 type IntoIter = slotmap::basic::IterMut<'a, RawAssetHandle, T>;
320
321 fn into_iter(self) -> Self::IntoIter {
322 self.storage.iter_mut()
323 }
324}
325
326pub struct ProcessedAssets<T: 'static + Send + Sync> {
331 storage: SecondaryMap<RawAssetHandle, T>,
332 pub(crate) names: HashMap<String, RawAssetHandle>,
333}
334
335impl<T: 'static + Send + Sync> ProcessedAssets<T> {
336 pub fn new() -> Self {
337 Self {
338 storage: SecondaryMap::new(),
339 names: HashMap::new(),
340 }
341 }
342
343 pub fn insert(&mut self, handle: RawAssetHandle, asset: T) -> Option<T> {
345 self.storage.insert(handle, asset)
346 }
347
348 pub fn get(&self, handle: RawAssetHandle) -> Option<&T> {
350 if warn_if_null::<T>(handle, "ProcessedAssets", "get", "did you forget to insert the source asset and store the returned handle?") {
351 return None;
352 }
353 let result = self.storage.get(handle);
354 if result.is_none() {
355 tracing::debug!(
356 "ProcessedAssets<{}>: get() for handle {:?} returned nothing — \
357 the asset may still be pending upload or was removed",
358 std::any::type_name::<T>(),
359 handle
360 );
361 }
362 result
363 }
364
365 pub fn get_mut(&mut self, handle: RawAssetHandle) -> Option<&mut T> {
367 if warn_if_null::<T>(handle, "ProcessedAssets", "get_mut", "did you forget to insert the source asset and store the returned handle?") {
368 return None;
369 }
370 let result = self.storage.get_mut(handle);
371 if result.is_none() {
372 tracing::debug!(
373 "ProcessedAssets<{}>: get_mut() for handle {:?} returned nothing — \
374 the asset may still be pending upload or was removed",
375 std::any::type_name::<T>(),
376 handle
377 );
378 }
379 result
380 }
381
382 pub fn remove(&mut self, handle: RawAssetHandle) -> Option<T> {
384 self.names.retain(|_, h| *h != handle);
385 self.storage.remove(handle)
386 }
387
388 pub fn contains(&self, handle: RawAssetHandle) -> bool {
390 self.storage.contains_key(handle)
391 }
392
393 pub fn iter(&self) -> impl Iterator<Item = (RawAssetHandle, &T)> {
395 self.storage.iter()
396 }
397
398 pub fn iter_mut(&mut self) -> impl Iterator<Item = (RawAssetHandle, &mut T)> {
400 self.storage.iter_mut()
401 }
402
403 pub fn get_by_name(&self, name: &str) -> Option<&T> {
408 let handle = self.names.get(name)?;
409 self.storage.get(*handle)
410 }
411}
412
413impl<'a, T: 'static + Send + Sync> IntoIterator for &'a ProcessedAssets<T> {
414 type Item = (RawAssetHandle, &'a T);
415 type IntoIter = slotmap::secondary::Iter<'a, RawAssetHandle, T>;
416
417 fn into_iter(self) -> Self::IntoIter {
418 self.storage.iter()
419 }
420}
421
422impl<'a, T: 'static + Send + Sync> IntoIterator for &'a mut ProcessedAssets<T> {
423 type Item = (RawAssetHandle, &'a mut T);
424 type IntoIter = slotmap::secondary::IterMut<'a, RawAssetHandle, T>;
425
426 fn into_iter(self) -> Self::IntoIter {
427 self.storage.iter_mut()
428 }
429}