blob: d08e66a5e1520ca78614dd4d66820bd34c454e85 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use miniz_oxide::MZError;
use std::fmt::Debug;
use std::{error, io};
/// An error encountered during [`decode`].
///
/// [`decode`]: crate::decode
#[derive(Debug, thiserror::Error)]
pub enum Error<E>
where
E: std::error::Error + 'static,
{
/// An error yielded by the underlying input stream
#[error(transparent)]
Input(E),
#[error("gzip decode error: {0}")]
Decode(#[from] DecodeError),
}
impl From<io::Error> for Error<io::Error> {
fn from(e: io::Error) -> Self {
Self::Input(e)
}
}
/// A decoding error, due to malformed gzip data.
#[derive(Debug, thiserror::Error)]
pub enum DecodeError {
#[error("malformed header: {0}")]
Header(String),
#[error("malformed footer: {0}")]
Footer(String),
#[error("malformed DEFLATE body. miniz_oxide error code: {}", 0 as i32)]
Deflate(MZError),
/// Misc. catch-all, for things like unexpected eof.
#[error(transparent)]
Other(io::Error),
}
impl From<MZError> for DecodeError {
fn from(e: MZError) -> Self {
Self::Deflate(e)
}
}
/// Wrap an error in an `io::Error` with `ErrorKind::Other`.
///
/// See also [`try_unwrap_error`].
pub fn wrap_error<E>(inner: E) -> io::Error
where
E: error::Error + Send + Sync + 'static,
{
io::Error::new(io::ErrorKind::Other, inner)
}
/// Try to unwrap an inner error with `ErrorKind::Other` and the specified type.
///
/// If unsuccessful, return the original `io::Error`.
///
/// Inverse of [`wrap_error`].
pub fn try_unwrap_error<E>(outer: io::Error) -> Result<E, io::Error>
where
E: error::Error + 'static,
{
match (outer.kind(), outer.get_ref()) {
(k, Some(inner)) if k == io::ErrorKind::Other && inner.is::<E>() => {
// Consume and return the inner error.
let inner: Box<E> = outer
.into_inner()
.expect("get_ref() returned Some")
.downcast::<E>()
.expect("is::<E>() returned true");
Ok(*inner)
}
_ => Err(outer),
}
}
impl Error<io::Error> {
/// Convert from `Error<io::Error>` to `Error<E>`.
///
/// If there is an error of type `E` nested inside an `io::Error`, un-nest it.
pub(crate) fn unwrap_inner_error<E>(self) -> Error<E>
where
E: error::Error,
{
match self {
Error::Input(io_err) => match try_unwrap_error::<E>(io_err) {
Ok(inner) => Error::Input(inner),
Err(io_err) => Error::Decode(DecodeError::Other(io_err)),
},
// Trivially convert Error<io::Error> to Error<E>.
Error::Decode(d) => Error::Decode(d),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::MockError;
#[test]
fn test_wrap_error_round_trip() {
let e1 = MockError::BadThing("oh no!".into());
let wrapped = wrap_error(e1);
let e2: MockError = try_unwrap_error(wrapped).unwrap();
assert!(matches!(e2, MockError::BadThing(..)));
}
}