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::Limits,
170 /// The supported strict limits
171 supported: crate::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 #[must_use]
198 pub fn from_format_and_kind(format: ImageFormatHint, kind: UnsupportedErrorKind) -> Self {
199 UnsupportedError { format, kind }
200 }
201
202 /// Returns the corresponding `UnsupportedErrorKind` of the error.
203 #[must_use]
204 pub fn kind(&self) -> UnsupportedErrorKind {
205 self.kind.clone()
206 }
207
208 /// Returns the image format associated with this error.
209 #[must_use]
210 pub fn format_hint(&self) -> ImageFormatHint {
211 self.format.clone()
212 }
213}
214
215impl DecodingError {
216 /// Create a `DecodingError` that stems from an arbitrary error of an underlying decoder.
217 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 /// Create a `DecodingError` for an image format.
225 ///
226 /// The error will not contain any further information but is very easy to create.
227 #[must_use]
228 pub fn from_format_hint(format: ImageFormatHint) -> Self {
229 DecodingError {
230 format,
231 underlying: None,
232 }
233 }
234
235 /// Returns the image format associated with this error.
236 #[must_use]
237 pub fn format_hint(&self) -> ImageFormatHint {
238 self.format.clone()
239 }
240}
241
242impl EncodingError {
243 /// Create an `EncodingError` that stems from an arbitrary error of an underlying encoder.
244 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 /// Create an `EncodingError` for an image format.
252 ///
253 /// The error will not contain any further information but is very easy to create.
254 #[must_use]
255 pub fn from_format_hint(format: ImageFormatHint) -> Self {
256 EncodingError {
257 format,
258 underlying: None,
259 }
260 }
261
262 /// Return the image format associated with this error.
263 #[must_use]
264 pub fn format_hint(&self) -> ImageFormatHint {
265 self.format.clone()
266 }
267}
268
269impl ParameterError {
270 /// Construct a `ParameterError` directly from a corresponding kind.
271 #[must_use]
272 pub fn from_kind(kind: ParameterErrorKind) -> Self {
273 ParameterError {
274 kind,
275 underlying: None,
276 }
277 }
278
279 /// Returns the corresponding `ParameterErrorKind` of the error.
280 #[must_use]
281 pub fn kind(&self) -> ParameterErrorKind {
282 self.kind.clone()
283 }
284}
285
286impl LimitError {
287 /// Construct a generic `LimitError` directly from a corresponding kind.
288 #[must_use]
289 pub fn from_kind(kind: LimitErrorKind) -> Self {
290 LimitError { kind }
291 }
292
293 /// Returns the corresponding `LimitErrorKind` of the error.
294 #[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: &OsStr) => 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
330/// Result of an image decoding/encoding process
331pub 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: &Error) => err.fmt(fmt),
337 ImageError::Decoding(err: &DecodingError) => err.fmt(fmt),
338 ImageError::Encoding(err: &EncodingError) => err.fmt(fmt),
339 ImageError::Parameter(err: &ParameterError) => err.fmt(fmt),
340 ImageError::Limits(err: &LimitError) => err.fmt(fmt),
341 ImageError::Unsupported(err: &UnsupportedError) => 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: &Error) => err.source(),
350 ImageError::Decoding(err: &DecodingError) => err.source(),
351 ImageError::Encoding(err: &EncodingError) => err.source(),
352 ImageError::Parameter(err: &ParameterError) => err.source(),
353 ImageError::Limits(err: &LimitError) => err.source(),
354 ImageError::Unsupported(err: &UnsupportedError) => 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: &Box) => 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: &Box) => 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: &Box) => 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: &Box) => {
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: &Box) => 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: &ImageFormat) => write!(fmt, "{format:?}"),
492 ImageFormatHint::Name(name: &String) => write!(fmt, "`{name}`"),
493 ImageFormatHint::PathExtension(ext: &PathBuf) => 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 // This will fail to compile if the size of this type is large.
506 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}
515