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