1use std::collections::TryReserveError;
17use std::error::Error;
18use std::{fmt, io};
19
20use crate::color::ExtendedColorType;
21use crate::{metadata::Cicp, ImageFormat};
22
23#[derive(Debug)]
28pub enum ImageError {
29 Decoding(DecodingError),
35
36 Encoding(EncodingError),
43
44 Parameter(ParameterError),
49
50 Limits(LimitError),
55
56 Unsupported(UnsupportedError),
63
64 IoError(io::Error),
66}
67
68#[derive(Debug)]
74pub struct UnsupportedError {
75 format: ImageFormatHint,
76 kind: UnsupportedErrorKind,
77}
78
79#[derive(Clone, Debug, Hash, PartialEq)]
81#[non_exhaustive]
82pub enum UnsupportedErrorKind {
83 Color(ExtendedColorType),
85 ColorLayout(ExtendedColorType),
87 ColorspaceCicp(Cicp),
89 Format(ImageFormatHint),
91 GenericFeature(String),
94}
95
96#[derive(Debug)]
103pub struct EncodingError {
104 format: ImageFormatHint,
105 underlying: Option<Box<dyn Error + Send + Sync>>,
106}
107
108#[derive(Debug)]
115pub struct ParameterError {
116 kind: ParameterErrorKind,
117 underlying: Option<Box<dyn Error + Send + Sync>>,
118}
119
120#[derive(Clone, Debug, Hash, PartialEq)]
122#[non_exhaustive]
123pub enum ParameterErrorKind {
124 DimensionMismatch,
126 FailedAlready,
128 RgbCicpRequired(Cicp),
130 Generic(String),
133 NoMoreData,
135 CicpMismatch {
137 expected: Cicp,
139 found: Cicp,
141 },
142}
143
144#[derive(Debug)]
151pub struct DecodingError {
152 format: ImageFormatHint,
153 underlying: Option<Box<dyn Error + Send + Sync>>,
154}
155
156#[derive(Debug)]
163pub struct LimitError {
164 kind: LimitErrorKind,
165 }
167
168#[derive(Clone, Debug, Hash, PartialEq, Eq)]
173#[non_exhaustive]
174#[allow(missing_copy_implementations)] pub enum LimitErrorKind {
176 DimensionError,
178 InsufficientMemory,
180 Unsupported {
182 limits: crate::Limits,
184 supported: crate::LimitSupport,
186 },
187}
188
189#[derive(Clone, Debug, Hash, PartialEq)]
191#[non_exhaustive]
192pub enum ImageFormatHint {
193 Exact(ImageFormat),
195
196 Name(String),
198
199 PathExtension(std::path::PathBuf),
201
202 Unknown,
204}
205
206impl UnsupportedError {
207 #[must_use]
212 pub fn from_format_and_kind(format: ImageFormatHint, kind: UnsupportedErrorKind) -> Self {
213 UnsupportedError { format, kind }
214 }
215
216 #[must_use]
218 pub fn kind(&self) -> UnsupportedErrorKind {
219 self.kind.clone()
220 }
221
222 #[must_use]
224 pub fn format_hint(&self) -> ImageFormatHint {
225 self.format.clone()
226 }
227}
228
229impl DecodingError {
230 pub fn new(format: ImageFormatHint, err: impl Into<Box<dyn Error + Send + Sync>>) -> Self {
232 DecodingError {
233 format,
234 underlying: Some(err.into()),
235 }
236 }
237
238 #[must_use]
242 pub fn from_format_hint(format: ImageFormatHint) -> Self {
243 DecodingError {
244 format,
245 underlying: None,
246 }
247 }
248
249 #[must_use]
251 pub fn format_hint(&self) -> ImageFormatHint {
252 self.format.clone()
253 }
254}
255
256impl EncodingError {
257 pub fn new(format: ImageFormatHint, err: impl Into<Box<dyn Error + Send + Sync>>) -> Self {
259 EncodingError {
260 format,
261 underlying: Some(err.into()),
262 }
263 }
264
265 #[must_use]
269 pub fn from_format_hint(format: ImageFormatHint) -> Self {
270 EncodingError {
271 format,
272 underlying: None,
273 }
274 }
275
276 #[must_use]
278 pub fn format_hint(&self) -> ImageFormatHint {
279 self.format.clone()
280 }
281}
282
283impl ParameterError {
284 #[must_use]
286 pub fn from_kind(kind: ParameterErrorKind) -> Self {
287 ParameterError {
288 kind,
289 underlying: None,
290 }
291 }
292
293 #[must_use]
295 pub fn kind(&self) -> ParameterErrorKind {
296 self.kind.clone()
297 }
298}
299
300impl LimitError {
301 #[must_use]
303 pub fn from_kind(kind: LimitErrorKind) -> Self {
304 LimitError { kind }
305 }
306
307 #[must_use]
309 pub fn kind(&self) -> LimitErrorKind {
310 self.kind.clone()
311 }
312}
313
314impl From<LimitErrorKind> for LimitError {
315 fn from(kind: LimitErrorKind) -> Self {
316 Self { kind }
317 }
318}
319
320impl From<io::Error> for ImageError {
321 fn from(err: io::Error) -> ImageError {
322 ImageError::IoError(err)
323 }
324}
325
326impl From<TryReserveError> for ImageError {
327 fn from(_: TryReserveError) -> ImageError {
328 ImageError::Limits(LimitErrorKind::InsufficientMemory.into())
329 }
330}
331
332impl From<ImageFormat> for ImageFormatHint {
333 fn from(format: ImageFormat) -> Self {
334 ImageFormatHint::Exact(format)
335 }
336}
337
338impl From<&'_ std::path::Path> for ImageFormatHint {
339 fn from(path: &'_ std::path::Path) -> Self {
340 match path.extension() {
341 Some(ext) => ImageFormatHint::PathExtension(ext.into()),
342 None => ImageFormatHint::Unknown,
343 }
344 }
345}
346
347impl From<ImageFormatHint> for UnsupportedError {
348 fn from(hint: ImageFormatHint) -> Self {
349 UnsupportedError {
350 format: hint.clone(),
351 kind: UnsupportedErrorKind::Format(hint),
352 }
353 }
354}
355
356pub type ImageResult<T> = Result<T, ImageError>;
358
359impl fmt::Display for ImageError {
360 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
361 match self {
362 ImageError::IoError(err) => err.fmt(fmt),
363 ImageError::Decoding(err) => err.fmt(fmt),
364 ImageError::Encoding(err) => err.fmt(fmt),
365 ImageError::Parameter(err) => err.fmt(fmt),
366 ImageError::Limits(err) => err.fmt(fmt),
367 ImageError::Unsupported(err) => err.fmt(fmt),
368 }
369 }
370}
371
372impl Error for ImageError {
373 fn source(&self) -> Option<&(dyn Error + 'static)> {
374 match self {
375 ImageError::IoError(err) => err.source(),
376 ImageError::Decoding(err) => err.source(),
377 ImageError::Encoding(err) => err.source(),
378 ImageError::Parameter(err) => err.source(),
379 ImageError::Limits(err) => err.source(),
380 ImageError::Unsupported(err) => err.source(),
381 }
382 }
383}
384
385impl fmt::Display for UnsupportedError {
386 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
387 match &self.kind {
388 UnsupportedErrorKind::Format(ImageFormatHint::Unknown) => {
389 write!(fmt, "The image format could not be determined",)
390 }
391 UnsupportedErrorKind::Format(format @ ImageFormatHint::PathExtension(_)) => write!(
392 fmt,
393 "The file extension {format} was not recognized as an image format",
394 ),
395 UnsupportedErrorKind::Format(format) => {
396 write!(fmt, "The image format {format} is not supported",)
397 }
398 UnsupportedErrorKind::Color(color) => write!(
399 fmt,
400 "The encoder or decoder for {} does not support the color type `{:?}`",
401 self.format, color,
402 ),
403 UnsupportedErrorKind::ColorLayout(layout) => write!(
404 fmt,
405 "Converting with the texel memory layout {layout:?} is not supported",
406 ),
407 UnsupportedErrorKind::ColorspaceCicp(color) => write!(
408 fmt,
409 "The colorimetric interpretation of a CICP color space is not supported for `{color:?}`",
410 ),
411 UnsupportedErrorKind::GenericFeature(message) => match &self.format {
412 ImageFormatHint::Unknown => write!(
413 fmt,
414 "The decoder does not support the format feature {message}",
415 ),
416 other => write!(
417 fmt,
418 "The decoder for {other} does not support the format features {message}",
419 ),
420 },
421 }
422 }
423}
424
425impl Error for UnsupportedError {}
426
427impl fmt::Display for ParameterError {
428 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
429 match &self.kind {
430 ParameterErrorKind::DimensionMismatch => write!(
431 fmt,
432 "The Image's dimensions are either too \
433 small or too large"
434 ),
435 ParameterErrorKind::FailedAlready => write!(
436 fmt,
437 "The end the image stream has been reached due to a previous error"
438 ),
439 ParameterErrorKind::RgbCicpRequired(cicp) => {
440 write!(fmt, "The CICP {cicp:?} can not be used for RGB images",)
441 }
442
443 ParameterErrorKind::Generic(message) => {
444 write!(fmt, "The parameter is malformed: {message}",)
445 }
446 ParameterErrorKind::NoMoreData => write!(fmt, "The end of the image has been reached",),
447 ParameterErrorKind::CicpMismatch { expected, found } => {
448 write!(
449 fmt,
450 "The color space {found:?} does not match the expected {expected:?}",
451 )
452 }
453 }?;
454
455 if let Some(underlying) = &self.underlying {
456 write!(fmt, "\n{underlying}")?;
457 }
458
459 Ok(())
460 }
461}
462
463impl Error for ParameterError {
464 fn source(&self) -> Option<&(dyn Error + 'static)> {
465 match &self.underlying {
466 None => None,
467 Some(source) => Some(&**source),
468 }
469 }
470}
471
472impl fmt::Display for EncodingError {
473 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
474 match &self.underlying {
475 Some(underlying) => write!(
476 fmt,
477 "Format error encoding {}:\n{}",
478 self.format, underlying,
479 ),
480 None => write!(fmt, "Format error encoding {}", self.format,),
481 }
482 }
483}
484
485impl Error for EncodingError {
486 fn source(&self) -> Option<&(dyn Error + 'static)> {
487 match &self.underlying {
488 None => None,
489 Some(source) => Some(&**source),
490 }
491 }
492}
493
494impl fmt::Display for DecodingError {
495 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
496 match &self.underlying {
497 None => match self.format {
498 ImageFormatHint::Unknown => write!(fmt, "Format error"),
499 _ => write!(fmt, "Format error decoding {}", self.format),
500 },
501 Some(underlying) => {
502 write!(fmt, "Format error decoding {}: {}", self.format, underlying)
503 }
504 }
505 }
506}
507
508impl Error for DecodingError {
509 fn source(&self) -> Option<&(dyn Error + 'static)> {
510 match &self.underlying {
511 None => None,
512 Some(source) => Some(&**source),
513 }
514 }
515}
516
517impl fmt::Display for LimitError {
518 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
519 match self.kind {
520 LimitErrorKind::InsufficientMemory => write!(fmt, "Memory limit exceeded"),
521 LimitErrorKind::DimensionError => write!(fmt, "Image size exceeds limit"),
522 LimitErrorKind::Unsupported { .. } => {
523 write!(fmt, "The following strict limits are specified but not supported by the opertation: ")?;
524 Ok(())
525 }
526 }
527 }
528}
529
530impl Error for LimitError {}
531
532impl fmt::Display for ImageFormatHint {
533 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
534 match self {
535 ImageFormatHint::Exact(format) => write!(fmt, "{format:?}"),
536 ImageFormatHint::Name(name) => write!(fmt, "`{name}`"),
537 ImageFormatHint::PathExtension(ext) => write!(fmt, "`.{ext:?}`"),
538 ImageFormatHint::Unknown => write!(fmt, "`Unknown`"),
539 }
540 }
541}
542
543#[derive(Clone)]
547#[allow(missing_copy_implementations)]
548pub struct TryFromExtendedColorError {
549 pub(crate) was: ExtendedColorType,
550}
551
552impl fmt::Debug for TryFromExtendedColorError {
553 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
554 write!(f, "{}", self)
555 }
556}
557
558impl fmt::Display for TryFromExtendedColorError {
559 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
560 write!(
561 f,
562 "The pixel layout {:?} is not supported as a buffer ColorType",
563 self.was
564 )
565 }
566}
567
568impl Error for TryFromExtendedColorError {}
569
570impl From<TryFromExtendedColorError> for ImageError {
571 fn from(err: TryFromExtendedColorError) -> ImageError {
572 ImageError::Unsupported(UnsupportedError::from_format_and_kind(
573 ImageFormatHint::Unknown,
574 UnsupportedErrorKind::Color(err.was),
575 ))
576 }
577}
578
579#[cfg(test)]
580mod tests {
581 use super::*;
582 use std::mem::size_of;
583
584 #[allow(dead_code)]
585 const ASSERT_SMALLISH: usize = [0][(size_of::<ImageError>() >= 200) as usize];
587
588 #[test]
589 fn test_send_sync_stability() {
590 fn assert_send_sync<T: Send + Sync>() {}
591
592 assert_send_sync::<ImageError>();
593 }
594}