1use std::fmt;
4
5pub type Result<T> = std::result::Result<T, Error>;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10#[non_exhaustive]
11pub enum Error {
12 InvalidDimensions {
14 width: u32,
16 height: u32,
18 },
19 BufferSizeMismatch {
21 expected: usize,
23 actual: usize,
25 },
26 InvalidQuality(u8),
28 InvalidQuantTableIndex(usize),
30 InvalidComponentIndex(usize),
32 InvalidHuffmanTableIndex(usize),
34 InvalidSamplingFactor {
36 h: u8,
38 v: u8,
40 },
41 InvalidScanSpec {
43 reason: &'static str,
45 },
46 InvalidHuffmanTable,
48 HuffmanCodeLengthOverflow,
50 UnsupportedColorSpace,
52 UnsupportedFeature(&'static str),
54 InternalError(&'static str),
56 IoError(String),
58 AllocationFailed,
60 Cancelled,
62 TimedOut,
64 DimensionLimitExceeded {
66 width: u32,
68 height: u32,
70 max_width: u32,
72 max_height: u32,
74 },
75 AllocationLimitExceeded {
77 estimated: usize,
79 limit: usize,
81 },
82 PixelCountExceeded {
84 pixel_count: u64,
86 limit: u64,
88 },
89 IccProfileTooLarge {
91 size: usize,
93 limit: usize,
95 },
96 InvalidStride {
98 stride: usize,
100 minimum: usize,
102 },
103}
104
105impl fmt::Display for Error {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 match self {
108 Error::InvalidDimensions { width, height } => {
109 write!(f, "Invalid image dimensions: {}x{}", width, height)
110 }
111 Error::BufferSizeMismatch { expected, actual } => {
112 write!(
113 f,
114 "Buffer size mismatch: expected {}, got {}",
115 expected, actual
116 )
117 }
118 Error::InvalidQuality(q) => {
119 write!(f, "Invalid quality value: {} (must be 1-100)", q)
120 }
121 Error::InvalidQuantTableIndex(idx) => {
122 write!(f, "Invalid quantization table index: {}", idx)
123 }
124 Error::InvalidComponentIndex(idx) => {
125 write!(f, "Invalid component index: {}", idx)
126 }
127 Error::InvalidHuffmanTableIndex(idx) => {
128 write!(f, "Invalid Huffman table index: {}", idx)
129 }
130 Error::InvalidSamplingFactor { h, v } => {
131 write!(f, "Invalid sampling factor: {}x{}", h, v)
132 }
133 Error::InvalidScanSpec { reason } => {
134 write!(f, "Invalid scan specification: {}", reason)
135 }
136 Error::InvalidHuffmanTable => {
137 write!(f, "Invalid Huffman table structure")
138 }
139 Error::HuffmanCodeLengthOverflow => {
140 write!(f, "Huffman code length overflow (exceeds 16 bits)")
141 }
142 Error::UnsupportedColorSpace => {
143 write!(f, "Unsupported color space")
144 }
145 Error::UnsupportedFeature(feature) => {
146 write!(f, "Unsupported feature: {}", feature)
147 }
148 Error::InternalError(msg) => {
149 write!(f, "Internal encoder error: {}", msg)
150 }
151 Error::IoError(msg) => {
152 write!(f, "I/O error: {}", msg)
153 }
154 Error::AllocationFailed => {
155 write!(f, "Memory allocation failed")
156 }
157 Error::Cancelled => {
158 write!(f, "Encoding was cancelled")
159 }
160 Error::TimedOut => {
161 write!(f, "Encoding timed out")
162 }
163 Error::DimensionLimitExceeded {
164 width,
165 height,
166 max_width,
167 max_height,
168 } => {
169 write!(
170 f,
171 "Image dimensions {}x{} exceed limit {}x{}",
172 width, height, max_width, max_height
173 )
174 }
175 Error::AllocationLimitExceeded { estimated, limit } => {
176 write!(
177 f,
178 "Estimated memory {} bytes exceeds limit {} bytes",
179 estimated, limit
180 )
181 }
182 Error::PixelCountExceeded { pixel_count, limit } => {
183 write!(f, "Pixel count {} exceeds limit {}", pixel_count, limit)
184 }
185 Error::IccProfileTooLarge { size, limit } => {
186 write!(
187 f,
188 "ICC profile size {} bytes exceeds limit {} bytes",
189 size, limit
190 )
191 }
192 Error::InvalidStride { stride, minimum } => {
193 write!(
194 f,
195 "Invalid stride: {} bytes is less than minimum {} bytes",
196 stride, minimum
197 )
198 }
199 }
200 }
201}
202
203impl std::error::Error for Error {}
204
205impl From<std::io::Error> for Error {
206 fn from(e: std::io::Error) -> Self {
207 Error::IoError(e.to_string())
208 }
209}
210
211impl From<std::collections::TryReserveError> for Error {
212 fn from(_: std::collections::TryReserveError) -> Self {
213 Error::AllocationFailed
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 #[test]
222 fn test_error_display() {
223 let errors = [
225 (
226 Error::InvalidDimensions {
227 width: 0,
228 height: 100,
229 },
230 "Invalid image dimensions: 0x100",
231 ),
232 (
233 Error::BufferSizeMismatch {
234 expected: 1000,
235 actual: 500,
236 },
237 "Buffer size mismatch: expected 1000, got 500",
238 ),
239 (
240 Error::InvalidQuality(0),
241 "Invalid quality value: 0 (must be 1-100)",
242 ),
243 (
244 Error::InvalidQuantTableIndex(5),
245 "Invalid quantization table index: 5",
246 ),
247 (
248 Error::InvalidComponentIndex(4),
249 "Invalid component index: 4",
250 ),
251 (
252 Error::InvalidHuffmanTableIndex(8),
253 "Invalid Huffman table index: 8",
254 ),
255 (
256 Error::InvalidSamplingFactor { h: 5, v: 3 },
257 "Invalid sampling factor: 5x3",
258 ),
259 (
260 Error::InvalidScanSpec {
261 reason: "test reason",
262 },
263 "Invalid scan specification: test reason",
264 ),
265 (
266 Error::InvalidHuffmanTable,
267 "Invalid Huffman table structure",
268 ),
269 (
270 Error::HuffmanCodeLengthOverflow,
271 "Huffman code length overflow (exceeds 16 bits)",
272 ),
273 (Error::UnsupportedColorSpace, "Unsupported color space"),
274 (
275 Error::UnsupportedFeature("arithmetic coding"),
276 "Unsupported feature: arithmetic coding",
277 ),
278 (
279 Error::InternalError("test error"),
280 "Internal encoder error: test error",
281 ),
282 (Error::IoError("disk full".into()), "I/O error: disk full"),
283 (Error::AllocationFailed, "Memory allocation failed"),
284 (Error::Cancelled, "Encoding was cancelled"),
285 (Error::TimedOut, "Encoding timed out"),
286 (
287 Error::DimensionLimitExceeded {
288 width: 5000,
289 height: 3000,
290 max_width: 4096,
291 max_height: 4096,
292 },
293 "Image dimensions 5000x3000 exceed limit 4096x4096",
294 ),
295 (
296 Error::AllocationLimitExceeded {
297 estimated: 100_000_000,
298 limit: 50_000_000,
299 },
300 "Estimated memory 100000000 bytes exceeds limit 50000000 bytes",
301 ),
302 (
303 Error::InvalidStride {
304 stride: 100,
305 minimum: 300,
306 },
307 "Invalid stride: 100 bytes is less than minimum 300 bytes",
308 ),
309 ];
310
311 for (error, expected_msg) in errors {
312 assert_eq!(error.to_string(), expected_msg);
313 }
314 }
315
316 #[test]
317 fn test_error_is_error_trait() {
318 let error: &dyn std::error::Error = &Error::InvalidQuality(0);
319 let _ = error.to_string();
321 }
322
323 #[test]
324 fn test_from_io_error() {
325 let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
326 let error: Error = io_error.into();
327 assert!(matches!(error, Error::IoError(_)));
328 assert!(error.to_string().contains("file not found"));
329 }
330
331 #[test]
332 fn test_error_clone_and_eq() {
333 let error1 = Error::InvalidQuality(50);
334 let error2 = error1.clone();
335 assert_eq!(error1, error2);
336
337 let error3 = Error::InvalidQuality(60);
338 assert_ne!(error1, error3);
339 }
340}