Skip to main content

pith_digest/
error.rs

1//! The one error type every fallible operation in the kit returns.
2
3use core::fmt;
4
5/// The single error type of the `pith` suite.
6///
7/// Every fallible operation across the workspace returns
8/// [`Result<T>`](crate::Result), which uses this type, so a caller that
9/// handles errors once handles all of them. Each variant carries a
10/// `&'static str` naming the field or structure at fault, so a message
11/// names a thing rather than saying "invalid input".
12///
13/// [`Display`](core::fmt::Display) never allocates and never runs the
14/// formatting machinery: it concatenates the literal prefix with the
15/// `what` field through [`Formatter::write_str`](core::fmt::Formatter::write_str)
16/// alone. The numeric payloads (`needed`, `found`, `limit`) stay
17/// reachable through `Debug`, which derives normally.
18#[derive(Copy, Clone, Debug, PartialEq, Eq)]
19pub enum Error {
20    /// Input ended before a structure that the format guarantees.
21    Truncated {
22        /// What was being read, e.g. `"idat chunk"`.
23        what: &'static str,
24        /// How many bytes the structure needs.
25        needed: usize,
26        /// How many bytes were actually there.
27        found: usize,
28    },
29    /// A magic number, signature or fixed field did not match.
30    InvalidMagic {
31        /// The field that did not match, e.g. `"PNG signature"`.
32        what: &'static str,
33    },
34    /// A structurally valid but semantically impossible value: a zero
35    /// denominator, a declared length that cannot fit, a table index past
36    /// the end of the table.
37    BadValue(&'static str),
38    /// A real format variant this crate deliberately does not implement.
39    /// This is not a failure to parse; it is a refusal to guess.
40    Unsupported(&'static str),
41    /// An allocation the format could request but that exceeds a
42    /// configured ceiling. Always a refusal, never an OOM.
43    TooLarge {
44        /// The thing whose size exceeded the ceiling.
45        what: &'static str,
46        /// The configured ceiling, in bytes.
47        limit: usize,
48    },
49}
50
51impl Error {
52    /// Builds [`Error::Truncated`] without spelling the struct literal.
53    pub fn truncated(what: &'static str, needed: usize, found: usize) -> Self {
54        Self::Truncated {
55            what,
56            needed,
57            found,
58        }
59    }
60
61    /// Builds [`Error::TooLarge`] without spelling the struct literal.
62    pub fn too_large(what: &'static str, limit: usize) -> Self {
63        Self::TooLarge { what, limit }
64    }
65}
66
67impl fmt::Display for Error {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        match self {
70            Error::Truncated { what, .. } => {
71                f.write_str("truncated: ")?;
72                f.write_str(what)
73            }
74            Error::InvalidMagic { what } => {
75                f.write_str("invalid magic: ")?;
76                f.write_str(what)
77            }
78            Error::BadValue(what) => {
79                f.write_str("bad value: ")?;
80                f.write_str(what)
81            }
82            Error::Unsupported(what) => {
83                f.write_str("unsupported: ")?;
84                f.write_str(what)
85            }
86            Error::TooLarge { what, .. } => {
87                f.write_str("too large: ")?;
88                f.write_str(what)
89            }
90        }
91    }
92}
93
94/// The result type used by every fallible operation in the kit. The
95/// error type defaults to [`Error`], which is what the kit's own APIs
96/// return; the parameter stays open so host code can reuse the alias
97/// with its own error type.
98pub type Result<T, E = Error> = core::result::Result<T, E>;