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}