1use std::error::Error;
17use std::{fmt, io};
18
19use crate::color::ExtendedColorType;
20use crate::image::ImageFormat;
21
22#[derive(Debug)]
27pub enum ImageError {
28    Decoding(DecodingError),
34
35    Encoding(EncodingError),
42
43    Parameter(ParameterError),
48
49    Limits(LimitError),
54
55    Unsupported(UnsupportedError),
62
63    IoError(io::Error),
65}
66
67#[derive(Debug)]
73pub struct UnsupportedError {
74    format: ImageFormatHint,
75    kind: UnsupportedErrorKind,
76}
77
78#[derive(Clone, Debug, Hash, PartialEq)]
80#[non_exhaustive]
81pub enum UnsupportedErrorKind {
82    Color(ExtendedColorType),
84    Format(ImageFormatHint),
86    GenericFeature(String),
89}
90
91#[derive(Debug)]
98pub struct EncodingError {
99    format: ImageFormatHint,
100    underlying: Option<Box<dyn Error + Send + Sync>>,
101}
102
103#[derive(Debug)]
110pub struct ParameterError {
111    kind: ParameterErrorKind,
112    underlying: Option<Box<dyn Error + Send + Sync>>,
113}
114
115#[derive(Clone, Debug, Hash, PartialEq)]
117#[non_exhaustive]
118pub enum ParameterErrorKind {
119    DimensionMismatch,
121    FailedAlready,
123    Generic(String),
126    NoMoreData,
128}
129
130#[derive(Debug)]
137pub struct DecodingError {
138    format: ImageFormatHint,
139    underlying: Option<Box<dyn Error + Send + Sync>>,
140}
141
142#[derive(Debug)]
149pub struct LimitError {
150    kind: LimitErrorKind,
151    }
153
154#[derive(Clone, Debug, Hash, PartialEq, Eq)]
159#[non_exhaustive]
160#[allow(missing_copy_implementations)] pub enum LimitErrorKind {
162    DimensionError,
164    InsufficientMemory,
166    Unsupported {
168        limits: crate::Limits,
170        supported: crate::LimitSupport,
172    },
173}
174
175#[derive(Clone, Debug, Hash, PartialEq)]
177#[non_exhaustive]
178pub enum ImageFormatHint {
179    Exact(ImageFormat),
181
182    Name(String),
184
185    PathExtension(std::path::PathBuf),
187
188    Unknown,
190}
191
192impl UnsupportedError {
193    #[must_use]
198    pub fn from_format_and_kind(format: ImageFormatHint, kind: UnsupportedErrorKind) -> Self {
199        UnsupportedError { format, kind }
200    }
201
202    #[must_use]
204    pub fn kind(&self) -> UnsupportedErrorKind {
205        self.kind.clone()
206    }
207
208    #[must_use]
210    pub fn format_hint(&self) -> ImageFormatHint {
211        self.format.clone()
212    }
213}
214
215impl DecodingError {
216    pub fn new(format: ImageFormatHint, err: impl Into<Box<dyn Error + Send + Sync>>) -> Self {
218        DecodingError {
219            format,
220            underlying: Some(err.into()),
221        }
222    }
223
224    #[must_use]
228    pub fn from_format_hint(format: ImageFormatHint) -> Self {
229        DecodingError {
230            format,
231            underlying: None,
232        }
233    }
234
235    #[must_use]
237    pub fn format_hint(&self) -> ImageFormatHint {
238        self.format.clone()
239    }
240}
241
242impl EncodingError {
243    pub fn new(format: ImageFormatHint, err: impl Into<Box<dyn Error + Send + Sync>>) -> Self {
245        EncodingError {
246            format,
247            underlying: Some(err.into()),
248        }
249    }
250
251    #[must_use]
255    pub fn from_format_hint(format: ImageFormatHint) -> Self {
256        EncodingError {
257            format,
258            underlying: None,
259        }
260    }
261
262    #[must_use]
264    pub fn format_hint(&self) -> ImageFormatHint {
265        self.format.clone()
266    }
267}
268
269impl ParameterError {
270    #[must_use]
272    pub fn from_kind(kind: ParameterErrorKind) -> Self {
273        ParameterError {
274            kind,
275            underlying: None,
276        }
277    }
278
279    #[must_use]
281    pub fn kind(&self) -> ParameterErrorKind {
282        self.kind.clone()
283    }
284}
285
286impl LimitError {
287    #[must_use]
289    pub fn from_kind(kind: LimitErrorKind) -> Self {
290        LimitError { kind }
291    }
292
293    #[must_use]
295    pub fn kind(&self) -> LimitErrorKind {
296        self.kind.clone()
297    }
298}
299
300impl From<io::Error> for ImageError {
301    fn from(err: io::Error) -> ImageError {
302        ImageError::IoError(err)
303    }
304}
305
306impl From<ImageFormat> for ImageFormatHint {
307    fn from(format: ImageFormat) -> Self {
308        ImageFormatHint::Exact(format)
309    }
310}
311
312impl From<&'_ std::path::Path> for ImageFormatHint {
313    fn from(path: &'_ std::path::Path) -> Self {
314        match path.extension() {
315            Some(ext) => ImageFormatHint::PathExtension(ext.into()),
316            None => ImageFormatHint::Unknown,
317        }
318    }
319}
320
321impl From<ImageFormatHint> for UnsupportedError {
322    fn from(hint: ImageFormatHint) -> Self {
323        UnsupportedError {
324            format: hint.clone(),
325            kind: UnsupportedErrorKind::Format(hint),
326        }
327    }
328}
329
330pub type ImageResult<T> = Result<T, ImageError>;
332
333impl fmt::Display for ImageError {
334    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
335        match self {
336            ImageError::IoError(err) => err.fmt(fmt),
337            ImageError::Decoding(err) => err.fmt(fmt),
338            ImageError::Encoding(err) => err.fmt(fmt),
339            ImageError::Parameter(err) => err.fmt(fmt),
340            ImageError::Limits(err) => err.fmt(fmt),
341            ImageError::Unsupported(err) => err.fmt(fmt),
342        }
343    }
344}
345
346impl Error for ImageError {
347    fn source(&self) -> Option<&(dyn Error + 'static)> {
348        match self {
349            ImageError::IoError(err) => err.source(),
350            ImageError::Decoding(err) => err.source(),
351            ImageError::Encoding(err) => err.source(),
352            ImageError::Parameter(err) => err.source(),
353            ImageError::Limits(err) => err.source(),
354            ImageError::Unsupported(err) => err.source(),
355        }
356    }
357}
358
359impl fmt::Display for UnsupportedError {
360    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
361        match &self.kind {
362            UnsupportedErrorKind::Format(ImageFormatHint::Unknown) => {
363                write!(fmt, "The image format could not be determined",)
364            }
365            UnsupportedErrorKind::Format(format @ ImageFormatHint::PathExtension(_)) => write!(
366                fmt,
367                "The file extension {format} was not recognized as an image format",
368            ),
369            UnsupportedErrorKind::Format(format) => {
370                write!(fmt, "The image format {format} is not supported",)
371            }
372            UnsupportedErrorKind::Color(color) => write!(
373                fmt,
374                "The encoder or decoder for {} does not support the color type `{:?}`",
375                self.format, color,
376            ),
377            UnsupportedErrorKind::GenericFeature(message) => match &self.format {
378                ImageFormatHint::Unknown => write!(
379                    fmt,
380                    "The decoder does not support the format feature {message}",
381                ),
382                other => write!(
383                    fmt,
384                    "The decoder for {other} does not support the format features {message}",
385                ),
386            },
387        }
388    }
389}
390
391impl Error for UnsupportedError {}
392
393impl fmt::Display for ParameterError {
394    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
395        match &self.kind {
396            ParameterErrorKind::DimensionMismatch => write!(
397                fmt,
398                "The Image's dimensions are either too \
399                 small or too large"
400            ),
401            ParameterErrorKind::FailedAlready => write!(
402                fmt,
403                "The end the image stream has been reached due to a previous error"
404            ),
405            ParameterErrorKind::Generic(message) => {
406                write!(fmt, "The parameter is malformed: {message}",)
407            }
408            ParameterErrorKind::NoMoreData => write!(fmt, "The end of the image has been reached",),
409        }?;
410
411        if let Some(underlying) = &self.underlying {
412            write!(fmt, "\n{underlying}")?;
413        }
414
415        Ok(())
416    }
417}
418
419impl Error for ParameterError {
420    fn source(&self) -> Option<&(dyn Error + 'static)> {
421        match &self.underlying {
422            None => None,
423            Some(source) => Some(&**source),
424        }
425    }
426}
427
428impl fmt::Display for EncodingError {
429    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
430        match &self.underlying {
431            Some(underlying) => write!(
432                fmt,
433                "Format error encoding {}:\n{}",
434                self.format, underlying,
435            ),
436            None => write!(fmt, "Format error encoding {}", self.format,),
437        }
438    }
439}
440
441impl Error for EncodingError {
442    fn source(&self) -> Option<&(dyn Error + 'static)> {
443        match &self.underlying {
444            None => None,
445            Some(source) => Some(&**source),
446        }
447    }
448}
449
450impl fmt::Display for DecodingError {
451    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
452        match &self.underlying {
453            None => match self.format {
454                ImageFormatHint::Unknown => write!(fmt, "Format error"),
455                _ => write!(fmt, "Format error decoding {}", self.format),
456            },
457            Some(underlying) => {
458                write!(fmt, "Format error decoding {}: {}", self.format, underlying)
459            }
460        }
461    }
462}
463
464impl Error for DecodingError {
465    fn source(&self) -> Option<&(dyn Error + 'static)> {
466        match &self.underlying {
467            None => None,
468            Some(source) => Some(&**source),
469        }
470    }
471}
472
473impl fmt::Display for LimitError {
474    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
475        match self.kind {
476            LimitErrorKind::InsufficientMemory => write!(fmt, "Memory limit exceeded"),
477            LimitErrorKind::DimensionError => write!(fmt, "Image size exceeds limit"),
478            LimitErrorKind::Unsupported { .. } => {
479                write!(fmt, "The following strict limits are specified but not supported by the opertation: ")?;
480                Ok(())
481            }
482        }
483    }
484}
485
486impl Error for LimitError {}
487
488impl fmt::Display for ImageFormatHint {
489    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
490        match self {
491            ImageFormatHint::Exact(format) => write!(fmt, "{format:?}"),
492            ImageFormatHint::Name(name) => write!(fmt, "`{name}`"),
493            ImageFormatHint::PathExtension(ext) => write!(fmt, "`.{ext:?}`"),
494            ImageFormatHint::Unknown => write!(fmt, "`Unknown`"),
495        }
496    }
497}
498
499#[cfg(test)]
500mod tests {
501    use super::*;
502    use std::mem::size_of;
503
504    #[allow(dead_code)]
505    const ASSERT_SMALLISH: usize = [0][(size_of::<ImageError>() >= 200) as usize];
507
508    #[test]
509    fn test_send_sync_stability() {
510        fn assert_send_sync<T: Send + Sync>() {}
511
512        assert_send_sync::<ImageError>();
513    }
514}