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