#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Codec {
Lossless,
Lossy,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
NotWebp,
Truncated,
MissingImage,
InvalidBitstream {
codec: Codec,
},
InvalidDimensions,
PixelBufferMismatch,
UnsupportedFeature,
InvalidContainer,
LimitExceeded {
pixels: u64,
limit: u64,
},
#[cfg(feature = "std")]
Io(std::io::ErrorKind),
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::NotWebp => f.write_str("not a WebP file (bad RIFF/WEBP magic)"),
Self::Truncated => f.write_str("unexpected end of input"),
Self::MissingImage => f.write_str("no decodable image chunk (VP8L or VP8)"),
Self::InvalidBitstream {
codec: Codec::Lossless,
} => f.write_str("invalid WebP VP8L (lossless) bitstream"),
Self::InvalidBitstream {
codec: Codec::Lossy,
} => f.write_str("invalid WebP VP8 (lossy) bitstream"),
Self::InvalidDimensions => f.write_str("image dimensions out of range (1..=16384)"),
Self::PixelBufferMismatch => {
f.write_str("pixel buffer length does not match width * height * 4")
},
Self::UnsupportedFeature => f.write_str("unsupported WebP feature"),
Self::InvalidContainer => f.write_str("malformed WebP extended (VP8X) container"),
Self::LimitExceeded { pixels, limit } => {
write!(
f,
"image has {pixels} pixels, exceeding the limit of {limit}"
)
},
#[cfg(feature = "std")]
Self::Io(kind) => write!(f, "I/O error: {kind:?}"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
#[cfg(feature = "std")]
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Self::Io(err.kind())
}
}
pub type Result<T> = core::result::Result<T, Error>;
#[cfg(test)]
mod tests {
use super::{Codec, Error};
#[test]
fn display_is_non_empty_for_every_variant() {
let variants = [
Error::NotWebp,
Error::Truncated,
Error::MissingImage,
Error::InvalidBitstream {
codec: Codec::Lossless,
},
Error::InvalidBitstream {
codec: Codec::Lossy,
},
Error::InvalidDimensions,
Error::PixelBufferMismatch,
Error::UnsupportedFeature,
Error::InvalidContainer,
Error::LimitExceeded {
pixels: 100,
limit: 10,
},
];
for variant in variants {
assert!(!variant.to_string().is_empty());
}
}
#[test]
fn limit_exceeded_reports_both_numbers() {
let msg = Error::LimitExceeded {
pixels: 4096,
limit: 1024,
}
.to_string();
assert!(msg.contains("4096") && msg.contains("1024"));
}
#[test]
fn io_error_maps_to_its_kind() {
use std::io::{Error as IoError, ErrorKind};
let err: Error = IoError::from(ErrorKind::UnexpectedEof).into();
assert_eq!(err, Error::Io(ErrorKind::UnexpectedEof));
}
}