1use thiserror::Error;
4
5pub type Result<T> = std::result::Result<T, BinaryError>;
7
8#[derive(Error, Debug)]
10pub enum BinaryError {
11 #[error("I/O error: {0}")]
13 Io(#[from] std::io::Error),
14
15 #[error("Invalid file format: {0}")]
17 InvalidFormat(String),
18
19 #[error("Unsupported file version: {0}")]
21 UnsupportedVersion(String),
22
23 #[error("Unsupported compression: {0}")]
25 UnsupportedCompression(String),
26
27 #[error("Decompression failed: {0}")]
29 DecompressionFailed(String),
30
31 #[error("Invalid data: {0}")]
33 InvalidData(String),
34
35 #[error("Parse error: {0}")]
37 ParseError(String),
38
39 #[error("Not enough data: expected {expected}, got {actual}")]
41 NotEnoughData { expected: usize, actual: usize },
42
43 #[error("Invalid signature: expected {expected}, got {actual}")]
45 InvalidSignature { expected: String, actual: String },
46
47 #[error("Unsupported feature: {0}")]
49 Unsupported(String),
50
51 #[error("Memory allocation error: {0}")]
53 MemoryError(String),
54
55 #[error("Operation timed out: {0}")]
57 Timeout(String),
58
59 #[error("Resource limit exceeded: {0}")]
61 ResourceLimitExceeded(String),
62
63 #[error("Corrupted data detected: {0}")]
65 CorruptedData(String),
66
67 #[error("Version compatibility error: {0}")]
69 VersionCompatibility(String),
70
71 #[error("Error: {0}")]
73 Generic(String),
74}
75
76impl BinaryError {
77 pub fn invalid_format<S: Into<String>>(msg: S) -> Self {
79 Self::InvalidFormat(msg.into())
80 }
81
82 pub fn format<S: Into<String>>(msg: S) -> Self {
84 Self::Generic(msg.into())
85 }
86
87 pub fn unsupported_version<S: Into<String>>(version: S) -> Self {
89 Self::UnsupportedVersion(version.into())
90 }
91
92 pub fn unsupported_compression<S: Into<String>>(compression: S) -> Self {
94 Self::UnsupportedCompression(compression.into())
95 }
96
97 pub fn decompression_failed<S: Into<String>>(msg: S) -> Self {
99 Self::DecompressionFailed(msg.into())
100 }
101
102 pub fn invalid_data<S: Into<String>>(msg: S) -> Self {
104 Self::InvalidData(msg.into())
105 }
106
107 pub fn parse_error<S: Into<String>>(msg: S) -> Self {
109 Self::ParseError(msg.into())
110 }
111
112 pub fn not_enough_data(expected: usize, actual: usize) -> Self {
114 Self::NotEnoughData { expected, actual }
115 }
116
117 pub fn invalid_signature<S: Into<String>>(expected: S, actual: S) -> Self {
119 Self::InvalidSignature {
120 expected: expected.into(),
121 actual: actual.into(),
122 }
123 }
124
125 pub fn unsupported<S: Into<String>>(feature: S) -> Self {
127 Self::Unsupported(feature.into())
128 }
129
130 pub fn generic<S: Into<String>>(msg: S) -> Self {
132 Self::Generic(msg.into())
133 }
134
135 pub fn io_error<S: Into<String>>(msg: S) -> Self {
137 Self::Generic(msg.into())
138 }
139}
140
141impl From<lz4_flex::block::DecompressError> for BinaryError {
143 fn from(err: lz4_flex::block::DecompressError) -> Self {
144 Self::decompression_failed(format!("LZ4 decompression failed: {}", err))
145 }
146}
147
148impl From<lz4_flex::frame::Error> for BinaryError {
149 fn from(err: lz4_flex::frame::Error) -> Self {
150 Self::decompression_failed(format!("LZ4 frame error: {}", err))
151 }
152}
153
154impl From<std::string::FromUtf8Error> for BinaryError {
155 fn from(err: std::string::FromUtf8Error) -> Self {
156 Self::invalid_data(format!("Invalid UTF-8 string: {}", err))
157 }
158}
159
160impl From<std::str::Utf8Error> for BinaryError {
161 fn from(err: std::str::Utf8Error) -> Self {
162 Self::invalid_data(format!("Invalid UTF-8 string: {}", err))
163 }
164}
165
166#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
168pub enum ErrorSeverity {
169 Low,
171 Medium,
173 High,
175 Critical,
177}
178
179impl BinaryError {
180 pub fn memory_error(msg: impl Into<String>) -> Self {
182 BinaryError::MemoryError(msg.into())
183 }
184
185 pub fn timeout(msg: impl Into<String>) -> Self {
187 BinaryError::Timeout(msg.into())
188 }
189
190 pub fn corrupted_data(msg: impl Into<String>) -> Self {
192 BinaryError::CorruptedData(msg.into())
193 }
194
195 pub fn version_compatibility(msg: impl Into<String>) -> Self {
197 BinaryError::VersionCompatibility(msg.into())
198 }
199
200 pub fn is_recoverable(&self) -> bool {
202 match self {
203 BinaryError::Io(_) => false,
204 BinaryError::InvalidFormat(_) => false,
205 BinaryError::UnsupportedVersion(_) => false,
206 BinaryError::UnsupportedCompression(_) => true, BinaryError::DecompressionFailed(_) => true, BinaryError::InvalidData(_) => true, BinaryError::ParseError(_) => true, BinaryError::NotEnoughData { .. } => false,
211 BinaryError::InvalidSignature { .. } => false,
212 BinaryError::Unsupported(_) => true, BinaryError::MemoryError(_) => false,
214 BinaryError::Timeout(_) => true, BinaryError::ResourceLimitExceeded(_) => true, BinaryError::CorruptedData(_) => true, BinaryError::VersionCompatibility(_) => true, BinaryError::Generic(_) => true, }
220 }
221
222 pub fn severity(&self) -> ErrorSeverity {
224 match self {
225 BinaryError::Io(_) => ErrorSeverity::Critical,
226 BinaryError::InvalidFormat(_) => ErrorSeverity::Critical,
227 BinaryError::UnsupportedVersion(_) => ErrorSeverity::High,
228 BinaryError::UnsupportedCompression(_) => ErrorSeverity::Medium,
229 BinaryError::DecompressionFailed(_) => ErrorSeverity::Medium,
230 BinaryError::InvalidData(_) => ErrorSeverity::Medium,
231 BinaryError::ParseError(_) => ErrorSeverity::Medium,
232 BinaryError::NotEnoughData { .. } => ErrorSeverity::High,
233 BinaryError::InvalidSignature { .. } => ErrorSeverity::High,
234 BinaryError::Unsupported(_) => ErrorSeverity::Low,
235 BinaryError::MemoryError(_) => ErrorSeverity::Critical,
236 BinaryError::Timeout(_) => ErrorSeverity::Medium,
237 BinaryError::ResourceLimitExceeded(_) => ErrorSeverity::Medium,
238 BinaryError::CorruptedData(_) => ErrorSeverity::Medium,
239 BinaryError::VersionCompatibility(_) => ErrorSeverity::Low,
240 BinaryError::Generic(_) => ErrorSeverity::Medium,
241 }
242 }
243
244 pub fn recovery_suggestion(&self) -> Option<&'static str> {
246 match self {
247 BinaryError::UnsupportedCompression(_) => Some("Try different compression method"),
248 BinaryError::DecompressionFailed(_) => Some("Skip compressed section or retry"),
249 BinaryError::InvalidData(_) => Some("Skip corrupted object and continue"),
250 BinaryError::ParseError(_) => Some("Skip problematic object and continue"),
251 BinaryError::Unsupported(_) => Some("Skip unsupported feature"),
252 BinaryError::Timeout(_) => Some("Retry with longer timeout"),
253 BinaryError::ResourceLimitExceeded(_) => Some("Reduce processing limits"),
254 BinaryError::CorruptedData(_) => Some("Skip corrupted section"),
255 BinaryError::VersionCompatibility(_) => Some("Enable compatibility mode"),
256 _ => None,
257 }
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn test_error_creation() {
267 let err = BinaryError::invalid_format("test format");
268 assert!(matches!(err, BinaryError::InvalidFormat(_)));
269 assert_eq!(err.to_string(), "Invalid file format: test format");
270 }
271
272 #[test]
273 fn test_not_enough_data_error() {
274 let err = BinaryError::not_enough_data(100, 50);
275 assert!(matches!(err, BinaryError::NotEnoughData { .. }));
276 assert_eq!(err.to_string(), "Not enough data: expected 100, got 50");
277 }
278
279 #[test]
280 fn test_invalid_signature_error() {
281 let err = BinaryError::invalid_signature("UnityFS", "UnityWeb");
282 assert!(matches!(err, BinaryError::InvalidSignature { .. }));
283 assert_eq!(
284 err.to_string(),
285 "Invalid signature: expected UnityFS, got UnityWeb"
286 );
287 }
288}