1use std::io;
14use thiserror::Error;
15
16use crate::core::BitDepth;
17#[cfg(feature = "tiff-parser")]
18use crate::tiff::TiffTag;
19
20#[derive(Debug, Error)]
22pub enum RawError {
23 #[error("I/O error: {0}")]
25 Io(#[from] io::Error),
26
27 #[error(transparent)]
29 Parse(#[from] ParseError),
30
31 #[error(transparent)]
33 Format(#[from] FormatError),
34
35 #[error(transparent)]
37 Processing(#[from] ProcessingError),
38
39 #[error(transparent)]
41 Encode(#[from] EncodeError),
42
43 #[error("Unsupported: {0}")]
45 Unsupported(String),
46}
47
48#[derive(Debug, Error)]
50pub enum ParseError {
51 #[error("Invalid TIFF magic number: expected {expected}, found {found}")]
53 InvalidMagic {
54 expected: u16,
56 found: u16,
58 },
59
60 #[error("Invalid byte order marker: 0x{0:04X} (expected 'II' or 'MM')")]
62 InvalidByteOrder(u16),
63
64 #[error("Invalid IFD at offset {offset}: {reason}")]
66 InvalidIfd {
67 offset: u64,
69 reason: String,
71 },
72
73 #[cfg(feature = "tiff-parser")]
75 #[error("Required tag not found: {0}")]
76 TagNotFound(TiffTag),
77
78 #[error("Offset out of bounds: offset {offset} + size {size} exceeds file size {file_size}")]
80 OffsetOutOfBounds {
81 offset: u64,
83 size: u64,
85 file_size: u64,
87 },
88
89 #[error("Unknown TIFF data type: {0}")]
91 UnknownDataType(u16),
92
93 #[error("Invalid image dimensions: {width}x{height}")]
95 InvalidDimensions {
96 width: u32,
98 height: u32,
100 },
101
102 #[error("Circular reference detected in IFD chain at offset {0}")]
104 CircularReference(u64),
105
106 #[error("Binary parse error: {0}")]
108 BinaryParse(String),
109}
110
111#[derive(Debug, Error)]
113pub enum FormatError {
114 #[cfg(feature = "cr2-decode")]
116 #[error("CR2 error: {0}")]
117 Cr2(String),
118
119 #[cfg(feature = "nef-decode")]
121 #[error("NEF error: {0}")]
122 Nef(String),
123
124 #[cfg(feature = "cr3-decode")]
126 #[error("CR3 error: {0}")]
127 Cr3(String),
128
129 #[cfg(feature = "raf-decode")]
131 #[error("RAF error: {0}")]
132 Raf(String),
133
134 #[cfg(feature = "crw-decode")]
136 #[error("CRW error: {0}")]
137 Crw(String),
138
139 #[error("Image decode error ({format}): {message}")]
141 ImageDecode {
142 format: &'static str,
144 message: String,
146 },
147
148 #[error("Decompression error: {0}")]
150 Decompression(String),
151}
152
153#[derive(Debug, Error)]
155pub enum ProcessingError {
156 #[error("Demosaic error: {0}")]
158 Demosaic(String),
159
160 #[error("Color processing error: {0}")]
162 Color(String),
163}
164
165#[derive(Debug, Error)]
170#[non_exhaustive]
171pub enum EncodeError {
172 #[error("Encoding error ({format}): {message}")]
174 Encoding {
175 format: &'static str,
177 message: String,
179 },
180
181 #[error("{format} encoder does not support {requested:?} output")]
183 UnsupportedBitDepth {
184 format: &'static str,
186 requested: BitDepth,
188 },
189
190 #[cfg(feature = "jpeg-encode")]
192 #[error("JPEG encoding error: {0}")]
193 Jpeg(#[from] jpeg_encoder::EncodingError),
194
195 #[error("WebP error: {0}")]
197 WebP(String),
198
199 #[error("JXL error: {0}")]
201 Jxl(String),
202}
203
204#[cfg(feature = "tiff-parser")]
205impl From<binrw::Error> for RawError {
206 fn from(err: binrw::Error) -> Self {
207 RawError::Parse(ParseError::BinaryParse(err.to_string()))
208 }
209}
210
211#[cfg(feature = "jpeg-encode")]
212impl From<jpeg_encoder::EncodingError> for RawError {
213 fn from(err: jpeg_encoder::EncodingError) -> Self {
214 RawError::Encode(EncodeError::Jpeg(err))
215 }
216}
217
218pub type RawResult<T> = Result<T, RawError>;
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224
225 #[test]
226 fn test_error_display() {
227 let err = RawError::Parse(ParseError::InvalidMagic {
228 expected: 42,
229 found: 0,
230 });
231 let s = format!("{}", err);
232 assert!(s.contains("Invalid TIFF magic"));
233
234 #[cfg(feature = "tiff-parser")]
235 {
236 let err = RawError::Parse(ParseError::TagNotFound(crate::tiff::TiffTag::ImageWidth));
237 let s = format!("{}", err);
238 assert!(s.contains("ImageWidth"));
239 }
240 }
241
242 #[test]
243 fn test_io_error_conversion() {
244 let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found");
245 let raw_err: RawError = io_err.into();
246 assert!(matches!(raw_err, RawError::Io(_)));
247 }
248
249 #[test]
250 fn test_parse_error_conversion() {
251 let parse_err = ParseError::InvalidByteOrder(0x1234);
252 let raw_err: RawError = parse_err.into();
253 assert!(matches!(
254 raw_err,
255 RawError::Parse(ParseError::InvalidByteOrder(0x1234))
256 ));
257 }
258
259 #[cfg(feature = "cr2-decode")]
260 #[test]
261 fn test_format_error_conversion() {
262 let fmt_err = FormatError::Cr2("test error".to_string());
263 let raw_err: RawError = fmt_err.into();
264 assert!(matches!(raw_err, RawError::Format(FormatError::Cr2(_))));
265 }
266
267 #[test]
268 fn test_encode_error_conversion() {
269 let enc_err = EncodeError::Encoding {
270 format: "PNG",
271 message: "test".to_string(),
272 };
273 let raw_err: RawError = enc_err.into();
274 assert!(matches!(
275 raw_err,
276 RawError::Encode(EncodeError::Encoding { .. })
277 ));
278 }
279}