Skip to main content

fovea_io/
error.rs

1// ═══════════════════════════════════════════════════════════════════════════════
2// Crate-level I/O error
3//
4// Codec-agnostic: no feature-gated fields.  Underlying codec errors are
5// type-erased via `Box<dyn Error>` so this type compiles identically
6// regardless of which codecs are enabled.
7//
8// Trade-off: `Box<dyn Error>` prevents `Clone`.  We accept this because
9// preserving error chains (`source()`) matters more for diagnostics than
10// `Clone` does for error handling patterns.
11// ═══════════════════════════════════════════════════════════════════════════════
12
13/// Crate-level I/O error.
14///
15/// Each variant classifies *what went wrong*.  Variants that wrap a codec
16/// error carry a type-erased source via `Box<dyn Error + Send + Sync>`,
17/// keeping this type codec-agnostic (no feature-gated fields).
18///
19/// Match on the enum directly to branch on the error category.
20///
21/// # Examples
22///
23/// ```
24/// use fovea_io::IoError;
25///
26/// let err = IoError::InvalidFormat { reason: "not a PNG file" };
27/// assert_eq!(err.to_string(), "invalid format: not a PNG file");
28///
29/// match err {
30///     IoError::InvalidFormat { reason } => assert_eq!(reason, "not a PNG file"),
31///     _ => unreachable!(),
32/// }
33/// ```
34#[derive(Debug, thiserror::Error)]
35pub enum IoError {
36    /// The input bytes don't match the expected format signature.
37    #[error("invalid format: {reason}")]
38    InvalidFormat {
39        /// A short, static description of why the format is invalid.
40        reason: &'static str,
41    },
42
43    /// The file is structurally valid but contains data we can't decode
44    /// (e.g. unsupported bit depth, compression method, colour type).
45    #[error("unsupported feature: {reason}")]
46    UnsupportedFeature {
47        /// A short, static description of the unsupported feature.
48        reason: &'static str,
49    },
50
51    /// The codec reported a corruption or constraint violation during
52    /// decoding.  The wrapped source carries the codec-specific detail.
53    #[error("decode failed")]
54    DecodeFailed {
55        /// The underlying codec error.
56        #[source]
57        source: Box<dyn std::error::Error + Send + Sync>,
58    },
59
60    /// An encoding operation failed.  The wrapped source carries the
61    /// codec-specific detail.
62    #[error("encode failed")]
63    EncodeFailed {
64        /// The underlying codec error.
65        #[source]
66        source: Box<dyn std::error::Error + Send + Sync>,
67    },
68
69    /// A standard I/O error (read/write/seek failure).
70    #[error(transparent)]
71    Io(#[from] std::io::Error),
72}