use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Error {
Format {
detail: String,
position: Option<u64>,
},
Limits {
which: &'static str,
limit: u64,
actual: u64,
},
Decompress(&'static str),
Unsupported {
what: String,
},
Io(String),
ImageIo(String),
Internal(&'static str),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Format { detail, position } => match position {
Some(p) => write!(f, "invalid stream: {detail} (at {p})"),
None => write!(f, "invalid stream: {detail}"),
},
Error::Limits {
which,
limit,
actual,
} => {
write!(f, "{which} limit exceeded: {actual} > {limit}")
}
Error::Decompress(what) => write!(f, "decompress: {what}"),
Error::Unsupported { what } => write!(f, "unsupported: {what}"),
Error::Io(e) => write!(f, "i/o: {e}"),
Error::ImageIo(e) => write!(f, "image: {e}"),
Error::Internal(what) => write!(f, "internal error: {what}"),
}
}
}
impl std::error::Error for Error {}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Error::Io(e.to_string())
}
}
pub type Result<T> = std::result::Result<T, Error>;
impl Error {
pub(crate) fn fmt_at(detail: impl fmt::Display, position: u64) -> Error {
Error::Format {
detail: detail.to_string(),
position: Some(position),
}
}
pub fn msg(detail: impl fmt::Display) -> Error {
Error::Format {
detail: detail.to_string(),
position: None,
}
}
pub(crate) fn fmt_msg(detail: impl fmt::Display) -> Error {
Error::msg(detail)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display_is_stable() {
let e = Error::fmt_at("tile 3 truncated", 3);
assert_eq!(e.to_string(), "invalid stream: tile 3 truncated (at 3)");
let e = Error::Limits {
which: "pixels",
limit: 67_108_864,
actual: 67_108_865,
};
assert_eq!(e.to_string(), "pixels limit exceeded: 67108865 > 67108864");
}
#[test]
fn error_is_std_error() {
fn assert_error<T: std::error::Error>(_: &T) {}
assert_error(&Error::Decompress("x"));
}
}