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
200#[cfg(feature = "tiff-parser")]
201impl From<binrw::Error> for RawError {
202 fn from(err: binrw::Error) -> Self {
203 RawError::Parse(ParseError::BinaryParse(err.to_string()))
204 }
205}
206
207#[cfg(feature = "jpeg-encode")]
208impl From<jpeg_encoder::EncodingError> for RawError {
209 fn from(err: jpeg_encoder::EncodingError) -> Self {
210 RawError::Encode(EncodeError::Jpeg(err))
211 }
212}
213
214pub type RawResult<T> = Result<T, RawError>;
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 #[test]
222 fn test_error_display() {
223 let err = RawError::Parse(ParseError::InvalidMagic {
224 expected: 42,
225 found: 0,
226 });
227 let s = format!("{}", err);
228 assert!(s.contains("Invalid TIFF magic"));
229
230 #[cfg(feature = "tiff-parser")]
231 {
232 let err = RawError::Parse(ParseError::TagNotFound(crate::tiff::TiffTag::ImageWidth));
233 let s = format!("{}", err);
234 assert!(s.contains("ImageWidth"));
235 }
236 }
237
238 #[test]
239 fn test_io_error_conversion() {
240 let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found");
241 let raw_err: RawError = io_err.into();
242 assert!(matches!(raw_err, RawError::Io(_)));
243 }
244
245 #[test]
246 fn test_parse_error_conversion() {
247 let parse_err = ParseError::InvalidByteOrder(0x1234);
248 let raw_err: RawError = parse_err.into();
249 assert!(matches!(
250 raw_err,
251 RawError::Parse(ParseError::InvalidByteOrder(0x1234))
252 ));
253 }
254
255 #[cfg(feature = "cr2-decode")]
256 #[test]
257 fn test_format_error_conversion() {
258 let fmt_err = FormatError::Cr2("test error".to_string());
259 let raw_err: RawError = fmt_err.into();
260 assert!(matches!(raw_err, RawError::Format(FormatError::Cr2(_))));
261 }
262
263 #[test]
264 fn test_encode_error_conversion() {
265 let enc_err = EncodeError::Encoding {
266 format: "PNG",
267 message: "test".to_string(),
268 };
269 let raw_err: RawError = enc_err.into();
270 assert!(matches!(
271 raw_err,
272 RawError::Encode(EncodeError::Encoding { .. })
273 ));
274 }
275}