use crate::error::{
Error, ErrorExt, OutOfMemory, Result,
boxed::try_new_uninit_box,
error::{OomOrDynError, OomOrDynErrorMut, OomOrDynErrorRef},
};
use core::any::TypeId;
use core::fmt;
use core::ptr::NonNull;
use std_alloc::boxed::Box;
mod sealed {
use super::*;
pub trait Sealed {}
impl<T, E> Sealed for Result<T, E> {}
impl<T> Sealed for Option<T> {}
}
pub trait Context<T, E>: sealed::Sealed {
fn context<C>(self, context: C) -> Result<T, Error>
where
C: fmt::Display + Send + Sync + 'static;
fn with_context<C, F>(self, f: F) -> Result<T, Error>
where
C: fmt::Display + Send + Sync + 'static,
F: FnOnce() -> C;
}
impl<T, E> Context<T, E> for Result<T, E>
where
E: core::error::Error + Send + Sync + 'static,
{
#[inline]
fn context<C>(self, context: C) -> Result<T>
where
C: fmt::Display + Send + Sync + 'static,
{
match self {
Ok(x) => Ok(x),
Err(e) => Err(Error::new(e).context(context)),
}
}
#[inline]
fn with_context<C, F>(self, f: F) -> Result<T>
where
C: fmt::Display + Send + Sync + 'static,
F: FnOnce() -> C,
{
match self {
Ok(x) => Ok(x),
Err(e) => Err(Error::new(e).context(f())),
}
}
}
impl<T> Context<T, Error> for Result<T> {
fn context<C>(self, context: C) -> Result<T, Error>
where
C: fmt::Display + Send + Sync + 'static,
{
match self {
Ok(x) => Ok(x),
Err(e) => Err(e.context(context)),
}
}
fn with_context<C, F>(self, f: F) -> Result<T, Error>
where
C: fmt::Display + Send + Sync + 'static,
F: FnOnce() -> C,
{
match self {
Ok(x) => Ok(x),
Err(e) => Err(e.context(f())),
}
}
}
impl<T> Context<T, core::convert::Infallible> for Option<T> {
fn context<C>(self, context: C) -> Result<T>
where
C: fmt::Display + Send + Sync + 'static,
{
match self {
Some(x) => Ok(x),
None => Err(Error::from_error_ext(ContextError {
context,
error: None,
})),
}
}
fn with_context<C, F>(self, f: F) -> Result<T>
where
C: fmt::Display + Send + Sync + 'static,
F: FnOnce() -> C,
{
match self {
Some(x) => Ok(x),
None => Err(Error::from_error_ext(ContextError {
context: f(),
error: None,
})),
}
}
}
#[repr(C)]
pub(crate) struct ContextError<C> {
pub(crate) context: C,
pub(crate) error: Option<Error>,
}
impl<C> fmt::Debug for ContextError<C>
where
C: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl<C> fmt::Display for ContextError<C>
where
C: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.context.fmt(f)
}
}
impl<C> core::error::Error for ContextError<C>
where
C: fmt::Display + Send + Sync + 'static,
{
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
let source = self.ext_source()?;
Some(source.as_dyn_core_error())
}
}
unsafe impl<C> ErrorExt for ContextError<C>
where
C: fmt::Display + Send + Sync + 'static,
{
fn ext_as_dyn_core_error(&self) -> &(dyn core::error::Error + Send + Sync + 'static) {
self
}
fn ext_into_boxed_dyn_core_error(
self,
) -> Result<Box<dyn core::error::Error + Send + Sync + 'static>, OutOfMemory> {
let boxed = try_new_uninit_box()?;
Ok(Box::write(boxed, self) as _)
}
fn ext_source(&self) -> Option<OomOrDynErrorRef<'_>> {
let error = self.error.as_ref()?;
Some(error.inner.unpack())
}
fn ext_source_mut(&mut self) -> Option<OomOrDynErrorMut<'_>> {
let error = self.error.as_mut()?;
Some(error.inner.unpack_mut())
}
fn ext_take_source(&mut self) -> Option<OomOrDynError> {
let error = self.error.take()?;
Some(error.inner)
}
fn ext_is(&self, type_id: TypeId) -> bool {
type_id == TypeId::of::<C>()
}
unsafe fn ext_move(self, to: NonNull<u8>) {
unsafe {
to.cast::<C>().write(self.context);
}
}
#[cfg(feature = "backtrace")]
fn take_backtrace(&mut self) -> Option<std::backtrace::Backtrace> {
let error = self.error.as_mut()?;
match error.inner.unpack_mut() {
OomOrDynErrorMut::Oom(_) => None,
OomOrDynErrorMut::DynError(mut e) => {
let r = unsafe { e.as_mut() };
r.backtrace.take()
}
}
}
}