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 iter(&self) -> impl Iterator<Item = (RawAssetHandle, &T)> {
204 self.storage.iter()
205 }
206
207 pub fn iter_mut(&mut self) -> impl Iterator<Item = (RawAssetHandle, &mut T)> {
209 self.storage.iter_mut()
210 }
211
212 pub fn names(&self) -> impl Iterator<Item = (&str, RawAssetHandle)> {
214 self.handles
215 .iter()
216 .map(|(name, &handle)| (name.as_str(), handle))
217 }
218
219 pub fn name_for_handle(&self, handle: RawAssetHandle) -> Option<&str> {
220 self.handles
221 .iter()
222 .find(|(_, h)| **h == handle)
223 .map(|(name, _)| name.as_str())
224 }
225}
226
227impl<'a, T: 'static + Send + Sync> IntoIterator for &'a Assets<T> {
228 type Item = (RawAssetHandle, &'a T);
229 type IntoIter = slotmap::basic::Iter<'a, RawAssetHandle, T>;
230
231 fn into_iter(self) -> Self::IntoIter {
232 self.storage.iter()
233 }
234}
235
236impl<'a, T: 'static + Send + Sync> IntoIterator for &'a mut Assets<T> {
237 type Item = (RawAssetHandle, &'a mut T);
238 type IntoIter = slotmap::basic::IterMut<'a, RawAssetHandle, T>;
239
240 fn into_iter(self) -> Self::IntoIter {
241 self.storage.iter_mut()
242 }
243}
244
245pub struct ProcessedAssets<T: 'static + Send + Sync> {
250 storage: SecondaryMap<RawAssetHandle, T>,
251 pub(crate) names: HashMap<String, RawAssetHandle>,
252}
253
254impl<T: 'static + Send + Sync> ProcessedAssets<T> {
255 pub fn new() -> Self {
256 Self {
257 storage: SecondaryMap::new(),
258 names: HashMap::new(),
259 }
260 }
261
262 pub fn insert(&mut self, handle: RawAssetHandle, asset: T) -> Option<T> {
264 self.storage.insert(handle, asset)
265 }
266
267 pub fn get(&self, handle: RawAssetHandle) -> Option<&T> {
269 if handle.is_null() {
270 tracing::warn!(
271 "ProcessedAssets<{}>: get() called with a null/default handle — \
272 did you forget to insert the source asset and store the returned handle?",
273 std::any::type_name::<T>()
274 );
275 return None;
276 }
277 let result = self.storage.get(handle);
278 if result.is_none() {
279 tracing::debug!(
280 "ProcessedAssets<{}>: get() for handle {:?} returned nothing — \
281 the asset may still be pending upload or was removed",
282 std::any::type_name::<T>(),
283 handle
284 );
285 }
286 result
287 }
288
289 pub fn get_mut(&mut self, handle: RawAssetHandle) -> Option<&mut T> {
291 if handle.is_null() {
292 tracing::warn!(
293 "ProcessedAssets<{}>: get_mut() called with a null/default handle — \
294 did you forget to insert the source asset and store the returned handle?",
295 std::any::type_name::<T>()
296 );
297 return None;
298 }
299 let result = self.storage.get_mut(handle);
300 if result.is_none() {
301 tracing::debug!(
302 "ProcessedAssets<{}>: get_mut() for handle {:?} returned nothing — \
303 the asset may still be pending upload or was removed",
304 std::any::type_name::<T>(),
305 handle
306 );
307 }
308 result
309 }
310
311 pub fn remove(&mut self, handle: RawAssetHandle) -> Option<T> {
313 self.names.retain(|_, h| *h != handle);
314 self.storage.remove(handle)
315 }
316
317 pub fn contains(&self, handle: RawAssetHandle) -> bool {
319 self.storage.contains_key(handle)
320 }
321
322 pub fn iter(&self) -> impl Iterator<Item = (RawAssetHandle, &T)> {
324 self.storage.iter()
325 }
326
327 pub fn iter_mut(&mut self) -> impl Iterator<Item = (RawAssetHandle, &mut T)> {
329 self.storage.iter_mut()
330 }
331
332 pub fn get_by_name(&self, name: &str) -> Option<&T> {
333 let handle = self.names.get(name)?;
334 self.storage.get(*handle)
335 }
336}
337
338impl<'a, T: 'static + Send + Sync> IntoIterator for &'a ProcessedAssets<T> {
339 type Item = (RawAssetHandle, &'a T);
340 type IntoIter = slotmap::secondary::Iter<'a, RawAssetHandle, T>;
341
342 fn into_iter(self) -> Self::IntoIter {
343 self.storage.iter()
344 }
345}
346
347impl<'a, T: 'static + Send + Sync> IntoIterator for &'a mut ProcessedAssets<T> {
348 type Item = (RawAssetHandle, &'a mut T);
349 type IntoIter = slotmap::secondary::IterMut<'a, RawAssetHandle, T>;
350
351 fn into_iter(self) -> Self::IntoIter {
352 self.storage.iter_mut()
353 }
354}