1use crate::assets::{Asset, AssetId, AssetState, AssetType};
46use std::borrow::Cow;
47use std::fmt;
48use std::hash::{Hash, Hasher};
49use std::marker::PhantomData;
50use std::path::Path;
51
52#[repr(C)]
102pub struct AssetHandle<A: Asset> {
103 index: u32,
105
106 generation: u32,
108
109 _marker: PhantomData<A>,
111}
112
113impl<A: Asset> AssetHandle<A> {
114 pub const INVALID: Self = Self {
133 index: u32::MAX,
134 generation: 0,
135 _marker: PhantomData,
136 };
137
138 #[inline]
160 pub const fn new(index: u32, generation: u32) -> Self {
161 Self {
162 index,
163 generation,
164 _marker: PhantomData,
165 }
166 }
167
168 #[inline]
172 pub const fn index(&self) -> u32 {
173 self.index
174 }
175
176 #[inline]
181 pub const fn generation(&self) -> u32 {
182 self.generation
183 }
184
185 #[inline]
205 pub const fn is_valid(&self) -> bool {
206 !(self.index == u32::MAX && self.generation == 0)
207 }
208
209 #[inline]
229 pub fn untyped(&self) -> UntypedAssetHandle {
230 UntypedAssetHandle::new(self.index, self.generation, AssetId::of::<A>())
231 }
232
233 #[inline]
240 pub const fn to_u64(&self) -> u64 {
241 ((self.generation as u64) << 32) | (self.index as u64)
242 }
243
244 #[inline]
248 pub const fn from_u64(packed: u64) -> Self {
249 let index = packed as u32;
250 let generation = (packed >> 32) as u32;
251 Self::new(index, generation)
252 }
253
254 #[inline]
258 pub fn asset_id() -> AssetId {
259 AssetId::of::<A>()
260 }
261
262 #[inline]
266 pub fn asset_type() -> AssetType {
267 A::asset_type()
268 }
269}
270
271impl<A: Asset> Clone for AssetHandle<A> {
274 #[inline]
275 fn clone(&self) -> Self {
276 *self
277 }
278}
279
280impl<A: Asset> Copy for AssetHandle<A> {}
281
282impl<A: Asset> Default for AssetHandle<A> {
283 #[inline]
285 fn default() -> Self {
286 Self::INVALID
287 }
288}
289
290impl<A: Asset> fmt::Debug for AssetHandle<A> {
291 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292 let type_name = A::asset_type_name();
293 if self.is_valid() {
294 write!(
295 f,
296 "AssetHandle<{}>({}:{})",
297 type_name, self.index, self.generation
298 )
299 } else {
300 write!(f, "AssetHandle<{}>(INVALID)", type_name)
301 }
302 }
303}
304
305impl<A: Asset> fmt::Display for AssetHandle<A> {
306 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
307 if self.is_valid() {
308 write!(f, "{}:{}", self.index, self.generation)
309 } else {
310 write!(f, "INVALID")
311 }
312 }
313}
314
315impl<A: Asset> PartialEq for AssetHandle<A> {
316 #[inline]
317 fn eq(&self, other: &Self) -> bool {
318 self.index == other.index && self.generation == other.generation
319 }
320}
321
322impl<A: Asset> Eq for AssetHandle<A> {}
323
324impl<A: Asset> Hash for AssetHandle<A> {
325 #[inline]
326 fn hash<H: Hasher>(&self, state: &mut H) {
327 self.to_u64().hash(state);
328 }
329}
330
331impl<A: Asset> PartialOrd for AssetHandle<A> {
332 #[inline]
333 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
334 Some(self.cmp(other))
335 }
336}
337
338impl<A: Asset> Ord for AssetHandle<A> {
339 #[inline]
340 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
341 self.to_u64().cmp(&other.to_u64())
342 }
343}
344
345impl<A: Asset> From<AssetHandle<A>> for u64 {
347 #[inline]
348 fn from(handle: AssetHandle<A>) -> u64 {
349 handle.to_u64()
350 }
351}
352
353impl<A: Asset> From<u64> for AssetHandle<A> {
354 #[inline]
355 fn from(packed: u64) -> Self {
356 Self::from_u64(packed)
357 }
358}
359
360#[derive(Clone, Copy)]
419pub struct UntypedAssetHandle {
420 index: u32,
422
423 generation: u32,
425
426 asset_id: AssetId,
428}
429
430impl UntypedAssetHandle {
431 pub fn invalid() -> Self {
435 Self {
436 index: u32::MAX,
437 generation: 0,
438 asset_id: AssetId::of_raw::<()>(),
439 }
440 }
441
442 #[inline]
450 pub const fn new(index: u32, generation: u32, asset_id: AssetId) -> Self {
451 Self {
452 index,
453 generation,
454 asset_id,
455 }
456 }
457
458 #[inline]
462 pub fn from_typed<A: Asset>(handle: AssetHandle<A>) -> Self {
463 handle.untyped()
464 }
465
466 #[inline]
468 pub const fn index(&self) -> u32 {
469 self.index
470 }
471
472 #[inline]
474 pub const fn generation(&self) -> u32 {
475 self.generation
476 }
477
478 #[inline]
480 pub const fn asset_id(&self) -> AssetId {
481 self.asset_id
482 }
483
484 #[inline]
486 pub fn is_valid(&self) -> bool {
487 !(self.index == u32::MAX && self.generation == 0)
488 }
489
490 #[inline]
520 pub fn typed<A: Asset>(&self) -> Option<AssetHandle<A>> {
521 if self.asset_id == AssetId::of::<A>() {
522 Some(AssetHandle::new(self.index, self.generation))
523 } else {
524 None
525 }
526 }
527
528 #[inline]
552 pub unsafe fn typed_unchecked<A: Asset>(&self) -> AssetHandle<A> {
553 AssetHandle::new(self.index, self.generation)
554 }
555
556 #[inline]
576 pub fn is_type<A: Asset>(&self) -> bool {
577 self.asset_id == AssetId::of::<A>()
578 }
579
580 #[inline]
582 pub const fn to_u64(&self) -> u64 {
583 ((self.generation as u64) << 32) | (self.index as u64)
584 }
585}
586
587impl Default for UntypedAssetHandle {
588 #[inline]
589 fn default() -> Self {
590 Self::invalid()
591 }
592}
593
594impl fmt::Debug for UntypedAssetHandle {
595 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
596 if self.is_valid() {
597 write!(
598 f,
599 "UntypedAssetHandle({}:{}, {:?})",
600 self.index, self.generation, self.asset_id
601 )
602 } else {
603 write!(f, "UntypedAssetHandle(INVALID)")
604 }
605 }
606}
607
608impl fmt::Display for UntypedAssetHandle {
609 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
610 if self.is_valid() {
611 write!(f, "{}:{}", self.index, self.generation)
612 } else {
613 write!(f, "INVALID")
614 }
615 }
616}
617
618impl PartialEq for UntypedAssetHandle {
619 #[inline]
620 fn eq(&self, other: &Self) -> bool {
621 self.index == other.index
622 && self.generation == other.generation
623 && self.asset_id == other.asset_id
624 }
625}
626
627impl Eq for UntypedAssetHandle {}
628
629impl Hash for UntypedAssetHandle {
630 #[inline]
631 fn hash<H: Hasher>(&self, state: &mut H) {
632 self.index.hash(state);
633 self.generation.hash(state);
634 self.asset_id.hash(state);
635 }
636}
637
638#[derive(Debug, Clone, PartialEq)]
667pub struct HandleLoadState<A: Asset> {
668 handle: AssetHandle<A>,
670
671 state: AssetState,
673}
674
675impl<A: Asset> HandleLoadState<A> {
676 #[inline]
678 pub fn new(handle: AssetHandle<A>, state: AssetState) -> Self {
679 Self { handle, state }
680 }
681
682 #[inline]
684 pub fn invalid() -> Self {
685 Self {
686 handle: AssetHandle::INVALID,
687 state: AssetState::NotLoaded,
688 }
689 }
690
691 #[inline]
693 pub fn handle(&self) -> &AssetHandle<A> {
694 &self.handle
695 }
696
697 #[inline]
699 pub fn state(&self) -> &AssetState {
700 &self.state
701 }
702
703 #[inline]
705 pub fn is_valid(&self) -> bool {
706 self.handle.is_valid()
707 }
708
709 #[inline]
711 pub fn is_ready(&self) -> bool {
712 self.handle.is_valid() && self.state.is_ready()
713 }
714
715 #[inline]
717 pub fn is_loading(&self) -> bool {
718 self.state.is_loading()
719 }
720
721 #[inline]
723 pub fn is_failed(&self) -> bool {
724 self.state.is_failed()
725 }
726
727 #[inline]
729 pub fn progress(&self) -> Option<f32> {
730 self.state.progress()
731 }
732
733 #[inline]
735 pub fn error(&self) -> Option<&str> {
736 self.state.error()
737 }
738
739 #[inline]
741 pub fn into_handle(self) -> AssetHandle<A> {
742 self.handle
743 }
744
745 #[inline]
747 pub fn set_state(&mut self, state: AssetState) {
748 self.state = state;
749 }
750}
751
752impl<A: Asset> Default for HandleLoadState<A> {
753 #[inline]
754 fn default() -> Self {
755 Self::invalid()
756 }
757}
758
759#[derive(Clone, PartialEq, Eq, Hash)]
798pub struct AssetPath<'a> {
799 path: Cow<'a, str>,
801}
802
803impl<'a> AssetPath<'a> {
804 #[inline]
815 pub fn new(path: &'a str) -> Self {
816 Self {
817 path: Cow::Borrowed(path),
818 }
819 }
820
821 #[inline]
832 pub fn from_string(path: String) -> AssetPath<'static> {
833 AssetPath {
834 path: Cow::Owned(path),
835 }
836 }
837
838 #[inline]
840 pub fn as_str(&self) -> &str {
841 &self.path
842 }
843
844 #[inline]
846 pub fn is_empty(&self) -> bool {
847 self.path.is_empty()
848 }
849
850 #[inline]
852 pub fn len(&self) -> usize {
853 self.path.len()
854 }
855
856 pub fn file_name(&self) -> Option<&str> {
868 let path = self.path.as_ref();
869 if path.ends_with('/') {
870 return None;
871 }
872 path.rsplit('/').next().filter(|s| !s.is_empty())
873 }
874
875 pub fn extension(&self) -> Option<&str> {
888 let file_name = self.file_name()?;
889 let dot_pos = file_name.rfind('.')?;
890
891 if dot_pos == 0 {
893 return None;
894 }
895
896 Some(&file_name[dot_pos + 1..])
897 }
898
899 pub fn directory(&self) -> Option<&str> {
911 let path = self.path.as_ref();
912 let pos = path.rfind('/')?;
913 if pos == 0 {
914 return None;
915 }
916 Some(&path[..pos])
917 }
918
919 pub fn stem(&self) -> Option<&str> {
932 let file_name = self.file_name()?;
933 if let Some(dot_pos) = file_name.rfind('.') {
934 if dot_pos == 0 {
935 Some(file_name)
937 } else {
938 Some(&file_name[..dot_pos])
939 }
940 } else {
941 Some(file_name)
943 }
944 }
945
946 pub fn into_owned(self) -> AssetPath<'static> {
951 AssetPath {
952 path: Cow::Owned(self.path.into_owned()),
953 }
954 }
955
956 pub fn from_path(path: &Path) -> AssetPath<'static> {
970 let path_str = path.to_string_lossy();
971 let normalized = path_str.replace('\\', "/");
973 AssetPath::from_string(normalized)
974 }
975
976 pub fn join(&self, other: &str) -> AssetPath<'static> {
993 let base = self.path.trim_end_matches('/');
994 let other = other.trim_start_matches('/');
995
996 if base.is_empty() {
997 AssetPath::from_string(other.to_string())
998 } else if other.is_empty() {
999 AssetPath::from_string(base.to_string())
1000 } else {
1001 AssetPath::from_string(format!("{}/{}", base, other))
1002 }
1003 }
1004
1005 pub fn with_extension(&self, ext: &str) -> AssetPath<'static> {
1022 if let Some(stem) = self.stem() {
1023 if let Some(dir) = self.directory() {
1024 AssetPath::from_string(format!("{}/{}.{}", dir, stem, ext))
1025 } else {
1026 AssetPath::from_string(format!("{}.{}", stem, ext))
1027 }
1028 } else {
1029 AssetPath::from_string(format!("{}.{}", self.path, ext))
1031 }
1032 }
1033}
1034
1035impl<'a> fmt::Debug for AssetPath<'a> {
1036 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1037 write!(f, "AssetPath({:?})", self.path)
1038 }
1039}
1040
1041impl<'a> fmt::Display for AssetPath<'a> {
1042 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1043 write!(f, "{}", self.path)
1044 }
1045}
1046
1047impl<'a> AsRef<str> for AssetPath<'a> {
1048 #[inline]
1049 fn as_ref(&self) -> &str {
1050 &self.path
1051 }
1052}
1053
1054impl<'a> From<&'a str> for AssetPath<'a> {
1055 #[inline]
1056 fn from(s: &'a str) -> Self {
1057 Self::new(s)
1058 }
1059}
1060
1061impl From<String> for AssetPath<'static> {
1062 #[inline]
1063 fn from(s: String) -> Self {
1064 Self::from_string(s)
1065 }
1066}
1067
1068impl<'a> PartialEq<str> for AssetPath<'a> {
1069 #[inline]
1070 fn eq(&self, other: &str) -> bool {
1071 self.path.as_ref() == other
1072 }
1073}
1074
1075impl<'a> PartialEq<&str> for AssetPath<'a> {
1076 #[inline]
1077 fn eq(&self, other: &&str) -> bool {
1078 self.path.as_ref() == *other
1079 }
1080}
1081
1082#[repr(C)]
1112pub struct WeakAssetHandle<A: Asset> {
1113 index: u32,
1115
1116 generation: u32,
1118
1119 _marker: PhantomData<A>,
1121}
1122
1123impl<A: Asset> WeakAssetHandle<A> {
1124 pub const INVALID: Self = Self {
1126 index: u32::MAX,
1127 generation: 0,
1128 _marker: PhantomData,
1129 };
1130
1131 #[inline]
1133 pub fn from_handle(handle: &AssetHandle<A>) -> Self {
1134 Self {
1135 index: handle.index,
1136 generation: handle.generation,
1137 _marker: PhantomData,
1138 }
1139 }
1140
1141 #[inline]
1143 pub const fn new(index: u32, generation: u32) -> Self {
1144 Self {
1145 index,
1146 generation,
1147 _marker: PhantomData,
1148 }
1149 }
1150
1151 #[inline]
1153 pub const fn index(&self) -> u32 {
1154 self.index
1155 }
1156
1157 #[inline]
1159 pub const fn generation(&self) -> u32 {
1160 self.generation
1161 }
1162
1163 #[inline]
1165 pub const fn is_valid(&self) -> bool {
1166 !(self.index == u32::MAX && self.generation == 0)
1167 }
1168
1169 #[inline]
1174 pub fn upgrade(&self) -> AssetHandle<A> {
1175 AssetHandle::new(self.index, self.generation)
1176 }
1177}
1178
1179impl<A: Asset> Clone for WeakAssetHandle<A> {
1180 #[inline]
1181 fn clone(&self) -> Self {
1182 *self
1183 }
1184}
1185
1186impl<A: Asset> Copy for WeakAssetHandle<A> {}
1187
1188impl<A: Asset> Default for WeakAssetHandle<A> {
1189 #[inline]
1190 fn default() -> Self {
1191 Self::INVALID
1192 }
1193}
1194
1195impl<A: Asset> fmt::Debug for WeakAssetHandle<A> {
1196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1197 let type_name = A::asset_type_name();
1198 if self.is_valid() {
1199 write!(
1200 f,
1201 "WeakAssetHandle<{}>({}:{})",
1202 type_name, self.index, self.generation
1203 )
1204 } else {
1205 write!(f, "WeakAssetHandle<{}>(INVALID)", type_name)
1206 }
1207 }
1208}
1209
1210impl<A: Asset> PartialEq for WeakAssetHandle<A> {
1211 #[inline]
1212 fn eq(&self, other: &Self) -> bool {
1213 self.index == other.index && self.generation == other.generation
1214 }
1215}
1216
1217impl<A: Asset> Eq for WeakAssetHandle<A> {}
1218
1219impl<A: Asset> Hash for WeakAssetHandle<A> {
1220 #[inline]
1221 fn hash<H: Hasher>(&self, state: &mut H) {
1222 self.index.hash(state);
1223 self.generation.hash(state);
1224 }
1225}
1226
1227impl<A: Asset> From<&AssetHandle<A>> for WeakAssetHandle<A> {
1228 #[inline]
1229 fn from(handle: &AssetHandle<A>) -> Self {
1230 Self::from_handle(handle)
1231 }
1232}
1233
1234pub struct AssetHandleAllocator<A: Asset> {
1276 generations: Vec<u32>,
1279
1280 free_list: Vec<u32>,
1282
1283 _marker: PhantomData<A>,
1285}
1286
1287impl<A: Asset> AssetHandleAllocator<A> {
1288 #[inline]
1302 pub fn new() -> Self {
1303 Self {
1304 generations: Vec::new(),
1305 free_list: Vec::new(),
1306 _marker: PhantomData,
1307 }
1308 }
1309
1310 #[inline]
1328 pub fn with_capacity(capacity: usize) -> Self {
1329 Self {
1330 generations: Vec::with_capacity(capacity),
1331 free_list: Vec::new(),
1332 _marker: PhantomData,
1333 }
1334 }
1335
1336 pub fn allocate(&mut self) -> AssetHandle<A> {
1363 if let Some(index) = self.free_list.pop() {
1364 let generation = self.generations[index as usize];
1366 AssetHandle::new(index, generation)
1367 } else {
1368 let index = self.generations.len();
1370 assert!(
1371 index < u32::MAX as usize,
1372 "AssetHandleAllocator exceeded maximum capacity"
1373 );
1374
1375 self.generations.push(1);
1377 AssetHandle::new(index as u32, 1)
1378 }
1379 }
1380
1381 pub fn deallocate(&mut self, handle: AssetHandle<A>) -> bool {
1411 if !handle.is_valid() {
1412 return false;
1413 }
1414
1415 let index = handle.index() as usize;
1416
1417 if index >= self.generations.len() {
1419 return false;
1420 }
1421
1422 if self.generations[index] != handle.generation() {
1424 return false;
1425 }
1426
1427 let new_gen = self.generations[index].wrapping_add(1);
1429 self.generations[index] = if new_gen == 0 { 1 } else { new_gen };
1430
1431 self.free_list.push(handle.index());
1433
1434 true
1435 }
1436
1437 #[inline]
1464 pub fn is_alive(&self, handle: AssetHandle<A>) -> bool {
1465 if !handle.is_valid() {
1466 return false;
1467 }
1468
1469 let index = handle.index() as usize;
1470 index < self.generations.len() && self.generations[index] == handle.generation()
1471 }
1472
1473 #[inline]
1494 pub fn len(&self) -> usize {
1495 self.generations.len() - self.free_list.len()
1496 }
1497
1498 #[inline]
1502 pub fn capacity(&self) -> usize {
1503 self.generations.len()
1504 }
1505
1506 #[inline]
1508 pub fn is_empty(&self) -> bool {
1509 self.len() == 0
1510 }
1511
1512 pub fn clear(&mut self) {
1536 for gen in &mut self.generations {
1538 let new_gen = gen.wrapping_add(1);
1539 *gen = if new_gen == 0 { 1 } else { new_gen };
1540 }
1541
1542 self.free_list.clear();
1544 self.free_list.reserve(self.generations.len());
1545 for i in (0..self.generations.len()).rev() {
1546 self.free_list.push(i as u32);
1547 }
1548 }
1549
1550 #[inline]
1552 pub fn shrink_to_fit(&mut self) {
1553 self.free_list.shrink_to_fit();
1554 }
1555
1556 #[inline]
1560 pub fn generation_at(&self, index: u32) -> Option<u32> {
1561 self.generations.get(index as usize).copied()
1562 }
1563}
1564
1565impl<A: Asset> Default for AssetHandleAllocator<A> {
1566 #[inline]
1567 fn default() -> Self {
1568 Self::new()
1569 }
1570}
1571
1572impl<A: Asset> fmt::Debug for AssetHandleAllocator<A> {
1573 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1574 let type_name = A::asset_type_name();
1575 f.debug_struct(&format!("AssetHandleAllocator<{}>", type_name))
1576 .field("len", &self.len())
1577 .field("capacity", &self.capacity())
1578 .field("free_slots", &self.free_list.len())
1579 .finish()
1580 }
1581}
1582
1583#[cfg(test)]
1588mod tests {
1589 use super::*;
1590
1591 #[derive(Clone, Debug, PartialEq)]
1593 struct TestTexture {
1594 #[allow(dead_code)]
1595 width: u32,
1596 }
1597
1598 impl Asset for TestTexture {
1599 fn asset_type_name() -> &'static str {
1600 "TestTexture"
1601 }
1602
1603 fn asset_type() -> AssetType {
1604 AssetType::Texture
1605 }
1606 }
1607
1608 #[derive(Clone, Debug, PartialEq)]
1609 struct TestAudio {
1610 #[allow(dead_code)]
1611 duration: f32,
1612 }
1613
1614 impl Asset for TestAudio {
1615 fn asset_type_name() -> &'static str {
1616 "TestAudio"
1617 }
1618
1619 fn asset_type() -> AssetType {
1620 AssetType::Audio
1621 }
1622 }
1623
1624 struct SimpleAsset;
1626 impl Asset for SimpleAsset {}
1627
1628 mod asset_handle {
1633 use super::*;
1634
1635 #[test]
1636 fn test_new() {
1637 let handle: AssetHandle<TestTexture> = AssetHandle::new(42, 7);
1638 assert_eq!(handle.index(), 42);
1639 assert_eq!(handle.generation(), 7);
1640 }
1641
1642 #[test]
1643 fn test_invalid() {
1644 let handle: AssetHandle<TestTexture> = AssetHandle::INVALID;
1645 assert_eq!(handle.index(), u32::MAX);
1646 assert_eq!(handle.generation(), 0);
1647 assert!(!handle.is_valid());
1648 }
1649
1650 #[test]
1651 fn test_is_valid() {
1652 let valid: AssetHandle<TestTexture> = AssetHandle::new(0, 1);
1653 assert!(valid.is_valid());
1654
1655 let invalid: AssetHandle<TestTexture> = AssetHandle::INVALID;
1656 assert!(!invalid.is_valid());
1657
1658 let edge: AssetHandle<TestTexture> = AssetHandle::new(u32::MAX, 1);
1660 assert!(edge.is_valid());
1661 }
1662
1663 #[test]
1664 fn test_default() {
1665 let handle: AssetHandle<TestTexture> = Default::default();
1666 assert!(!handle.is_valid());
1667 assert_eq!(handle, AssetHandle::INVALID);
1668 }
1669
1670 #[test]
1671 fn test_clone_copy() {
1672 let h1: AssetHandle<TestTexture> = AssetHandle::new(10, 5);
1673 let h2 = h1; let h3 = h1.clone();
1675
1676 assert_eq!(h1, h2);
1677 assert_eq!(h1, h3);
1678 }
1679
1680 #[test]
1681 fn test_equality() {
1682 let h1: AssetHandle<TestTexture> = AssetHandle::new(1, 1);
1683 let h2: AssetHandle<TestTexture> = AssetHandle::new(1, 1);
1684 let h3: AssetHandle<TestTexture> = AssetHandle::new(1, 2);
1685 let h4: AssetHandle<TestTexture> = AssetHandle::new(2, 1);
1686
1687 assert_eq!(h1, h2);
1688 assert_ne!(h1, h3); assert_ne!(h1, h4); }
1691
1692 #[test]
1693 fn test_hash() {
1694 use std::collections::HashSet;
1695
1696 let mut set = HashSet::new();
1697 set.insert(AssetHandle::<TestTexture>::new(1, 1));
1698 set.insert(AssetHandle::<TestTexture>::new(2, 1));
1699
1700 assert_eq!(set.len(), 2);
1701
1702 set.insert(AssetHandle::<TestTexture>::new(1, 1));
1704 assert_eq!(set.len(), 2);
1705 }
1706
1707 #[test]
1708 fn test_ord() {
1709 use std::collections::BTreeSet;
1710
1711 let mut set = BTreeSet::new();
1712 set.insert(AssetHandle::<TestTexture>::new(3, 1));
1713 set.insert(AssetHandle::<TestTexture>::new(1, 1));
1714 set.insert(AssetHandle::<TestTexture>::new(2, 1));
1715
1716 let vec: Vec<_> = set.iter().collect();
1717 assert!(vec[0].index() < vec[1].index());
1718 assert!(vec[1].index() < vec[2].index());
1719 }
1720
1721 #[test]
1722 fn test_debug() {
1723 let handle: AssetHandle<TestTexture> = AssetHandle::new(42, 7);
1724 let debug_str = format!("{:?}", handle);
1725 assert!(debug_str.contains("AssetHandle"));
1726 assert!(debug_str.contains("TestTexture"));
1727 assert!(debug_str.contains("42"));
1728 assert!(debug_str.contains("7"));
1729
1730 let invalid: AssetHandle<TestTexture> = AssetHandle::INVALID;
1731 let debug_str = format!("{:?}", invalid);
1732 assert!(debug_str.contains("INVALID"));
1733 }
1734
1735 #[test]
1736 fn test_display() {
1737 let handle: AssetHandle<TestTexture> = AssetHandle::new(42, 7);
1738 assert_eq!(format!("{}", handle), "42:7");
1739
1740 let invalid: AssetHandle<TestTexture> = AssetHandle::INVALID;
1741 assert_eq!(format!("{}", invalid), "INVALID");
1742 }
1743
1744 #[test]
1745 fn test_to_u64() {
1746 let handle: AssetHandle<TestTexture> = AssetHandle::new(42, 7);
1747 let packed = handle.to_u64();
1748
1749 assert_eq!(packed & 0xFFFFFFFF, 42);
1751 assert_eq!(packed >> 32, 7);
1752 }
1753
1754 #[test]
1755 fn test_from_u64() {
1756 let packed: u64 = (7u64 << 32) | 42u64;
1757 let handle: AssetHandle<TestTexture> = AssetHandle::from_u64(packed);
1758
1759 assert_eq!(handle.index(), 42);
1760 assert_eq!(handle.generation(), 7);
1761 }
1762
1763 #[test]
1764 fn test_u64_roundtrip() {
1765 let original: AssetHandle<TestTexture> = AssetHandle::new(12345, 99);
1766 let packed = original.to_u64();
1767 let recovered: AssetHandle<TestTexture> = AssetHandle::from_u64(packed);
1768
1769 assert_eq!(original, recovered);
1770 }
1771
1772 #[test]
1773 fn test_from_into_u64() {
1774 let handle: AssetHandle<TestTexture> = AssetHandle::new(10, 20);
1775 let packed: u64 = handle.into();
1776 let recovered: AssetHandle<TestTexture> = packed.into();
1777
1778 assert_eq!(handle, recovered);
1779 }
1780
1781 #[test]
1782 fn test_untyped() {
1783 let typed: AssetHandle<TestTexture> = AssetHandle::new(5, 3);
1784 let untyped = typed.untyped();
1785
1786 assert_eq!(untyped.index(), 5);
1787 assert_eq!(untyped.generation(), 3);
1788 assert_eq!(untyped.asset_id(), AssetId::of::<TestTexture>());
1789 }
1790
1791 #[test]
1792 fn test_asset_id() {
1793 assert_eq!(
1794 AssetHandle::<TestTexture>::asset_id(),
1795 AssetId::of::<TestTexture>()
1796 );
1797 assert_ne!(
1798 AssetHandle::<TestTexture>::asset_id(),
1799 AssetHandle::<TestAudio>::asset_id()
1800 );
1801 }
1802
1803 #[test]
1804 fn test_asset_type() {
1805 assert_eq!(AssetHandle::<TestTexture>::asset_type(), AssetType::Texture);
1806 assert_eq!(AssetHandle::<TestAudio>::asset_type(), AssetType::Audio);
1807 }
1808
1809 #[test]
1810 fn test_size_and_align() {
1811 assert_eq!(std::mem::size_of::<AssetHandle<TestTexture>>(), 8);
1813 assert_eq!(std::mem::align_of::<AssetHandle<TestTexture>>(), 4);
1814 }
1815
1816 #[test]
1817 fn test_is_send() {
1818 fn requires_send<T: Send>() {}
1819 requires_send::<AssetHandle<TestTexture>>();
1820 }
1821
1822 #[test]
1823 fn test_is_sync() {
1824 fn requires_sync<T: Sync>() {}
1825 requires_sync::<AssetHandle<TestTexture>>();
1826 }
1827 }
1828
1829 mod untyped_asset_handle {
1834 use super::*;
1835
1836 #[test]
1837 fn test_new() {
1838 let handle = UntypedAssetHandle::new(42, 7, AssetId::of::<TestTexture>());
1839 assert_eq!(handle.index(), 42);
1840 assert_eq!(handle.generation(), 7);
1841 assert_eq!(handle.asset_id(), AssetId::of::<TestTexture>());
1842 }
1843
1844 #[test]
1845 fn test_invalid() {
1846 let handle = UntypedAssetHandle::invalid();
1847 assert!(!handle.is_valid());
1848 }
1849
1850 #[test]
1851 fn test_from_typed() {
1852 let typed: AssetHandle<TestTexture> = AssetHandle::new(10, 5);
1853 let untyped = UntypedAssetHandle::from_typed(typed);
1854
1855 assert_eq!(untyped.index(), 10);
1856 assert_eq!(untyped.generation(), 5);
1857 assert_eq!(untyped.asset_id(), AssetId::of::<TestTexture>());
1858 }
1859
1860 #[test]
1861 fn test_typed() {
1862 let typed: AssetHandle<TestTexture> = AssetHandle::new(10, 5);
1863 let untyped = typed.untyped();
1864
1865 let recovered: Option<AssetHandle<TestTexture>> = untyped.typed();
1867 assert!(recovered.is_some());
1868 assert_eq!(recovered.unwrap(), typed);
1869
1870 let wrong: Option<AssetHandle<TestAudio>> = untyped.typed();
1872 assert!(wrong.is_none());
1873 }
1874
1875 #[test]
1876 fn test_typed_unchecked() {
1877 let typed: AssetHandle<TestTexture> = AssetHandle::new(10, 5);
1878 let untyped = typed.untyped();
1879
1880 let recovered: AssetHandle<TestTexture> = unsafe { untyped.typed_unchecked() };
1882 assert_eq!(typed, recovered);
1883 }
1884
1885 #[test]
1886 fn test_is_type() {
1887 let typed: AssetHandle<TestTexture> = AssetHandle::new(10, 5);
1888 let untyped = typed.untyped();
1889
1890 assert!(untyped.is_type::<TestTexture>());
1891 assert!(!untyped.is_type::<TestAudio>());
1892 }
1893
1894 #[test]
1895 fn test_equality() {
1896 let h1 = UntypedAssetHandle::new(1, 1, AssetId::of::<TestTexture>());
1897 let h2 = UntypedAssetHandle::new(1, 1, AssetId::of::<TestTexture>());
1898 let h3 = UntypedAssetHandle::new(1, 1, AssetId::of::<TestAudio>()); let h4 = UntypedAssetHandle::new(1, 2, AssetId::of::<TestTexture>()); assert_eq!(h1, h2);
1902 assert_ne!(h1, h3); assert_ne!(h1, h4); }
1905
1906 #[test]
1907 fn test_hash() {
1908 use std::collections::HashSet;
1909
1910 let mut set = HashSet::new();
1911 set.insert(UntypedAssetHandle::new(1, 1, AssetId::of::<TestTexture>()));
1912 set.insert(UntypedAssetHandle::new(1, 1, AssetId::of::<TestAudio>()));
1913
1914 assert_eq!(set.len(), 2);
1916 }
1917
1918 #[test]
1919 fn test_debug() {
1920 let handle = UntypedAssetHandle::new(42, 7, AssetId::of::<TestTexture>());
1921 let debug_str = format!("{:?}", handle);
1922 assert!(debug_str.contains("UntypedAssetHandle"));
1923 assert!(debug_str.contains("42"));
1924 assert!(debug_str.contains("7"));
1925
1926 let invalid = UntypedAssetHandle::invalid();
1927 let debug_str = format!("{:?}", invalid);
1928 assert!(debug_str.contains("INVALID"));
1929 }
1930
1931 #[test]
1932 fn test_display() {
1933 let handle = UntypedAssetHandle::new(42, 7, AssetId::of::<TestTexture>());
1934 assert_eq!(format!("{}", handle), "42:7");
1935
1936 let invalid = UntypedAssetHandle::invalid();
1937 assert_eq!(format!("{}", invalid), "INVALID");
1938 }
1939
1940 #[test]
1941 fn test_clone_copy() {
1942 let h1 = UntypedAssetHandle::new(1, 1, AssetId::of::<TestTexture>());
1943 let h2 = h1; let h3 = h1.clone();
1945
1946 assert_eq!(h1, h2);
1947 assert_eq!(h1, h3);
1948 }
1949
1950 #[test]
1951 fn test_default() {
1952 let handle: UntypedAssetHandle = Default::default();
1953 assert!(!handle.is_valid());
1954 }
1955
1956 #[test]
1957 fn test_is_send() {
1958 fn requires_send<T: Send>() {}
1959 requires_send::<UntypedAssetHandle>();
1960 }
1961
1962 #[test]
1963 fn test_is_sync() {
1964 fn requires_sync<T: Sync>() {}
1965 requires_sync::<UntypedAssetHandle>();
1966 }
1967 }
1968
1969 mod handle_load_state {
1974 use super::*;
1975
1976 #[test]
1977 fn test_new() {
1978 let handle: AssetHandle<TestTexture> = AssetHandle::new(1, 1);
1979 let state = HandleLoadState::new(handle, AssetState::Loaded);
1980
1981 assert_eq!(*state.handle(), handle);
1982 assert!(state.is_ready());
1983 }
1984
1985 #[test]
1986 fn test_invalid() {
1987 let state: HandleLoadState<TestTexture> = HandleLoadState::invalid();
1988 assert!(!state.is_valid());
1989 assert!(!state.is_ready());
1990 assert_eq!(*state.state(), AssetState::NotLoaded);
1991 }
1992
1993 #[test]
1994 fn test_is_ready() {
1995 let handle: AssetHandle<TestTexture> = AssetHandle::new(1, 1);
1996
1997 let loaded = HandleLoadState::new(handle, AssetState::Loaded);
1998 assert!(loaded.is_ready());
1999
2000 let loading = HandleLoadState::new(handle, AssetState::Loading { progress: 0.5 });
2001 assert!(!loading.is_ready());
2002
2003 let failed = HandleLoadState::new(
2004 handle,
2005 AssetState::Failed {
2006 error: "test".to_string(),
2007 },
2008 );
2009 assert!(!failed.is_ready());
2010 }
2011
2012 #[test]
2013 fn test_is_loading() {
2014 let handle: AssetHandle<TestTexture> = AssetHandle::new(1, 1);
2015 let state = HandleLoadState::new(handle, AssetState::Loading { progress: 0.5 });
2016
2017 assert!(state.is_loading());
2018 assert_eq!(state.progress(), Some(0.5));
2019 }
2020
2021 #[test]
2022 fn test_is_failed() {
2023 let handle: AssetHandle<TestTexture> = AssetHandle::new(1, 1);
2024 let state = HandleLoadState::new(
2025 handle,
2026 AssetState::Failed {
2027 error: "File not found".to_string(),
2028 },
2029 );
2030
2031 assert!(state.is_failed());
2032 assert_eq!(state.error(), Some("File not found"));
2033 }
2034
2035 #[test]
2036 fn test_into_handle() {
2037 let handle: AssetHandle<TestTexture> = AssetHandle::new(1, 1);
2038 let state = HandleLoadState::new(handle, AssetState::Loaded);
2039
2040 let recovered = state.into_handle();
2041 assert_eq!(recovered, handle);
2042 }
2043
2044 #[test]
2045 fn test_set_state() {
2046 let handle: AssetHandle<TestTexture> = AssetHandle::new(1, 1);
2047 let mut state = HandleLoadState::new(handle, AssetState::Loading { progress: 0.0 });
2048
2049 assert!(state.is_loading());
2050
2051 state.set_state(AssetState::Loaded);
2052 assert!(state.is_ready());
2053 }
2054
2055 #[test]
2056 fn test_default() {
2057 let state: HandleLoadState<TestTexture> = Default::default();
2058 assert!(!state.is_valid());
2059 }
2060
2061 #[test]
2062 fn test_clone() {
2063 let handle: AssetHandle<TestTexture> = AssetHandle::new(1, 1);
2064 let state1 = HandleLoadState::new(handle, AssetState::Loaded);
2065 let state2 = state1.clone();
2066
2067 assert_eq!(state1, state2);
2068 }
2069 }
2070
2071 mod asset_path {
2076 use super::*;
2077
2078 #[test]
2079 fn test_new() {
2080 let path = AssetPath::new("textures/player.png");
2081 assert_eq!(path.as_str(), "textures/player.png");
2082 }
2083
2084 #[test]
2085 fn test_from_string() {
2086 let path = AssetPath::from_string("textures/player.png".to_string());
2087 assert_eq!(path.as_str(), "textures/player.png");
2088 }
2089
2090 #[test]
2091 fn test_is_empty() {
2092 let empty = AssetPath::new("");
2093 assert!(empty.is_empty());
2094 assert_eq!(empty.len(), 0);
2095
2096 let non_empty = AssetPath::new("file.txt");
2097 assert!(!non_empty.is_empty());
2098 }
2099
2100 #[test]
2101 fn test_file_name() {
2102 assert_eq!(
2103 AssetPath::new("textures/player.png").file_name(),
2104 Some("player.png")
2105 );
2106 assert_eq!(AssetPath::new("player.png").file_name(), Some("player.png"));
2107 assert_eq!(
2108 AssetPath::new("a/b/c/file.txt").file_name(),
2109 Some("file.txt")
2110 );
2111 assert_eq!(AssetPath::new("textures/").file_name(), None);
2112 assert_eq!(AssetPath::new("").file_name(), None);
2113 }
2114
2115 #[test]
2116 fn test_extension() {
2117 assert_eq!(AssetPath::new("player.png").extension(), Some("png"));
2118 assert_eq!(
2119 AssetPath::new("textures/player.png").extension(),
2120 Some("png")
2121 );
2122 assert_eq!(AssetPath::new("archive.tar.gz").extension(), Some("gz"));
2123 assert_eq!(AssetPath::new("Makefile").extension(), None);
2124 assert_eq!(AssetPath::new(".gitignore").extension(), None);
2125 assert_eq!(AssetPath::new("").extension(), None);
2126 }
2127
2128 #[test]
2129 fn test_directory() {
2130 assert_eq!(
2131 AssetPath::new("textures/player.png").directory(),
2132 Some("textures")
2133 );
2134 assert_eq!(AssetPath::new("a/b/c/file.txt").directory(), Some("a/b/c"));
2135 assert_eq!(AssetPath::new("file.txt").directory(), None);
2136 assert_eq!(AssetPath::new("").directory(), None);
2137 }
2138
2139 #[test]
2140 fn test_stem() {
2141 assert_eq!(AssetPath::new("player.png").stem(), Some("player"));
2142 assert_eq!(AssetPath::new("textures/player.png").stem(), Some("player"));
2143 assert_eq!(AssetPath::new("archive.tar.gz").stem(), Some("archive.tar"));
2144 assert_eq!(AssetPath::new(".gitignore").stem(), Some(".gitignore"));
2145 assert_eq!(AssetPath::new("Makefile").stem(), Some("Makefile"));
2146 }
2147
2148 #[test]
2149 fn test_into_owned() {
2150 let borrowed = AssetPath::new("textures/player.png");
2151 let owned = borrowed.into_owned();
2152 assert_eq!(owned.as_str(), "textures/player.png");
2153 }
2154
2155 #[test]
2156 fn test_from_path() {
2157 let path = AssetPath::from_path(Path::new("textures/player.png"));
2158 assert_eq!(path.as_str(), "textures/player.png");
2159 }
2160
2161 #[test]
2162 fn test_join() {
2163 let base = AssetPath::new("textures");
2164 let full = base.join("player.png");
2165 assert_eq!(full.as_str(), "textures/player.png");
2166
2167 let base = AssetPath::new("textures/");
2169 let full = base.join("player.png");
2170 assert_eq!(full.as_str(), "textures/player.png");
2171
2172 let base = AssetPath::new("textures");
2174 let full = base.join("/player.png");
2175 assert_eq!(full.as_str(), "textures/player.png");
2176
2177 let base = AssetPath::new("");
2179 let full = base.join("player.png");
2180 assert_eq!(full.as_str(), "player.png");
2181
2182 let base = AssetPath::new("textures");
2184 let full = base.join("");
2185 assert_eq!(full.as_str(), "textures");
2186 }
2187
2188 #[test]
2189 fn test_with_extension() {
2190 let path = AssetPath::new("textures/player.png");
2191 let new_path = path.with_extension("jpg");
2192 assert_eq!(new_path.as_str(), "textures/player.jpg");
2193
2194 let path = AssetPath::new("Makefile");
2196 let new_path = path.with_extension("bak");
2197 assert_eq!(new_path.as_str(), "Makefile.bak");
2198
2199 let path = AssetPath::new("player.png");
2201 let new_path = path.with_extension("jpg");
2202 assert_eq!(new_path.as_str(), "player.jpg");
2203 }
2204
2205 #[test]
2206 fn test_equality() {
2207 let p1 = AssetPath::new("textures/player.png");
2208 let p2 = AssetPath::new("textures/player.png");
2209 let p3 = AssetPath::new("textures/enemy.png");
2210
2211 assert_eq!(p1, p2);
2212 assert_ne!(p1, p3);
2213 }
2214
2215 #[test]
2216 fn test_equality_with_str() {
2217 let path = AssetPath::new("textures/player.png");
2218 assert!(path == "textures/player.png");
2219 let str_ref: &str = "textures/player.png";
2221 assert!(path == str_ref);
2222 }
2223
2224 #[test]
2225 fn test_hash() {
2226 use std::collections::HashSet;
2227
2228 let mut set = HashSet::new();
2229 set.insert(AssetPath::new("a.txt").into_owned());
2230 set.insert(AssetPath::new("b.txt").into_owned());
2231
2232 assert_eq!(set.len(), 2);
2233
2234 set.insert(AssetPath::new("a.txt").into_owned());
2235 assert_eq!(set.len(), 2);
2236 }
2237
2238 #[test]
2239 fn test_debug() {
2240 let path = AssetPath::new("textures/player.png");
2241 let debug_str = format!("{:?}", path);
2242 assert!(debug_str.contains("AssetPath"));
2243 assert!(debug_str.contains("textures/player.png"));
2244 }
2245
2246 #[test]
2247 fn test_display() {
2248 let path = AssetPath::new("textures/player.png");
2249 assert_eq!(format!("{}", path), "textures/player.png");
2250 }
2251
2252 #[test]
2253 fn test_as_ref() {
2254 let path = AssetPath::new("textures/player.png");
2255 let s: &str = path.as_ref();
2256 assert_eq!(s, "textures/player.png");
2257 }
2258
2259 #[test]
2260 fn test_from_str() {
2261 let path: AssetPath = "textures/player.png".into();
2262 assert_eq!(path.as_str(), "textures/player.png");
2263 }
2264
2265 #[test]
2266 fn test_from_string_into() {
2267 let path: AssetPath<'static> = "textures/player.png".to_string().into();
2268 assert_eq!(path.as_str(), "textures/player.png");
2269 }
2270 }
2271
2272 mod weak_asset_handle {
2277 use super::*;
2278
2279 #[test]
2280 fn test_from_handle() {
2281 let strong: AssetHandle<TestTexture> = AssetHandle::new(10, 5);
2282 let weak = WeakAssetHandle::from_handle(&strong);
2283
2284 assert_eq!(weak.index(), 10);
2285 assert_eq!(weak.generation(), 5);
2286 }
2287
2288 #[test]
2289 fn test_new() {
2290 let weak: WeakAssetHandle<TestTexture> = WeakAssetHandle::new(42, 7);
2291 assert_eq!(weak.index(), 42);
2292 assert_eq!(weak.generation(), 7);
2293 }
2294
2295 #[test]
2296 fn test_invalid() {
2297 let weak: WeakAssetHandle<TestTexture> = WeakAssetHandle::INVALID;
2298 assert!(!weak.is_valid());
2299 }
2300
2301 #[test]
2302 fn test_upgrade() {
2303 let strong: AssetHandle<TestTexture> = AssetHandle::new(10, 5);
2304 let weak = WeakAssetHandle::from_handle(&strong);
2305 let upgraded = weak.upgrade();
2306
2307 assert_eq!(upgraded, strong);
2308 }
2309
2310 #[test]
2311 fn test_clone_copy() {
2312 let w1: WeakAssetHandle<TestTexture> = WeakAssetHandle::new(1, 1);
2313 let w2 = w1; let w3 = w1.clone();
2315
2316 assert_eq!(w1, w2);
2317 assert_eq!(w1, w3);
2318 }
2319
2320 #[test]
2321 fn test_default() {
2322 let weak: WeakAssetHandle<TestTexture> = Default::default();
2323 assert!(!weak.is_valid());
2324 }
2325
2326 #[test]
2327 fn test_equality() {
2328 let w1: WeakAssetHandle<TestTexture> = WeakAssetHandle::new(1, 1);
2329 let w2: WeakAssetHandle<TestTexture> = WeakAssetHandle::new(1, 1);
2330 let w3: WeakAssetHandle<TestTexture> = WeakAssetHandle::new(1, 2);
2331
2332 assert_eq!(w1, w2);
2333 assert_ne!(w1, w3);
2334 }
2335
2336 #[test]
2337 fn test_hash() {
2338 use std::collections::HashSet;
2339
2340 let mut set = HashSet::new();
2341 set.insert(WeakAssetHandle::<TestTexture>::new(1, 1));
2342 set.insert(WeakAssetHandle::<TestTexture>::new(2, 1));
2343
2344 assert_eq!(set.len(), 2);
2345 }
2346
2347 #[test]
2348 fn test_debug() {
2349 let weak: WeakAssetHandle<TestTexture> = WeakAssetHandle::new(42, 7);
2350 let debug_str = format!("{:?}", weak);
2351 assert!(debug_str.contains("WeakAssetHandle"));
2352 assert!(debug_str.contains("TestTexture"));
2353 assert!(debug_str.contains("42"));
2354 }
2355
2356 #[test]
2357 fn test_from_ref() {
2358 let strong: AssetHandle<TestTexture> = AssetHandle::new(10, 5);
2359 let weak: WeakAssetHandle<TestTexture> = (&strong).into();
2360
2361 assert_eq!(weak.index(), 10);
2362 assert_eq!(weak.generation(), 5);
2363 }
2364
2365 #[test]
2366 fn test_size_and_align() {
2367 assert_eq!(std::mem::size_of::<WeakAssetHandle<TestTexture>>(), 8);
2369 assert_eq!(std::mem::align_of::<WeakAssetHandle<TestTexture>>(), 4);
2370 }
2371
2372 #[test]
2373 fn test_is_send() {
2374 fn requires_send<T: Send>() {}
2375 requires_send::<WeakAssetHandle<TestTexture>>();
2376 }
2377
2378 #[test]
2379 fn test_is_sync() {
2380 fn requires_sync<T: Sync>() {}
2381 requires_sync::<WeakAssetHandle<TestTexture>>();
2382 }
2383 }
2384
2385 mod integration {
2390 use super::*;
2391
2392 #[test]
2393 fn test_handle_lifecycle() {
2394 let handle: AssetHandle<TestTexture> = AssetHandle::new(0, 1);
2396
2397 let mut state = HandleLoadState::new(handle, AssetState::Loading { progress: 0.0 });
2399 assert!(state.is_loading());
2400
2401 state.set_state(AssetState::Loading { progress: 0.5 });
2403 assert_eq!(state.progress(), Some(0.5));
2404
2405 state.set_state(AssetState::Loaded);
2407 assert!(state.is_ready());
2408 }
2409
2410 #[test]
2411 fn test_mixed_handle_collection() {
2412 let tex_handle: AssetHandle<TestTexture> = AssetHandle::new(1, 1);
2414 let audio_handle: AssetHandle<TestAudio> = AssetHandle::new(2, 1);
2415
2416 let handles: Vec<UntypedAssetHandle> =
2417 vec![tex_handle.untyped(), audio_handle.untyped()];
2418
2419 let textures: Vec<_> = handles
2421 .iter()
2422 .filter_map(|h| h.typed::<TestTexture>())
2423 .collect();
2424 assert_eq!(textures.len(), 1);
2425 assert_eq!(textures[0], tex_handle);
2426 }
2427
2428 #[test]
2429 fn test_path_to_handle_workflow() {
2430 let path = AssetPath::new("textures/player.png");
2432 assert_eq!(path.extension(), Some("png"));
2433
2434 let handle: AssetHandle<TestTexture> = AssetHandle::new(0, 1);
2436
2437 assert_eq!(AssetHandle::<TestTexture>::asset_type(), AssetType::Texture);
2439 }
2440
2441 #[test]
2442 fn test_weak_handle_usage() {
2443 let strong: AssetHandle<TestTexture> = AssetHandle::new(1, 1);
2445
2446 let weak = WeakAssetHandle::from_handle(&strong);
2448
2449 let upgraded = weak.upgrade();
2451 assert_eq!(upgraded, strong);
2452 }
2453 }
2454
2455 mod asset_handle_allocator {
2460 use super::*;
2461
2462 #[test]
2463 fn test_new() {
2464 let allocator: AssetHandleAllocator<TestTexture> = AssetHandleAllocator::new();
2465 assert_eq!(allocator.len(), 0);
2466 assert!(allocator.is_empty());
2467 assert_eq!(allocator.capacity(), 0);
2468 }
2469
2470 #[test]
2471 fn test_with_capacity() {
2472 let allocator: AssetHandleAllocator<TestTexture> =
2473 AssetHandleAllocator::with_capacity(100);
2474 assert!(allocator.is_empty());
2475 }
2476
2477 #[test]
2478 fn test_allocate() {
2479 let mut allocator: AssetHandleAllocator<TestTexture> = AssetHandleAllocator::new();
2480
2481 let h1 = allocator.allocate();
2482 assert!(h1.is_valid());
2483 assert_eq!(h1.index(), 0);
2484 assert_eq!(h1.generation(), 1);
2485
2486 let h2 = allocator.allocate();
2487 assert!(h2.is_valid());
2488 assert_eq!(h2.index(), 1);
2489 assert_eq!(h2.generation(), 1);
2490
2491 assert_eq!(allocator.len(), 2);
2492 }
2493
2494 #[test]
2495 fn test_allocate_unique() {
2496 let mut allocator: AssetHandleAllocator<TestTexture> = AssetHandleAllocator::new();
2497
2498 let handles: Vec<_> = (0..100).map(|_| allocator.allocate()).collect();
2499
2500 for i in 0..handles.len() {
2502 for j in (i + 1)..handles.len() {
2503 assert_ne!(handles[i], handles[j]);
2504 }
2505 }
2506 }
2507
2508 #[test]
2509 fn test_deallocate() {
2510 let mut allocator: AssetHandleAllocator<TestTexture> = AssetHandleAllocator::new();
2511
2512 let handle = allocator.allocate();
2513 assert!(allocator.is_alive(handle));
2514 assert_eq!(allocator.len(), 1);
2515
2516 assert!(allocator.deallocate(handle));
2517 assert!(!allocator.is_alive(handle));
2518 assert_eq!(allocator.len(), 0);
2519 }
2520
2521 #[test]
2522 fn test_deallocate_invalid() {
2523 let mut allocator: AssetHandleAllocator<TestTexture> = AssetHandleAllocator::new();
2524
2525 assert!(!allocator.deallocate(AssetHandle::INVALID));
2527
2528 let fake_handle = AssetHandle::new(100, 1);
2530 assert!(!allocator.deallocate(fake_handle));
2531 }
2532
2533 #[test]
2534 fn test_deallocate_twice() {
2535 let mut allocator: AssetHandleAllocator<TestTexture> = AssetHandleAllocator::new();
2536
2537 let handle = allocator.allocate();
2538 assert!(allocator.deallocate(handle));
2539 assert!(!allocator.deallocate(handle)); }
2541
2542 #[test]
2543 fn test_is_alive() {
2544 let mut allocator: AssetHandleAllocator<TestTexture> = AssetHandleAllocator::new();
2545
2546 let handle = allocator.allocate();
2547 assert!(allocator.is_alive(handle));
2548
2549 allocator.deallocate(handle);
2550 assert!(!allocator.is_alive(handle));
2551
2552 assert!(!allocator.is_alive(AssetHandle::INVALID));
2554 }
2555
2556 #[test]
2557 fn test_slot_reuse() {
2558 let mut allocator: AssetHandleAllocator<TestTexture> = AssetHandleAllocator::new();
2559
2560 let h1 = allocator.allocate();
2561 let original_index = h1.index();
2562 let original_gen = h1.generation();
2563
2564 allocator.deallocate(h1);
2565
2566 let h2 = allocator.allocate();
2567
2568 assert_eq!(h2.index(), original_index);
2570 assert_eq!(h2.generation(), original_gen + 1);
2571 assert_ne!(h1, h2);
2572 }
2573
2574 #[test]
2575 fn test_slot_reuse_multiple() {
2576 let mut allocator: AssetHandleAllocator<TestTexture> = AssetHandleAllocator::new();
2577
2578 let handles: Vec<_> = (0..10).map(|_| allocator.allocate()).collect();
2580
2581 for h in &handles[..5] {
2583 allocator.deallocate(*h);
2584 }
2585
2586 assert_eq!(allocator.len(), 5);
2587 assert_eq!(allocator.capacity(), 10);
2588
2589 for _ in 0..5 {
2591 let h = allocator.allocate();
2592 assert!(h.index() < 5); assert_eq!(h.generation(), 2); }
2595
2596 assert_eq!(allocator.len(), 10);
2597 assert_eq!(allocator.capacity(), 10); }
2599
2600 #[test]
2601 fn test_len_and_capacity() {
2602 let mut allocator: AssetHandleAllocator<TestTexture> = AssetHandleAllocator::new();
2603
2604 assert_eq!(allocator.len(), 0);
2605 assert_eq!(allocator.capacity(), 0);
2606
2607 let h1 = allocator.allocate();
2608 let h2 = allocator.allocate();
2609 let h3 = allocator.allocate();
2610
2611 assert_eq!(allocator.len(), 3);
2612 assert_eq!(allocator.capacity(), 3);
2613
2614 allocator.deallocate(h2);
2615
2616 assert_eq!(allocator.len(), 2);
2617 assert_eq!(allocator.capacity(), 3); }
2619
2620 #[test]
2621 fn test_clear() {
2622 let mut allocator: AssetHandleAllocator<TestTexture> = AssetHandleAllocator::new();
2623
2624 let h1 = allocator.allocate();
2625 let h2 = allocator.allocate();
2626 let h3 = allocator.allocate();
2627
2628 assert_eq!(allocator.len(), 3);
2629
2630 allocator.clear();
2631
2632 assert_eq!(allocator.len(), 0);
2633 assert_eq!(allocator.capacity(), 3); assert!(!allocator.is_alive(h1));
2637 assert!(!allocator.is_alive(h2));
2638 assert!(!allocator.is_alive(h3));
2639
2640 let h4 = allocator.allocate();
2642 assert_eq!(h4.generation(), 2);
2643 }
2644
2645 #[test]
2646 fn test_generation_at() {
2647 let mut allocator: AssetHandleAllocator<TestTexture> = AssetHandleAllocator::new();
2648
2649 assert_eq!(allocator.generation_at(0), None);
2650
2651 let handle = allocator.allocate();
2652 assert_eq!(allocator.generation_at(0), Some(1));
2653
2654 allocator.deallocate(handle);
2655 assert_eq!(allocator.generation_at(0), Some(2));
2656 }
2657
2658 #[test]
2659 fn test_default() {
2660 let allocator: AssetHandleAllocator<TestTexture> = Default::default();
2661 assert!(allocator.is_empty());
2662 }
2663
2664 #[test]
2665 fn test_debug() {
2666 let mut allocator: AssetHandleAllocator<TestTexture> = AssetHandleAllocator::new();
2667 allocator.allocate();
2668 allocator.allocate();
2669
2670 let debug_str = format!("{:?}", allocator);
2671 assert!(debug_str.contains("AssetHandleAllocator"));
2672 assert!(debug_str.contains("TestTexture"));
2673 assert!(debug_str.contains("len"));
2674 assert!(debug_str.contains("2"));
2675 }
2676
2677 #[test]
2678 fn test_stress_allocate_deallocate() {
2679 let mut allocator: AssetHandleAllocator<TestTexture> = AssetHandleAllocator::new();
2680
2681 let handles: Vec<_> = (0..10000).map(|_| allocator.allocate()).collect();
2683 assert_eq!(allocator.len(), 10000);
2684
2685 for h in handles {
2687 assert!(allocator.deallocate(h));
2688 }
2689 assert_eq!(allocator.len(), 0);
2690 assert_eq!(allocator.capacity(), 10000);
2691 }
2692
2693 #[test]
2694 fn test_stress_churn() {
2695 let mut allocator: AssetHandleAllocator<TestTexture> = AssetHandleAllocator::new();
2696
2697 for _ in 0..100 {
2699 let handles: Vec<_> = (0..100).map(|_| allocator.allocate()).collect();
2700 for h in &handles[..50] {
2701 allocator.deallocate(*h);
2702 }
2703 }
2705
2706 assert_eq!(allocator.len(), 5000);
2708 }
2709
2710 #[test]
2711 fn test_generation_wrap() {
2712 let mut allocator: AssetHandleAllocator<TestTexture> = AssetHandleAllocator::new();
2713
2714 let handle = allocator.allocate();
2717
2718 allocator.deallocate(handle);
2720 assert!(!allocator.is_alive(handle));
2721
2722 for _ in 0..10 {
2724 let new_handle = allocator.allocate();
2725 allocator.deallocate(new_handle);
2726 }
2727
2728 assert!(!allocator.is_alive(handle));
2730
2731 let gen = allocator.generation_at(0).unwrap();
2733 assert!(gen > handle.generation());
2734 }
2735
2736 #[test]
2737 fn test_shrink_to_fit() {
2738 let mut allocator: AssetHandleAllocator<TestTexture> = AssetHandleAllocator::new();
2739
2740 let handles: Vec<_> = (0..100).map(|_| allocator.allocate()).collect();
2742 for h in handles {
2743 allocator.deallocate(h);
2744 }
2745
2746 allocator.shrink_to_fit();
2748 }
2750
2751 #[test]
2752 fn test_is_send() {
2753 fn requires_send<T: Send>() {}
2754 requires_send::<AssetHandleAllocator<TestTexture>>();
2755 }
2756
2757 }
2760}