use std::io::ErrorKind;
use std::str::Utf8Error;
use std::string::FromUtf8Error;
use std::{error, fmt, io};
pub struct Error {
leftovers: Option<Vec<u8>>,
repr: Repr,
}
enum Repr {
Simple(ErrorKind),
Custom {
kind: ErrorKind,
inner: Box<dyn error::Error + Send + Sync>,
},
}
impl Error {
pub(crate) fn with_leftovers(mut self, bytes: Vec<u8>) -> Self {
debug_assert!(!bytes.is_empty());
if self.leftovers.is_some() {
panic!("This error already had leftover bytes assigned, we won't drop them !")
}
self.leftovers = Some(bytes);
self
}
pub fn leftovers(&self) -> &[u8] {
match &self.repr {
Repr::Simple(_) => {
if let Some(l) = &self.leftovers {
l.as_slice()
} else {
&[]
}
}
Repr::Custom { inner, .. } => {
if let Some(e) = inner.downcast_ref::<FromUtf8Error>() {
e.as_bytes()
} else {
&[]
}
}
}
}
pub fn kind(&self) -> ErrorKind {
match self.repr {
Repr::Simple(kind) => kind,
Repr::Custom { kind, .. } => kind,
}
}
pub fn into_inner(self) -> Option<Box<dyn error::Error + Send + Sync>> {
if let Ok(inner) = self.into_inner_checked() {
inner
} else {
panic!("This error is holding leftover bytes, we won't drop them !")
}
}
pub fn into_inner_checked(
self,
) -> std::result::Result<Option<Box<dyn error::Error + Send + Sync>>, Self> {
if self.leftovers.is_some() {
Err(self)
} else {
match self.repr {
Repr::Simple(_) => Ok(None),
Repr::Custom { inner, .. } if !inner.is::<FromUtf8Error>() => Ok(Some(inner)),
Repr::Custom { .. } => Err(self),
}
}
}
pub fn into_inner_lossy(self) -> Option<Box<dyn error::Error + Send + Sync>> {
if let Repr::Custom { inner, .. } = self.repr {
Some(inner)
} else {
None
}
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.repr {
Repr::Simple(kind) => f
.debug_struct("Error")
.field("leftover bytes", &self.leftovers)
.field("kind", &kind)
.finish(),
Repr::Custom { inner, .. } => fmt::Debug::fmt(&inner, f),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.repr {
Repr::Simple(kind) => fmt::Display::fmt(&io::Error::from(*kind), f),
Repr::Custom { inner, .. } => fmt::Display::fmt(&inner, f),
}
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Self {
Error {
leftovers: None,
repr: Repr::Simple(kind),
}
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Error {
leftovers: None,
repr: Repr::Custom {
kind: err.kind(),
inner: err.into(),
},
}
}
}
impl From<Utf8Error> for Error {
fn from(err: Utf8Error) -> Self {
Error {
leftovers: None,
repr: Repr::Custom {
kind: ErrorKind::InvalidData,
inner: err.into(),
},
}
}
}
impl From<std::string::FromUtf8Error> for Error {
fn from(err: std::string::FromUtf8Error) -> Self {
Error {
leftovers: None,
repr: Repr::Custom {
kind: ErrorKind::InvalidData,
inner: err.into(),
},
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match &self.repr {
Repr::Simple(_) => None,
Repr::Custom { inner, .. } => inner.source(),
}
}
}
pub(crate) type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod with_leftovers_tests {
use crate::error::Repr;
use crate::Error;
use std::io::ErrorKind;
#[test]
#[should_panic]
fn double_call_with_leftovers() {
Error {
leftovers: None,
repr: Repr::Simple(ErrorKind::Interrupted),
}
.with_leftovers(Vec::new())
.with_leftovers(Vec::new());
}
}