crush_core/error.rs
1//! Error types for Crush compression library
2//!
3//! This module defines all error types using `thiserror` for ergonomic error handling.
4//! All public APIs return `Result<T, CrushError>` to enforce proper error propagation.
5
6use thiserror::Error;
7
8/// Main error type for Crush library operations
9#[derive(Error, Debug)]
10pub enum CrushError {
11 /// Plugin-related errors (registration, discovery, execution)
12 #[error("Plugin error: {0}")]
13 Plugin(#[from] PluginError),
14
15 /// Timeout-related errors (plugin operations exceeding limits)
16 #[error("Timeout error: {0}")]
17 Timeout(#[from] TimeoutError),
18
19 /// Validation errors (invalid input, corrupted data, malformed headers)
20 #[error("Validation error: {0}")]
21 Validation(#[from] ValidationError),
22
23 /// I/O errors (file operations, network, etc.)
24 #[error("I/O error: {0}")]
25 Io(#[from] std::io::Error),
26
27 /// Operation was cancelled by user (Ctrl+C) or programmatically
28 #[error("Operation cancelled")]
29 Cancelled,
30
31 /// Configuration validation failed (e.g. `block_size` out of range)
32 #[error("Invalid configuration: {0}")]
33 InvalidConfig(String),
34
35 /// The compressed file was produced by a different engine version.
36 /// Decompression is refused; the user must use the named producer version.
37 #[error("Version mismatch: file was produced by engine {file_version}, current engine is {current_version}")]
38 VersionMismatch {
39 /// Version string of the engine that produced the file.
40 file_version: String,
41 /// Version string of the currently running engine.
42 current_version: String,
43 },
44
45 /// File header magic bytes do not match the expected `CRSH` signature.
46 #[error("Invalid format: {0}")]
47 InvalidFormat(String),
48
49 /// A block's CRC32 checksum does not match the stored value.
50 #[error(
51 "Checksum mismatch at block {block_index}: expected {expected:#010x}, got {actual:#010x}"
52 )]
53 ChecksumMismatch {
54 /// Zero-based index of the corrupt block.
55 block_index: u64,
56 /// CRC32 value stored in the block header.
57 expected: u32,
58 /// CRC32 value computed from the decompressed payload.
59 actual: u32,
60 },
61
62 /// Decompressed output would exceed the caller's configured expansion limit.
63 #[error(
64 "Expansion limit exceeded at block {block_index}: decompressed size would exceed limit"
65 )]
66 ExpansionLimitExceeded {
67 /// Zero-based index of the block that would exceed the limit.
68 block_index: u64,
69 },
70
71 /// The block index is missing, corrupted, or truncated.
72 #[error("Block index corrupted or truncated: {0}")]
73 IndexCorrupted(String),
74}
75
76impl CrushError {
77 /// Returns `true` if this error represents a user-initiated or
78 /// programmatic cancellation (distinct from a compression failure).
79 #[must_use]
80 pub fn is_cancelled(&self) -> bool {
81 matches!(self, Self::Cancelled)
82 }
83}
84
85/// Plugin-specific errors
86#[derive(Error, Debug)]
87pub enum PluginError {
88 /// Plugin not found for the specified identifier
89 #[error("Plugin not found: {0}")]
90 NotFound(String),
91
92 /// Plugin with duplicate magic number or name
93 #[error("Duplicate plugin magic number: {0:?}")]
94 DuplicateMagic([u8; 4]),
95
96 /// Plugin metadata is invalid
97 #[error("Invalid plugin metadata: {0}")]
98 InvalidMetadata(String),
99
100 /// Plugin operation failed
101 #[error("Plugin operation failed: {0}")]
102 OperationFailed(String),
103
104 /// Plugin was cancelled due to timeout or user request
105 #[error("Plugin operation was cancelled")]
106 Cancelled,
107}
108
109/// Timeout-related errors
110#[derive(Error, Debug)]
111pub enum TimeoutError {
112 /// Operation exceeded configured timeout
113 #[error("Operation timed out after {0:?}")]
114 Timeout(std::time::Duration),
115
116 /// Plugin thread panicked during execution
117 #[error("Plugin panicked during execution")]
118 PluginPanic,
119}
120
121/// Validation errors
122#[derive(Error, Debug)]
123pub enum ValidationError {
124 /// Invalid magic number in file header
125 #[error("Invalid magic number: expected Crush format, got {0:?}")]
126 InvalidMagic([u8; 4]),
127
128 /// CRC32 checksum mismatch
129 #[error("CRC32 mismatch: expected {expected:08x}, got {actual:08x}")]
130 CrcMismatch { expected: u32, actual: u32 },
131
132 /// Invalid header format
133 #[error("Invalid header format: {0}")]
134 InvalidHeader(String),
135
136 /// Corrupted compressed data
137 #[error("Corrupted data: {0}")]
138 CorruptedData(String),
139
140 /// Invalid plugin scoring weights
141 #[error("Invalid scoring weights: {0}")]
142 InvalidWeights(String),
143
144 /// Invalid file metadata format
145 #[error("Invalid metadata format: {0}")]
146 InvalidMetadata(String),
147}
148
149/// Type alias for Results using `CrushError`
150pub type Result<T> = std::result::Result<T, CrushError>;