Skip to main content

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>;