//! Contains detailed error representation. //! //! See the main [`ImageError`] which contains a variant for each specialized error type. The //! subtypes used in each variant are opaque by design. They can be roughly inspected through their //! respective `kind` methods which work similar to `std::io::Error::kind`. //! //! The error interface makes it possible to inspect the error of an underlying decoder or encoder, //! through the `Error::source` method. Note that this is not part of the stable interface and you //! may not rely on a particular error value for a particular operation. This means mainly that //! `image` does not promise to remain on a particular version of its underlying decoders but if //! you ensure to use the same version of the dependency (or at least of the error type) through //! external means then you could inspect the error type in slightly more detail. //! //! [`ImageError`]: enum.ImageError.html use std::error::Error; use std::{fmt, io}; use crate::color::ExtendedColorType; use crate::image::ImageFormat; /// The generic error type for image operations. /// /// This high level enum allows, by variant matching, a rough separation of concerns between /// underlying IO, the caller, format specifications, and the `image` implementation. #[derive(Debug)] pub enum ImageError { /// An error was encountered while decoding. /// /// This means that the input data did not conform to the specification of some image format, /// or that no format could be determined, or that it did not match format specific /// requirements set by the caller. Decoding(DecodingError), /// An error was encountered while encoding. /// /// The input image can not be encoded with the chosen format, for example because the /// specification has no representation for its color space or because a necessary conversion /// is ambiguous. In some cases it might also happen that the dimensions can not be used with /// the format. Encoding(EncodingError), /// An error was encountered in input arguments. /// /// This is a catch-all case for strictly internal operations such as scaling, conversions, /// etc. that involve no external format specifications. Parameter(ParameterError), /// Completing the operation would have required more resources than allowed. /// /// Errors of this type are limits set by the user or environment, *not* inherent in a specific /// format or operation that was executed. Limits(LimitError), /// An operation can not be completed by the chosen abstraction. /// /// This means that it might be possible for the operation to succeed in general but /// * it requires a disabled feature, /// * the implementation does not yet exist, or /// * no abstraction for a lower level could be found. Unsupported(UnsupportedError), /// An error occurred while interacting with the environment. IoError(io::Error), } /// The implementation for an operation was not provided. /// /// See the variant [`Unsupported`] for more documentation. /// /// [`Unsupported`]: enum.ImageError.html#variant.Unsupported #[derive(Debug)] pub struct UnsupportedError { format: ImageFormatHint, kind: UnsupportedErrorKind, } /// Details what feature is not supported. #[derive(Clone, Debug, Hash, PartialEq)] #[non_exhaustive] pub enum UnsupportedErrorKind { /// The required color type can not be handled. Color(ExtendedColorType), /// An image format is not supported. Format(ImageFormatHint), /// Some feature specified by string. /// This is discouraged and is likely to get deprecated (but not removed). GenericFeature(String), } /// An error was encountered while encoding an image. /// /// This is used as an opaque representation for the [`ImageError::Encoding`] variant. See its /// documentation for more information. /// /// [`ImageError::Encoding`]: enum.ImageError.html#variant.Encoding #[derive(Debug)] pub struct EncodingError { format: ImageFormatHint, underlying: Option>, } /// An error was encountered in inputs arguments. /// /// This is used as an opaque representation for the [`ImageError::Parameter`] variant. See its /// documentation for more information. /// /// [`ImageError::Parameter`]: enum.ImageError.html#variant.Parameter #[derive(Debug)] pub struct ParameterError { kind: ParameterErrorKind, underlying: Option>, } /// Details how a parameter is malformed. #[derive(Clone, Debug, Hash, PartialEq)] #[non_exhaustive] pub enum ParameterErrorKind { /// The dimensions passed are wrong. DimensionMismatch, /// Repeated an operation for which error that could not be cloned was emitted already. FailedAlready, /// A string describing the parameter. /// This is discouraged and is likely to get deprecated (but not removed). Generic(String), /// The end of the image has been reached. NoMoreData, } /// An error was encountered while decoding an image. /// /// This is used as an opaque representation for the [`ImageError::Decoding`] variant. See its /// documentation for more information. /// /// [`ImageError::Decoding`]: enum.ImageError.html#variant.Decoding #[derive(Debug)] pub struct DecodingError { format: ImageFormatHint, underlying: Option>, } /// Completing the operation would have required more resources than allowed. /// /// This is used as an opaque representation for the [`ImageError::Limits`] variant. See its /// documentation for more information. /// /// [`ImageError::Limits`]: enum.ImageError.html#variant.Limits #[derive(Debug)] pub struct LimitError { kind: LimitErrorKind, // do we need an underlying error? } /// Indicates the limit that prevented an operation from completing. /// /// Note that this enumeration is not exhaustive and may in the future be extended to provide more /// detailed information or to incorporate other resources types. #[derive(Clone, Debug, Hash, PartialEq, Eq)] #[non_exhaustive] #[allow(missing_copy_implementations)] // Might be non-Copy in the future. pub enum LimitErrorKind { /// The resulting image exceed dimension limits in either direction. DimensionError, /// The operation would have performed an allocation larger than allowed. InsufficientMemory, /// The specified strict limits are not supported for this operation Unsupported { /// The given limits limits: crate::io::Limits, /// The supported strict limits supported: crate::io::LimitSupport, }, } /// A best effort representation for image formats. #[derive(Clone, Debug, Hash, PartialEq)] #[non_exhaustive] pub enum ImageFormatHint { /// The format is known exactly. Exact(ImageFormat), /// The format can be identified by a name. Name(String), /// A common path extension for the format is known. PathExtension(std::path::PathBuf), /// The format is not known or could not be determined. Unknown, } impl UnsupportedError { /// Create an `UnsupportedError` for an image with details on the unsupported feature. /// /// If the operation was not connected to a particular image format then the hint may be /// `Unknown`. pub fn from_format_and_kind(format: ImageFormatHint, kind: UnsupportedErrorKind) -> Self { UnsupportedError { format, kind } } /// Returns the corresponding `UnsupportedErrorKind` of the error. pub fn kind(&self) -> UnsupportedErrorKind { self.kind.clone() } /// Returns the image format associated with this error. pub fn format_hint(&self) -> ImageFormatHint { self.format.clone() } } impl DecodingError { /// Create a `DecodingError` that stems from an arbitrary error of an underlying decoder. pub fn new(format: ImageFormatHint, err: impl Into>) -> Self { DecodingError { format, underlying: Some(err.into()), } } /// Create a `DecodingError` for an image format. /// /// The error will not contain any further information but is very easy to create. pub fn from_format_hint(format: ImageFormatHint) -> Self { DecodingError { format, underlying: None, } } /// Returns the image format associated with this error. pub fn format_hint(&self) -> ImageFormatHint { self.format.clone() } } impl EncodingError { /// Create an `EncodingError` that stems from an arbitrary error of an underlying encoder. pub fn new(format: ImageFormatHint, err: impl Into>) -> Self { EncodingError { format, underlying: Some(err.into()), } } /// Create an `EncodingError` for an image format. /// /// The error will not contain any further information but is very easy to create. pub fn from_format_hint(format: ImageFormatHint) -> Self { EncodingError { format, underlying: None, } } /// Return the image format associated with this error. pub fn format_hint(&self) -> ImageFormatHint { self.format.clone() } } impl ParameterError { /// Construct a `ParameterError` directly from a corresponding kind. pub fn from_kind(kind: ParameterErrorKind) -> Self { ParameterError { kind, underlying: None, } } /// Returns the corresponding `ParameterErrorKind` of the error. pub fn kind(&self) -> ParameterErrorKind { self.kind.clone() } } impl LimitError { /// Construct a generic `LimitError` directly from a corresponding kind. pub fn from_kind(kind: LimitErrorKind) -> Self { LimitError { kind } } /// Returns the corresponding `LimitErrorKind` of the error. pub fn kind(&self) -> LimitErrorKind { self.kind.clone() } } impl From for ImageError { fn from(err: io::Error) -> ImageError { ImageError::IoError(err) } } impl From for ImageFormatHint { fn from(format: ImageFormat) -> Self { ImageFormatHint::Exact(format) } } impl From<&'_ std::path::Path> for ImageFormatHint { fn from(path: &'_ std::path::Path) -> Self { match path.extension() { Some(ext) => ImageFormatHint::PathExtension(ext.into()), None => ImageFormatHint::Unknown, } } } impl From for UnsupportedError { fn from(hint: ImageFormatHint) -> Self { UnsupportedError { format: hint.clone(), kind: UnsupportedErrorKind::Format(hint), } } } /// Result of an image decoding/encoding process pub type ImageResult = Result; impl fmt::Display for ImageError { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { match self { ImageError::IoError(err) => err.fmt(fmt), ImageError::Decoding(err) => err.fmt(fmt), ImageError::Encoding(err) => err.fmt(fmt), ImageError::Parameter(err) => err.fmt(fmt), ImageError::Limits(err) => err.fmt(fmt), ImageError::Unsupported(err) => err.fmt(fmt), } } } impl Error for ImageError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { ImageError::IoError(err) => err.source(), ImageError::Decoding(err) => err.source(), ImageError::Encoding(err) => err.source(), ImageError::Parameter(err) => err.source(), ImageError::Limits(err) => err.source(), ImageError::Unsupported(err) => err.source(), } } } impl fmt::Display for UnsupportedError { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { match &self.kind { UnsupportedErrorKind::Format(ImageFormatHint::Unknown) => { write!(fmt, "The image format could not be determined",) } UnsupportedErrorKind::Format(format @ ImageFormatHint::PathExtension(_)) => write!( fmt, "The file extension {} was not recognized as an image format", format, ), UnsupportedErrorKind::Format(format) => { write!(fmt, "The image format {} is not supported", format,) } UnsupportedErrorKind::Color(color) => write!( fmt, "The decoder for {} does not support the color type `{:?}`", self.format, color, ), UnsupportedErrorKind::GenericFeature(message) => match &self.format { ImageFormatHint::Unknown => write!( fmt, "The decoder does not support the format feature {}", message, ), other => write!( fmt, "The decoder for {} does not support the format features {}", other, message, ), }, } } } impl Error for UnsupportedError {} impl fmt::Display for ParameterError { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { match &self.kind { ParameterErrorKind::DimensionMismatch => write!( fmt, "The Image's dimensions are either too \ small or too large" ), ParameterErrorKind::FailedAlready => write!( fmt, "The end the image stream has been reached due to a previous error" ), ParameterErrorKind::Generic(message) => { write!(fmt, "The parameter is malformed: {}", message,) } ParameterErrorKind::NoMoreData => write!(fmt, "The end of the image has been reached",), }?; if let Some(underlying) = &self.underlying { write!(fmt, "\n{}", underlying)?; } Ok(()) } } impl Error for ParameterError { fn source(&self) -> Option<&(dyn Error + 'static)> { match &self.underlying { None => None, Some(source) => Some(&**source), } } } impl fmt::Display for EncodingError { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { match &self.underlying { Some(underlying) => write!( fmt, "Format error encoding {}:\n{}", self.format, underlying, ), None => write!(fmt, "Format error encoding {}", self.format,), } } } impl Error for EncodingError { fn source(&self) -> Option<&(dyn Error + 'static)> { match &self.underlying { None => None, Some(source) => Some(&**source), } } } impl fmt::Display for DecodingError { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { match &self.underlying { None => match self.format { ImageFormatHint::Unknown => write!(fmt, "Format error"), _ => write!(fmt, "Format error decoding {}", self.format), }, Some(underlying) => { write!(fmt, "Format error decoding {}: {}", self.format, underlying) } } } } impl Error for DecodingError { fn source(&self) -> Option<&(dyn Error + 'static)> { match &self.underlying { None => None, Some(source) => Some(&**source), } } } impl fmt::Display for LimitError { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { match self.kind { LimitErrorKind::InsufficientMemory => write!(fmt, "Insufficient memory"), LimitErrorKind::DimensionError => write!(fmt, "Image is too large"), LimitErrorKind::Unsupported { .. } => { write!(fmt, "The following strict limits are specified but not supported by the opertation: ")?; Ok(()) } } } } impl Error for LimitError {} impl fmt::Display for ImageFormatHint { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { match self { ImageFormatHint::Exact(format) => write!(fmt, "{:?}", format), ImageFormatHint::Name(name) => write!(fmt, "`{}`", name), ImageFormatHint::PathExtension(ext) => write!(fmt, "`.{:?}`", ext), ImageFormatHint::Unknown => write!(fmt, "`Unknown`"), } } } #[cfg(test)] mod tests { use super::*; use std::mem; #[allow(dead_code)] // This will fail to compile if the size of this type is large. const ASSERT_SMALLISH: usize = [0][(mem::size_of::() >= 200) as usize]; #[test] fn test_send_sync_stability() { fn assert_send_sync() {} assert_send_sync::(); } }