#[cfg(feature = "alloc")]
use alloc::{boxed::Box, string::String};
#[derive(Debug)]
pub struct Error {
#[cfg(feature = "alloc")]
inner: Box<ErrorInner>,
#[cfg(not(feature = "alloc"))]
inner: ErrorInner,
}
impl Error {
pub fn eocd_offset(&self) -> Option<u64> {
self.inner.eocd_offset
}
pub(crate) fn with_eocd_offset(mut self, offset: u64) -> Self {
self.inner.eocd_offset = Some(offset);
self
}
}
impl Error {
#[cfg(feature = "std")]
pub(crate) fn io(err: std::io::Error) -> Error {
Error::from(ErrorKind::IO(err))
}
#[cfg(feature = "alloc")]
pub(crate) fn utf8(err: core::str::Utf8Error) -> Error {
Error::from(ErrorKind::InvalidUtf8(err))
}
#[cfg(feature = "std")]
pub(crate) fn is_eof(&self) -> bool {
matches!(self.inner.kind, ErrorKind::Eof)
}
pub fn kind(&self) -> &ErrorKind {
&self.inner.kind
}
pub fn into_kind(self) -> ErrorKind {
self.inner.kind
}
}
#[derive(Debug)]
struct ErrorInner {
kind: ErrorKind,
eocd_offset: Option<u64>,
}
#[derive(Debug)]
#[non_exhaustive]
pub enum ErrorKind {
MissingEndOfCentralDirectory,
BufferTooSmall { required: usize },
InvalidSignature { expected: u32, actual: u32 },
InvalidChecksum { expected: u32, actual: u32 },
InvalidSize { expected: u64, actual: u64 },
#[cfg(feature = "alloc")]
InvalidUtf8(core::str::Utf8Error),
#[cfg(feature = "alloc")]
InvalidInput { msg: String },
InvalidEndOfCentralDirectory,
#[cfg(feature = "std")]
IO(std::io::Error),
Eof,
}
impl core::error::Error for Error {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match &self.inner.kind {
#[cfg(feature = "std")]
ErrorKind::IO(e) => Some(e),
#[cfg(feature = "alloc")]
ErrorKind::InvalidUtf8(e) => Some(e),
_ => None,
}
}
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.inner.kind)?;
Ok(())
}
}
impl core::fmt::Display for ErrorKind {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match *self {
#[cfg(feature = "std")]
ErrorKind::IO(ref err) => err.fmt(f),
ErrorKind::MissingEndOfCentralDirectory => {
write!(f, "Missing end of central directory")
}
ErrorKind::BufferTooSmall { required } => {
write!(f, "Buffer size too small: required {required} bytes")
}
ErrorKind::Eof => {
write!(f, "Unexpected end of file")
}
ErrorKind::InvalidSignature { expected, actual } => {
write!(
f,
"Invalid signature: expected 0x{expected:08x}, got 0x{actual:08x}"
)
}
ErrorKind::InvalidChecksum { expected, actual } => {
write!(
f,
"Invalid checksum: expected 0x{expected:08x}, got 0x{actual:08x}"
)
}
ErrorKind::InvalidSize { expected, actual } => {
write!(f, "Invalid size: expected {expected}, got {actual}")
}
#[cfg(feature = "alloc")]
ErrorKind::InvalidUtf8(ref err) => {
write!(f, "Invalid UTF-8: {err}")
}
#[cfg(feature = "alloc")]
ErrorKind::InvalidInput { ref msg } => {
write!(f, "Invalid input: {msg}")
}
ErrorKind::InvalidEndOfCentralDirectory => {
write!(f, "Invalid end of central directory")
}
}
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Error {
let inner = ErrorInner {
kind,
eocd_offset: None,
};
Error {
#[cfg(feature = "alloc")]
inner: Box::new(inner),
#[cfg(not(feature = "alloc"))]
inner,
}
}
}
#[cfg(feature = "std")]
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Error {
Error::from(ErrorKind::IO(err))
}
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
use std::error::Error as _;
#[test]
fn source_exposes_wrapped_errors() {
let io = Error::io(std::io::Error::other("boom"));
let io_source = io.source().expect("IO error should expose a source");
assert!(io_source.is::<std::io::Error>());
let invalid = vec![0xff_u8];
let utf8 = Error::utf8(std::str::from_utf8(&invalid).unwrap_err());
let utf8_source = utf8.source().expect("UTF-8 error should expose a source");
assert!(utf8_source.is::<std::str::Utf8Error>());
}
#[test]
fn source_is_none_for_non_wrapping_errors() {
let eof = Error::from(ErrorKind::Eof);
assert!(eof.source().is_none());
}
}