mod backtrace;
use crate::{io, BinResult};
use alloc::borrow::Cow;
#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, string::String, vec, vec::Vec};
pub use backtrace::*;
use core::{any::Any, fmt};
pub trait ContextExt {
#[must_use]
fn with_context<Frame: Into<BacktraceFrame>>(self, frame: Frame) -> Self;
#[must_use]
#[track_caller]
fn with_message(self, message: impl Into<Cow<'static, str>>) -> Self;
}
impl ContextExt for Error {
fn with_context<Frame: Into<BacktraceFrame>>(self, frame: Frame) -> Self {
match self {
Error::Backtrace(mut backtrace) => {
backtrace.frames.push(frame.into());
Error::Backtrace(backtrace)
}
error => Error::Backtrace(Backtrace::new(error, vec![frame.into()])),
}
}
#[track_caller]
fn with_message(self, message: impl Into<Cow<'static, str>>) -> Self {
match self {
Error::Backtrace(backtrace) => Error::Backtrace(backtrace.with_message(message)),
error => {
let caller = core::panic::Location::caller();
Error::Backtrace(Backtrace::new(
error,
vec![BacktraceFrame::Full {
code: None,
message: message.into(),
file: caller.file(),
line: caller.line(),
}],
))
}
}
}
}
impl<T> ContextExt for BinResult<T> {
fn with_context<Frame: Into<BacktraceFrame>>(self, frame: Frame) -> Self {
self.map_err(|err| err.with_context(frame))
}
#[track_caller]
fn with_message(self, message: impl Into<Cow<'static, str>>) -> Self {
match self {
Err(err) => {
let caller = core::panic::Location::caller();
Err(match err {
Error::Backtrace(backtrace) => {
Error::Backtrace(backtrace.with_message(message))
}
error => Error::Backtrace(Backtrace::new(
error,
vec![BacktraceFrame::Full {
code: None,
message: message.into(),
file: caller.file(),
line: caller.line(),
}],
)),
})
}
ok => ok,
}
}
}
pub trait CustomError: fmt::Display + fmt::Debug + Send + Sync + private::Sealed {
#[doc(hidden)]
fn as_any(&self) -> &(dyn Any + Send + Sync);
#[doc(hidden)]
fn as_any_mut(&mut self) -> &mut (dyn Any + Send + Sync);
#[doc(hidden)]
fn as_box_any(self: Box<Self>) -> Box<dyn Any + Send + Sync>;
}
impl<T: fmt::Display + fmt::Debug + Send + Sync + 'static> CustomError for T {
fn as_any(&self) -> &(dyn Any + Send + Sync) {
self
}
fn as_any_mut(&mut self) -> &mut (dyn Any + Send + Sync) {
self
}
fn as_box_any(self: Box<Self>) -> Box<dyn Any + Send + Sync> {
self
}
}
impl dyn CustomError {
#[allow(clippy::missing_panics_doc)]
pub fn downcast<T: CustomError + 'static>(self: Box<Self>) -> Result<Box<T>, Box<Self>> {
if self.is::<T>() {
Ok(self.as_box_any().downcast().unwrap())
} else {
Err(self)
}
}
pub fn downcast_mut<T: CustomError + 'static>(&mut self) -> Option<&mut T> {
self.as_any_mut().downcast_mut()
}
pub fn downcast_ref<T: CustomError + 'static>(&self) -> Option<&T> {
self.as_any().downcast_ref()
}
pub fn is<T: CustomError + 'static>(&self) -> bool {
core::any::TypeId::of::<T>() == self.as_any().type_id()
}
}
#[non_exhaustive]
pub enum Error {
BadMagic {
pos: u64,
found: Box<dyn fmt::Debug + Send + Sync>,
},
AssertFail {
pos: u64,
message: String,
},
Io(io::Error),
Custom {
pos: u64,
err: Box<dyn CustomError>,
},
NoVariantMatch {
pos: u64,
},
EnumErrors {
pos: u64,
variant_errors: Vec<(&'static str, Error)>,
},
Backtrace(Backtrace),
}
impl Error {
#[must_use]
pub fn root_cause(&self) -> &Self {
match self {
Self::Backtrace(backtrace) => &backtrace.error,
error => error,
}
}
#[must_use]
pub fn is_eof(&self) -> bool {
match self {
Error::Io(err) if err.kind() == io::ErrorKind::UnexpectedEof => true,
Error::EnumErrors { variant_errors, .. } => {
variant_errors.iter().all(|(_, err)| err.is_eof())
}
Error::Backtrace(bt) => bt.error.is_eof(),
_ => false,
}
}
#[must_use]
pub fn custom_err<T: CustomError + 'static>(&self) -> Option<&T> {
if let Error::Custom { err, .. } = self.root_cause() {
err.downcast_ref()
} else {
None
}
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Self::Io(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BadMagic { pos, found } => write!(f, "bad magic at 0x{pos:x}: {found:?}"),
Self::AssertFail { pos, message } => write!(f, "{message} at 0x{pos:x}"),
Self::Io(err) => fmt::Display::fmt(err, f),
Self::Custom { pos, err } => write!(f, "{err} at 0x{pos:x}"),
Self::NoVariantMatch { pos } => write!(f, "no variants matched at 0x{pos:x}"),
Self::EnumErrors {
pos,
variant_errors,
} => {
write!(f, "no variants matched at 0x{pos:x}:")?;
for (name, err) in variant_errors {
write!(f, "\n {name}: {err}")?;
}
Ok(())
}
Self::Backtrace(backtrace) => fmt::Display::fmt(backtrace, f),
}
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<Error as fmt::Display>::fmt(self, f)
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
mod private {
use core::fmt;
pub trait Sealed {}
impl<T: fmt::Display + fmt::Debug + Send + Sync + 'static> Sealed for T {}
}