1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//! Error types for the asset system.
use std::any::TypeId;
use std::fmt;
use std::path::PathBuf;
/// Errors that can occur during asset operations.
#[derive(Debug)]
pub enum AssetError {
/// The requested asset was not found.
NotFound {
/// The path or identifier of the asset.
path: String,
},
/// Failed to read asset data from the source.
IoError {
/// The path that failed to load.
path: PathBuf,
/// The underlying IO error.
source: std::io::Error,
},
/// No loader registered for this asset type.
NoLoader {
/// The type ID of the asset.
type_id: TypeId,
/// Human-readable type name if available.
type_name: Option<&'static str>,
},
/// No loader found for the given file extension.
NoLoaderForExtension {
/// The file extension.
extension: String,
},
/// The loader failed to parse/decode the asset.
LoaderError {
/// The path being loaded.
path: String,
/// Description of the error.
message: String,
},
/// The asset handle is invalid (use-after-free or wrong type).
InvalidHandle {
/// Description of why the handle is invalid.
reason: String,
},
/// Type mismatch when accessing an asset.
TypeMismatch {
/// Expected type name.
expected: &'static str,
/// Actual type ID.
actual: TypeId,
},
/// The asset is not ready yet (still loading).
NotReady {
/// The path of the asset.
path: String,
},
/// Lock was poisoned (RwLock/Mutex).
LockPoisoned {
/// Description of the poisoned lock.
message: String,
},
/// Generic error with a message.
Other {
/// Error message.
message: String,
},
}
impl fmt::Display for AssetError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AssetError::NotFound { path } => {
write!(f, "Asset not found: {}", path)
}
AssetError::IoError { path, source } => {
write!(f, "IO error loading '{}': {}", path.display(), source)
}
AssetError::NoLoader { type_name, .. } => {
if let Some(name) = type_name {
write!(f, "No loader registered for asset type: {}", name)
} else {
write!(f, "No loader registered for asset type")
}
}
AssetError::NoLoaderForExtension { extension } => {
write!(f, "No loader registered for extension: .{}", extension)
}
AssetError::LoaderError { path, message } => {
write!(f, "Failed to load '{}': {}", path, message)
}
AssetError::InvalidHandle { reason } => {
write!(f, "Invalid asset handle: {}", reason)
}
AssetError::TypeMismatch { expected, .. } => {
write!(f, "Type mismatch: expected {}", expected)
}
AssetError::NotReady { path } => {
write!(f, "Asset not ready: {}", path)
}
AssetError::LockPoisoned { message } => {
write!(
f,
"Lock poisoned (likely due to panic in another thread): {}",
message
)
}
AssetError::Other { message } => {
write!(f, "Asset error: {}", message)
}
}
}
}
impl std::error::Error for AssetError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
AssetError::IoError { source, .. } => Some(source),
_ => None,
}
}
}
impl From<std::io::Error> for AssetError {
fn from(err: std::io::Error) -> Self {
AssetError::IoError {
path: PathBuf::new(),
source: err,
}
}
}
impl<T> From<std::sync::PoisonError<T>> for AssetError {
fn from(err: std::sync::PoisonError<T>) -> Self {
AssetError::LockPoisoned {
message: err.to_string(),
}
}
}
/// Result type alias for asset operations.
pub type AssetResult<T> = Result<T, AssetError>;