diff options
Diffstat (limited to 'vendor/image/src/error.rs')
-rw-r--r-- | vendor/image/src/error.rs | 506 |
1 files changed, 506 insertions, 0 deletions
diff --git a/vendor/image/src/error.rs b/vendor/image/src/error.rs new file mode 100644 index 0000000..07ee275 --- /dev/null +++ b/vendor/image/src/error.rs @@ -0,0 +1,506 @@ +//! 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<Box<dyn Error + Send + Sync>>, +} + +/// 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<Box<dyn Error + Send + Sync>>, +} + +/// 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<Box<dyn Error + Send + Sync>>, +} + +/// 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<Box<dyn Error + Send + Sync>>) -> 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<Box<dyn Error + Send + Sync>>) -> 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<io::Error> for ImageError { + fn from(err: io::Error) -> ImageError { + ImageError::IoError(err) + } +} + +impl From<ImageFormat> 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<ImageFormatHint> 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<T> = Result<T, ImageError>; + +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::<ImageError>() >= 200) as usize]; + + #[test] + fn test_send_sync_stability() { + fn assert_send_sync<T: Send + Sync>() {} + + assert_send_sync::<ImageError>(); + } +} |