image/
error.rs

1//! Contains detailed error representation.
2//!
3//! See the main [`ImageError`] which contains a variant for each specialized error type. The
4//! subtypes used in each variant are opaque by design. They can be roughly inspected through their
5//! respective `kind` methods which work similar to `std::io::Error::kind`.
6//!
7//! The error interface makes it possible to inspect the error of an underlying decoder or encoder,
8//! through the `Error::source` method. Note that this is not part of the stable interface and you
9//! may not rely on a particular error value for a particular operation. This means mainly that
10//! `image` does not promise to remain on a particular version of its underlying decoders but if
11//! you ensure to use the same version of the dependency (or at least of the error type) through
12//! external means then you could inspect the error type in slightly more detail.
13//!
14//! [`ImageError`]: enum.ImageError.html
15
16use std::error::Error;
17use std::{fmt, io};
18
19use crate::color::ExtendedColorType;
20use crate::image::ImageFormat;
21
22/// The generic error type for image operations.
23///
24/// This high level enum allows, by variant matching, a rough separation of concerns between
25/// underlying IO, the caller, format specifications, and the `image` implementation.
26#[derive(Debug)]
27pub enum ImageError {
28    /// An error was encountered while decoding.
29    ///
30    /// This means that the input data did not conform to the specification of some image format,
31    /// or that no format could be determined, or that it did not match format specific
32    /// requirements set by the caller.
33    Decoding(DecodingError),
34
35    /// An error was encountered while encoding.
36    ///
37    /// The input image can not be encoded with the chosen format, for example because the
38    /// specification has no representation for its color space or because a necessary conversion
39    /// is ambiguous. In some cases it might also happen that the dimensions can not be used with
40    /// the format.
41    Encoding(EncodingError),
42
43    /// An error was encountered in input arguments.
44    ///
45    /// This is a catch-all case for strictly internal operations such as scaling, conversions,
46    /// etc. that involve no external format specifications.
47    Parameter(ParameterError),
48
49    /// Completing the operation would have required more resources than allowed.
50    ///
51    /// Errors of this type are limits set by the user or environment, *not* inherent in a specific
52    /// format or operation that was executed.
53    Limits(LimitError),
54
55    /// An operation can not be completed by the chosen abstraction.
56    ///
57    /// This means that it might be possible for the operation to succeed in general but
58    /// * it requires a disabled feature,
59    /// * the implementation does not yet exist, or
60    /// * no abstraction for a lower level could be found.
61    Unsupported(UnsupportedError),
62
63    /// An error occurred while interacting with the environment.
64    IoError(io::Error),
65}
66
67/// The implementation for an operation was not provided.
68///
69/// See the variant [`Unsupported`] for more documentation.
70///
71/// [`Unsupported`]: enum.ImageError.html#variant.Unsupported
72#[derive(Debug)]
73pub struct UnsupportedError {
74    format: ImageFormatHint,
75    kind: UnsupportedErrorKind,
76}
77
78/// Details what feature is not supported.
79#[derive(Clone, Debug, Hash, PartialEq)]
80#[non_exhaustive]
81pub enum UnsupportedErrorKind {
82    /// The required color type can not be handled.
83    Color(ExtendedColorType),
84    /// An image format is not supported.
85    Format(ImageFormatHint),
86    /// Some feature specified by string.
87    /// This is discouraged and is likely to get deprecated (but not removed).
88    GenericFeature(String),
89}
90
91/// An error was encountered while encoding an image.
92///
93/// This is used as an opaque representation for the [`ImageError::Encoding`] variant. See its
94/// documentation for more information.
95///
96/// [`ImageError::Encoding`]: enum.ImageError.html#variant.Encoding
97#[derive(Debug)]
98pub struct EncodingError {
99    format: ImageFormatHint,
100    underlying: Option<Box<dyn Error + Send + Sync>>,
101}
102
103/// An error was encountered in inputs arguments.
104///
105/// This is used as an opaque representation for the [`ImageError::Parameter`] variant. See its
106/// documentation for more information.
107///
108/// [`ImageError::Parameter`]: enum.ImageError.html#variant.Parameter
109#[derive(Debug)]
110pub struct ParameterError {
111    kind: ParameterErrorKind,
112    underlying: Option<Box<dyn Error + Send + Sync>>,
113}
114
115/// Details how a parameter is malformed.
116#[derive(Clone, Debug, Hash, PartialEq)]
117#[non_exhaustive]
118pub enum ParameterErrorKind {
119    /// The dimensions passed are wrong.
120    DimensionMismatch,
121    /// Repeated an operation for which error that could not be cloned was emitted already.
122    FailedAlready,
123    /// A string describing the parameter.
124    /// This is discouraged and is likely to get deprecated (but not removed).
125    Generic(String),
126    /// The end of the image has been reached.
127    NoMoreData,
128}
129
130/// An error was encountered while decoding an image.
131///
132/// This is used as an opaque representation for the [`ImageError::Decoding`] variant. See its
133/// documentation for more information.
134///
135/// [`ImageError::Decoding`]: enum.ImageError.html#variant.Decoding
136#[derive(Debug)]
137pub struct DecodingError {
138    format: ImageFormatHint,
139    underlying: Option<Box<dyn Error + Send + Sync>>,
140}
141
142/// Completing the operation would have required more resources than allowed.
143///
144/// This is used as an opaque representation for the [`ImageError::Limits`] variant. See its
145/// documentation for more information.
146///
147/// [`ImageError::Limits`]: enum.ImageError.html#variant.Limits
148#[derive(Debug)]
149pub struct LimitError {
150    kind: LimitErrorKind,
151    // do we need an underlying error?
152}
153
154/// Indicates the limit that prevented an operation from completing.
155///
156/// Note that this enumeration is not exhaustive and may in the future be extended to provide more
157/// detailed information or to incorporate other resources types.
158#[derive(Clone, Debug, Hash, PartialEq, Eq)]
159#[non_exhaustive]
160#[allow(missing_copy_implementations)] // Might be non-Copy in the future.
161pub enum LimitErrorKind {
162    /// The resulting image exceed dimension limits in either direction.
163    DimensionError,
164    /// The operation would have performed an allocation larger than allowed.
165    InsufficientMemory,
166    /// The specified strict limits are not supported for this operation
167    Unsupported {
168        /// The given limits
169        limits: crate::io::Limits,
170        /// The supported strict limits
171        supported: crate::io::LimitSupport,
172    },
173}
174
175/// A best effort representation for image formats.
176#[derive(Clone, Debug, Hash, PartialEq)]
177#[non_exhaustive]
178pub enum ImageFormatHint {
179    /// The format is known exactly.
180    Exact(ImageFormat),
181
182    /// The format can be identified by a name.
183    Name(String),
184
185    /// A common path extension for the format is known.
186    PathExtension(std::path::PathBuf),
187
188    /// The format is not known or could not be determined.
189    Unknown,
190}
191
192impl UnsupportedError {
193    /// Create an `UnsupportedError` for an image with details on the unsupported feature.
194    ///
195    /// If the operation was not connected to a particular image format then the hint may be
196    /// `Unknown`.
197    pub fn from_format_and_kind(format: ImageFormatHint, kind: UnsupportedErrorKind) -> Self {
198        UnsupportedError { format, kind }
199    }
200
201    /// Returns the corresponding `UnsupportedErrorKind` of the error.
202    pub fn kind(&self) -> UnsupportedErrorKind {
203        self.kind.clone()
204    }
205
206    /// Returns the image format associated with this error.
207    pub fn format_hint(&self) -> ImageFormatHint {
208        self.format.clone()
209    }
210}
211
212impl DecodingError {
213    /// Create a `DecodingError` that stems from an arbitrary error of an underlying decoder.
214    pub fn new(format: ImageFormatHint, err: impl Into<Box<dyn Error + Send + Sync>>) -> Self {
215        DecodingError {
216            format,
217            underlying: Some(err.into()),
218        }
219    }
220
221    /// Create a `DecodingError` for an image format.
222    ///
223    /// The error will not contain any further information but is very easy to create.
224    pub fn from_format_hint(format: ImageFormatHint) -> Self {
225        DecodingError {
226            format,
227            underlying: None,
228        }
229    }
230
231    /// Returns the image format associated with this error.
232    pub fn format_hint(&self) -> ImageFormatHint {
233        self.format.clone()
234    }
235}
236
237impl EncodingError {
238    /// Create an `EncodingError` that stems from an arbitrary error of an underlying encoder.
239    pub fn new(format: ImageFormatHint, err: impl Into<Box<dyn Error + Send + Sync>>) -> Self {
240        EncodingError {
241            format,
242            underlying: Some(err.into()),
243        }
244    }
245
246    /// Create an `EncodingError` for an image format.
247    ///
248    /// The error will not contain any further information but is very easy to create.
249    pub fn from_format_hint(format: ImageFormatHint) -> Self {
250        EncodingError {
251            format,
252            underlying: None,
253        }
254    }
255
256    /// Return the image format associated with this error.
257    pub fn format_hint(&self) -> ImageFormatHint {
258        self.format.clone()
259    }
260}
261
262impl ParameterError {
263    /// Construct a `ParameterError` directly from a corresponding kind.
264    pub fn from_kind(kind: ParameterErrorKind) -> Self {
265        ParameterError {
266            kind,
267            underlying: None,
268        }
269    }
270
271    /// Returns the corresponding `ParameterErrorKind` of the error.
272    pub fn kind(&self) -> ParameterErrorKind {
273        self.kind.clone()
274    }
275}
276
277impl LimitError {
278    /// Construct a generic `LimitError` directly from a corresponding kind.
279    pub fn from_kind(kind: LimitErrorKind) -> Self {
280        LimitError { kind }
281    }
282
283    /// Returns the corresponding `LimitErrorKind` of the error.
284    pub fn kind(&self) -> LimitErrorKind {
285        self.kind.clone()
286    }
287}
288
289impl From<io::Error> for ImageError {
290    fn from(err: io::Error) -> ImageError {
291        ImageError::IoError(err)
292    }
293}
294
295impl From<ImageFormat> for ImageFormatHint {
296    fn from(format: ImageFormat) -> Self {
297        ImageFormatHint::Exact(format)
298    }
299}
300
301impl From<&'_ std::path::Path> for ImageFormatHint {
302    fn from(path: &'_ std::path::Path) -> Self {
303        match path.extension() {
304            Some(ext) => ImageFormatHint::PathExtension(ext.into()),
305            None => ImageFormatHint::Unknown,
306        }
307    }
308}
309
310impl From<ImageFormatHint> for UnsupportedError {
311    fn from(hint: ImageFormatHint) -> Self {
312        UnsupportedError {
313            format: hint.clone(),
314            kind: UnsupportedErrorKind::Format(hint),
315        }
316    }
317}
318
319/// Result of an image decoding/encoding process
320pub type ImageResult<T> = Result<T, ImageError>;
321
322impl fmt::Display for ImageError {
323    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
324        match self {
325            ImageError::IoError(err) => err.fmt(fmt),
326            ImageError::Decoding(err) => err.fmt(fmt),
327            ImageError::Encoding(err) => err.fmt(fmt),
328            ImageError::Parameter(err) => err.fmt(fmt),
329            ImageError::Limits(err) => err.fmt(fmt),
330            ImageError::Unsupported(err) => err.fmt(fmt),
331        }
332    }
333}
334
335impl Error for ImageError {
336    fn source(&self) -> Option<&(dyn Error + 'static)> {
337        match self {
338            ImageError::IoError(err) => err.source(),
339            ImageError::Decoding(err) => err.source(),
340            ImageError::Encoding(err) => err.source(),
341            ImageError::Parameter(err) => err.source(),
342            ImageError::Limits(err) => err.source(),
343            ImageError::Unsupported(err) => err.source(),
344        }
345    }
346}
347
348impl fmt::Display for UnsupportedError {
349    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
350        match &self.kind {
351            UnsupportedErrorKind::Format(ImageFormatHint::Unknown) => {
352                write!(fmt, "The image format could not be determined",)
353            }
354            UnsupportedErrorKind::Format(format @ ImageFormatHint::PathExtension(_)) => write!(
355                fmt,
356                "The file extension {} was not recognized as an image format",
357                format,
358            ),
359            UnsupportedErrorKind::Format(format) => {
360                write!(fmt, "The image format {} is not supported", format,)
361            }
362            UnsupportedErrorKind::Color(color) => write!(
363                fmt,
364                "The decoder for {} does not support the color type `{:?}`",
365                self.format, color,
366            ),
367            UnsupportedErrorKind::GenericFeature(message) => match &self.format {
368                ImageFormatHint::Unknown => write!(
369                    fmt,
370                    "The decoder does not support the format feature {}",
371                    message,
372                ),
373                other => write!(
374                    fmt,
375                    "The decoder for {} does not support the format features {}",
376                    other, message,
377                ),
378            },
379        }
380    }
381}
382
383impl Error for UnsupportedError {}
384
385impl fmt::Display for ParameterError {
386    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
387        match &self.kind {
388            ParameterErrorKind::DimensionMismatch => write!(
389                fmt,
390                "The Image's dimensions are either too \
391                 small or too large"
392            ),
393            ParameterErrorKind::FailedAlready => write!(
394                fmt,
395                "The end the image stream has been reached due to a previous error"
396            ),
397            ParameterErrorKind::Generic(message) => {
398                write!(fmt, "The parameter is malformed: {}", message,)
399            }
400            ParameterErrorKind::NoMoreData => write!(fmt, "The end of the image has been reached",),
401        }?;
402
403        if let Some(underlying) = &self.underlying {
404            write!(fmt, "\n{}", underlying)?;
405        }
406
407        Ok(())
408    }
409}
410
411impl Error for ParameterError {
412    fn source(&self) -> Option<&(dyn Error + 'static)> {
413        match &self.underlying {
414            None => None,
415            Some(source) => Some(&**source),
416        }
417    }
418}
419
420impl fmt::Display for EncodingError {
421    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
422        match &self.underlying {
423            Some(underlying) => write!(
424                fmt,
425                "Format error encoding {}:\n{}",
426                self.format, underlying,
427            ),
428            None => write!(fmt, "Format error encoding {}", self.format,),
429        }
430    }
431}
432
433impl Error for EncodingError {
434    fn source(&self) -> Option<&(dyn Error + 'static)> {
435        match &self.underlying {
436            None => None,
437            Some(source) => Some(&**source),
438        }
439    }
440}
441
442impl fmt::Display for DecodingError {
443    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
444        match &self.underlying {
445            None => match self.format {
446                ImageFormatHint::Unknown => write!(fmt, "Format error"),
447                _ => write!(fmt, "Format error decoding {}", self.format),
448            },
449            Some(underlying) => {
450                write!(fmt, "Format error decoding {}: {}", self.format, underlying)
451            }
452        }
453    }
454}
455
456impl Error for DecodingError {
457    fn source(&self) -> Option<&(dyn Error + 'static)> {
458        match &self.underlying {
459            None => None,
460            Some(source) => Some(&**source),
461        }
462    }
463}
464
465impl fmt::Display for LimitError {
466    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
467        match self.kind {
468            LimitErrorKind::InsufficientMemory => write!(fmt, "Memory limit exceeded"),
469            LimitErrorKind::DimensionError => write!(fmt, "Image size exceeds limit"),
470            LimitErrorKind::Unsupported { .. } => {
471                write!(fmt, "The following strict limits are specified but not supported by the opertation: ")?;
472                Ok(())
473            }
474        }
475    }
476}
477
478impl Error for LimitError {}
479
480impl fmt::Display for ImageFormatHint {
481    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
482        match self {
483            ImageFormatHint::Exact(format) => write!(fmt, "{:?}", format),
484            ImageFormatHint::Name(name) => write!(fmt, "`{}`", name),
485            ImageFormatHint::PathExtension(ext) => write!(fmt, "`.{:?}`", ext),
486            ImageFormatHint::Unknown => write!(fmt, "`Unknown`"),
487        }
488    }
489}
490
491#[cfg(test)]
492mod tests {
493    use super::*;
494    use std::mem;
495
496    #[allow(dead_code)]
497    // This will fail to compile if the size of this type is large.
498    const ASSERT_SMALLISH: usize = [0][(mem::size_of::<ImageError>() >= 200) as usize];
499
500    #[test]
501    fn test_send_sync_stability() {
502        fn assert_send_sync<T: Send + Sync>() {}
503
504        assert_send_sync::<ImageError>();
505    }
506}